/*
* (c) Copyright 2010-2011 AgileBirds
*
* This file is part of OpenFlexo.
*
* OpenFlexo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenFlexo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openflexo.foundation.rm;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openflexo.foundation.DataModification;
import org.openflexo.foundation.FlexoException;
import org.openflexo.foundation.FlexoModelObject;
import org.openflexo.foundation.FlexoObservable;
import org.openflexo.foundation.utils.ProjectLoadingCancelledException;
import org.openflexo.foundation.utils.ProjectLoadingHandler;
import org.openflexo.foundation.validation.Validable;
import org.openflexo.foundation.validation.ValidationModel;
import org.openflexo.foundation.validation.ValidationReport;
import org.openflexo.localization.FlexoLocalization;
import org.openflexo.toolbox.FileFormat;
import org.openflexo.xmlcode.XMLSerializable;
/**
* This class represents a Flexo resource. A FlexoResource represent an object handled by Flexo Application Suite (all concerned modules),
* which could be stored in a File, generally located in related {@link FlexoProject} project directory (see {@link FlexoFileResource}) or
* simply stored in memory (see {@link FlexoMemoryResource}).
*
* @author sguerin
*/
public abstract class FlexoResource<RD extends FlexoResourceData> extends FlexoObservable implements XMLSerializable, Validable {
private static final FlexoDependantResourceComparator dependancyComparator = new FlexoDependantResourceComparator();
private static class FlexoDependantResourceComparator implements Comparator<FlexoResource> {
protected FlexoDependantResourceComparator() {
}
@Override
public int compare(FlexoResource resource1, FlexoResource resource2) {
return resource1.resourceOrder - resource2.resourceOrder;
}
}
public static void sortResourcesWithDependancies(List<? extends FlexoResource<? extends FlexoResourceData>> resources) {
// 1. Reset the sort
for (FlexoResource<? extends FlexoResourceData> r : resources) {
r.resourceHasBeenSeen = false;
r.resourceOrder = 0;
}
// 2. Resolve order
for (FlexoResource<? extends FlexoResourceData> r : resources) {
try {
r.resolveOrder();
} catch (ResourceDependencyLoopException e) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Loop in dependencies found! Will continue but this should not happen.");
}
}
}
// 3. Do the sort
Collections.sort(resources, dependancyComparator);
}
protected int resourceOrder;
public int getResourceOrder() {
return resourceOrder;
}
protected boolean resourceHasBeenSeen;
public int resolveOrder() throws ResourceDependencyLoopException {
resourceHasBeenSeen = true;
try {
int highOrder = -1;
for (Iterator<FlexoResource<FlexoResourceData>> iter = getDependentResources().iterator(); iter.hasNext();) {
FlexoResource<FlexoResourceData> r = iter.next();
if (r.resourceHasBeenSeen) {
throw new ResourceDependencyLoopException(r);
} else {
highOrder = Math.max(highOrder, r.resolveOrder());
}
}
// Set this order so it is one higher than the highest dependency.
resourceOrder = highOrder + 1;
return resourceOrder;
} finally {
resourceHasBeenSeen = false;
}
}
private static final Logger logger = Logger.getLogger(FlexoResource.class.getPackage().getName());
protected RD _resourceData;
protected transient FlexoProject project;
/**
* Vector of FlexoResource which this resource depends on
*/
protected DependentResources _dependentResources;
/**
* Vector of FlexoResource which depends on this resource
*/
protected AlteredResources _alteredResources;
/**
* Vector of FlexoResource which depends on this resource
*/
protected SynchronizedResources _synchronizedResources;
/**
* Hashtable coding the date 'this' resource was lastly backward synchronized with the resource matching related key
*/
private Hashtable<FlexoResource<?>, LastSynchronizedWithResourceEntry> _lastSynchronizedForResources;
/**
* Default constructor
*
* @param aProject
*/
public FlexoResource(FlexoProject aProject) {
super();
_lastSynchronizedForResources = new Hashtable<FlexoResource<?>, LastSynchronizedWithResourceEntry>();
project = aProject;
}
public ProjectLoadingHandler getLoadingHandler() {
if (getProject() != null) {
return getProject().getLoadingHandler();
}
return null;
}
public void notifyResourceStatusChanged() {
if (getProject() != null) {
getProject().notifyResourceStatusChanged(this);
}
}
protected void notifyResourceChanged() {
if (getProject() != null) {
getProject().notifyResourceChanged(this);
}
}
/**
* Override this
*/
public abstract boolean isToBeSerialized();
/**
* Return project related to this resource
*
* @return
*/
public final FlexoProject getProject() {
return project;
}
/**
* This date is VERY IMPORTANT and CRITICAL since this is the date used by ResourceManager to compute dependancies between resources.
* This method returns the date that must be considered as last known update for this resource
*
* @return a Date object
*/
public abstract Date getLastUpdate();
@Override
public String toString() {
return getResourceIdentifier();
}
public void setResourceIdentifier(String anIdentifier) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Setting resource identifier is not allowed !");
}
}
public final String getResourceIdentifier() {
return getResourceTypeIdentifier() + "." + getName();
}
public String getResourceIdentifierForNewName(String newName) {
return getResourceTypeIdentifier() + "." + newName;
}
public final String getResourceTypeIdentifier() {
if (getResourceType() != null) {
return getResourceType().getName();
}
return null;
}
public abstract ResourceType getResourceType();
public FileFormat getResourceFormat() {
return getResourceType().getFormat();
}
public abstract String getName();
/**
* MUST be overriden in relevant sub-classes !
*
* @param aName
*/
public void setName(String aName) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Could NOT rename this kind of resource: operation not allowed !");
}
}
/**
* Rebuild resource dependancies for this resource This method must be overriden, since only RM resource is declared to be in sync with
* current resource
*/
public void rebuildDependancies() {
addToSynchronizedResources(getProject().getFlexoRMResource());
}
/**
* Clear resource dependancies for this resource
*/
public void clearDependencies() {
getDependentResources().clear();
getAlteredResources().clear();
getSynchronizedResources().clear();
}
// ==========================================================================
// ======================== Dependant Resources
// =============================
// ==========================================================================
/**
* Return the dependantResources
*
* @return Returns the dependantResources.
*/
public DependentResources getDependentResources() {
if (_dependentResources == null) {
_dependentResources = new DependentResources(this);
}
return _dependentResources;
}
/**
* Sets the dependantResources
*
* @param dependantResources
* The dependantResources to set.
*/
public void setDependentResources(DependentResources resources) {
_dependentResources = resources;
resources.setRelatedResource(this);
}
/**
* Add to dependantResources
*
* @param dependantResources
* The dependantResources to add.
*/
public void addToDependentResources(FlexoResource aDependantResource) {
getDependentResources().addToResources(aDependantResource);
}
/**
* Remove from dependantResources
*
* @param dependantResources
* The dependantResources to remove.
*/
public void removeFromDependentResources(FlexoResource aDependantResource) {
getDependentResources().removeFromResources(aDependantResource);
}
// ==========================================================================
// ========================== Altered Resources
// =============================
// ==========================================================================
/**
* Return the AlteredResources
*
* @return Returns the AlteredResources.
*/
public AlteredResources getAlteredResources() {
if (_alteredResources == null) {
_alteredResources = new AlteredResources(this);
}
return _alteredResources;
}
/**
* Sets the AlteredResources
*
* @param resources
* The AlteredResources to set.
*/
public void setAlteredResources(AlteredResources resources) {
_alteredResources = resources;
resources.setRelatedResource(this);
}
/**
* Add to AlteredResources
*
* @param aDependingResource
* The FlexoResource to add.
*/
public void addToAlteredResources(FlexoResource aDependingResource) {
getAlteredResources().addToResources(aDependingResource);
}
/**
* Remove from AlteredResources
*
* @param aDependingResource
* The FlexoResource to remove.
*/
public void removeFromAlteredResources(FlexoResource aDependingResource) {
getAlteredResources().removeFromResources(aDependingResource);
}
// ==========================================================================
// ======================== Synchronized Resources
// ==========================
// ==========================================================================
/**
* Return the SynchronizedResources
*
* @return Returns the SynchronizedResources.
*/
public SynchronizedResources getSynchronizedResources() {
if (_synchronizedResources == null) {
_synchronizedResources = new SynchronizedResources(this);
}
return _synchronizedResources;
}
/**
* Sets the SynchronizedResources
*
* @param resources
* The SynchronizedResources to set.
*/
public void setSynchronizedResources(SynchronizedResources resources) {
_synchronizedResources = resources;
resources.setRelatedResource(this);
}
/**
* Add to SynchronizedResources
*
* @param aSynchronizedResource
* The FlexoResource to add.
*/
public void addToSynchronizedResources(FlexoResource aSynchronizedResource) {
getSynchronizedResources().addToResources(aSynchronizedResource);
}
/**
* Remove from SynchronizedResources
*
* @param aSynchronizedResource
* The FlexoResource to remove.
*/
public void removeFromSynchronizedResources(FlexoResource aSynchronizedResource) {
getSynchronizedResources().removeFromResources(aSynchronizedResource);
}
@Override
public void setChanged() {
getProject().notifyResourceChanged(this);
}
// ==========================================================================
// ============================== Deletion
// ==================================
// ==========================================================================
private boolean isDeleted = false;
/**
* Delete this resource. Note that this method does nothing, except removing resource from projet, so this must be overriden in
* sub-classes
*/
public void delete() {
isDeleted = true;
getProject().removeResource(this);
setChanged();
notifyObservers(new DataModification(DELETED_PROPERTY, this, null));
}
@Override
public String getDeletedProperty() {
return DELETED_PROPERTY;
}
@Override
public boolean isDeleted() {
return isDeleted;
}
// ==========================================================================
// ====================== RMNotification propagation
// ========================
// ==========================================================================
/**
*
* Receive a notification that should be handled by the ResourceManager scheme. The ResourceManager will handle and propagate this
* notification
*
* @see org.openflexo.foundation.rm.FlexoResourceData#notifyRM(org.openflexo.foundation.rm.RMNotification)
* @see org.openflexo.foundation.rm.FlexoResource#receiveRMNotification(org.openflexo.foundation.rm.RMNotification)
*/
public void notifyRM(RMNotification notification) throws FlexoException {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Resource " + getResourceIdentifier() + " is notified of " + notification);
}
if (notification.isDeepNotification()) {
Enumeration en = getDeepPropagationTargets(notification).elements();
while (en.hasMoreElements()) {
FlexoResource temp = (FlexoResource) en.nextElement();
temp.receiveRMNotification(notification);
}
} else {
Enumeration en = getSimplePropagationTargets(notification).elements();
while (en.hasMoreElements()) {
FlexoResource temp = (FlexoResource) en.nextElement();
temp.receiveRMNotification(notification);
}
}
}
private Vector<FlexoResource<FlexoResourceData>> getSimplePropagationTargets(RMNotification notification) {
Vector<FlexoResource<FlexoResourceData>> returned = new Vector<FlexoResource<FlexoResourceData>>();
Enumeration<FlexoResource<FlexoResourceData>> en = getSynchronizedResources().elements();
while (en.hasMoreElements()) {
FlexoResource<FlexoResourceData> temp = en.nextElement();
if (notification.propagateToSynchronizedResource(this, temp)) {
returned.add(temp);
}
}
en = getAlteredResources().elements();
while (en.hasMoreElements()) {
FlexoResource<FlexoResourceData> temp = en.nextElement();
if (notification.propagateToAlteredResource(this, temp)) {
returned.add(temp);
}
}
return returned;
}
private Vector<FlexoResource<FlexoResourceData>> getDeepPropagationTargets(RMNotification notification) {
Vector<FlexoResource<FlexoResourceData>> returned = new Vector<FlexoResource<FlexoResourceData>>();
addDeepPropagationTargets(returned, notification);
return returned;
}
private void addDeepPropagationTargets(Vector<FlexoResource<FlexoResourceData>> targetList, RMNotification notification) {
Enumeration<FlexoResource<FlexoResourceData>> en = getSynchronizedResources().elements();
while (en.hasMoreElements()) {
FlexoResource<FlexoResourceData> res = en.nextElement();
if (!targetList.contains(res) && notification.propagateToSynchronizedResource(this, res)) {
targetList.add(res);
res.addDeepPropagationTargets(targetList, notification);
}
}
en = getAlteredResources().elements();
while (en.hasMoreElements()) {
FlexoResource<FlexoResourceData> res = en.nextElement();
if (!targetList.contains(res) && notification.propagateToAlteredResource(this, res)) {
targetList.add(res);
res.addDeepPropagationTargets(targetList, notification);
}
}
}
/**
*
* Receive a notification that has been propagated by the ResourceManager scheme and coming from a modification on an other resource
*
* @see org.openflexo.foundation.rm.FlexoResource#notifyRM(org.openflexo.foundation.rm.RMNotification)
*/
public void receiveRMNotification(RMNotification notification) throws FlexoException {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Receive RMNotification in FlexoResource IGNORED\n\treceiver:" + this + "\n\tnotification:" + notification);
}
}
/**
* Returns true if and only if this resource is up-to-date and therefore doesn't require to be updated. (No assertion is done on the
* fact whether resource is loaded or not)
*
* Note: Calling this method assert that NO MODIFICATION will be performed on the entire model
*
* @return
* @throws ResourceDependencyLoopException
*/
public final boolean needsUpdate() throws ResourceDependencyLoopException {
return _needsUpdate(makeSingleton());
}
/**
* Update this resource.
*
* If this resource was up-to-date, ensure that resource will be correctely loaded (performUpdating is called during this method
* execution)
*
* If this resource wasn't up-to-date, update whole required model using dependancies, and perform updating on resource
*
* When returning, we are sure that the method is up-to-date.
*
* @return a tree representing all resources that were updated
* @throws ResourceDependencyLoopException
* @throws FlexoException
* @throws ProjectLoadingCancelledException
* @throws FileNotFoundException
* @throws LoadResourceException
*/
public final FlexoResourceTree update() throws ResourceDependencyLoopException, LoadResourceException, FileNotFoundException,
ProjectLoadingCancelledException, FlexoException {
return _update(makeSingleton());
}
/**
* Abstract method representing things to do (processing) for a method to build itself (semantic differs from different kind of
* resources: load/backward_sync/generate/etc...)
*
* @param updatedResources
* : supply a tree representing all resources that were updated below
* @throws ResourceDependencyLoopException
* @throws ProjectLoadingCancelledException
* @throws FileNotFoundException
* @throws LoadResourceException
*/
protected abstract void performUpdating(FlexoResourceTree updatedResources) throws ResourceDependencyLoopException,
LoadResourceException, FileNotFoundException, ProjectLoadingCancelledException, FlexoException;
/**
* Important method "telling" if a resource from which this resource depend is a state requiring this resource to be updated/processed
* (load/backward_sync/generate/etc...)
*
* Note: here is the default implementation, which is overriden in FlexoStorageResource with LastSynchonizedWith scheme
*
* Note2: this was your isToBeBacksynchronizedWith() method
*
* @return flag indicating if resource is in a state requiring this resource to be updated/processed
*/
protected boolean requireUpdateBecauseOf(FlexoResource resource) {
return resource.getLastUpdate() != null && getLastUpdate() != null && resource.getLastUpdate().after(getLastUpdate());
}
/**
* Internal scheme computing the dependancies tree This tree contains all resources requiring to be updated (except the root one,
* naturally)
*
* Note: Calling this method assert that NO MODIFICATION will be performed on the entire model
*
* Note2: we assert here that only current resource is requesting this data
*
* @return a FlexoResourceTree representing all resources requiring to be updated
* @throws ResourceDependencyLoopException
* if a loop was detected (loop in dependancies definition)
*/
private final FlexoResourceTree buildDependanciesTree() throws ResourceDependencyLoopException {
return buildDependencyTree(makeSingleton());
}
/**
* Internal scheme computing the dependancies tree, given a set of resources involved in this request (used to prevent infinite loop
* when loop in definition) This tree contains all resources requiring to be updated (except the root one, naturally)
*
* Note: Calling this method assert that NO MODIFICATION will be performed on the entire model
*
* @param processedResources
* : a set of resources involved in this request
* @return a FlexoResourceTree representing all resources requiring to be updated
* @throws ResourceDependencyLoopException
* if a loop was detected (loop in dependancies definition)
*/
private final FlexoResourceTree buildDependencyTree(List<FlexoResource<FlexoResourceData>> processedResources)
throws ResourceDependencyLoopException {
FlexoResourceTreeImplementation returned = new FlexoResourceTreeImplementation(this);
for (Enumeration<FlexoResource<FlexoResourceData>> e = getDependentResources().elements(false, getProject().getDependancyScheme()); e
.hasMoreElements();) {
FlexoResource<FlexoResourceData> resource = e.nextElement();
if (processedResources.contains(resource)) {
throw new ResourceDependencyLoopException(resource);
}
List<FlexoResource<FlexoResourceData>> newProcessesResources = new ArrayList<FlexoResource<FlexoResourceData>>(
processedResources);
newProcessesResources.add(resource);
if (resource._needsUpdate(newProcessesResources) || requireUpdateBecauseOf(resource)) {
// Add this resource to dependancies tree
returned.add(resource.buildDependencyTree(newProcessesResources));
}
}
return returned;
}
/**
* Internal method used to update the whole model above which we depend, given a set of resources involved in this request (used to
* prevent infinite loop when loop in definition)
*
* @param processedResources
* : a set of resources involved in this request
* @return a FlexoResourceTree representing all resources that were updated
* @throws ResourceDependencyLoopException
* if a loop was detected (loop in dependancies definition)
* @throws FlexoException
* @throws ProjectLoadingCancelledException
* @throws FileNotFoundException
* @throws LoadResourceException
*/
protected final FlexoResourceTree performUpdateDependanciesModel(List<FlexoResource<FlexoResourceData>> processedResources)
throws ResourceDependencyLoopException, LoadResourceException, FileNotFoundException, ProjectLoadingCancelledException,
FlexoException {
FlexoResourceTreeImplementation returned = new FlexoResourceTreeImplementation(this);
for (Enumeration<FlexoResource<FlexoResourceData>> e = getDependentResources().elements(false, getProject().getDependancyScheme()); e
.hasMoreElements();) {
FlexoResource<FlexoResourceData> resource = e.nextElement();
if (processedResources.contains(resource)) {
throw new ResourceDependencyLoopException(resource);
}
List<FlexoResource<FlexoResourceData>> newProcessesResources = new ArrayList<FlexoResource<FlexoResourceData>>(
processedResources);
newProcessesResources.add(resource);
try {
if (resource._needsUpdate(newProcessesResources)) {
// This resource require updating, update it
FlexoResourceTree updatedResources = resource._update(newProcessesResources);
returned.add(updatedResources);
} else if (requireUpdateBecauseOf(resource)) {
// Add this resource to dependancies tree
returned.add(resource.buildDependencyTree(newProcessesResources));
}
} catch (ResourceDependencyLoopException e1) {
e1.addToResourceStack(this);
throw e1;
}
}
return returned;
}
/**
* Internal method used to build a vector containing this resource
*
* @return
*/
private List<FlexoResource<FlexoResourceData>> makeSingleton() {
return Arrays.asList((FlexoResource<FlexoResourceData>) this);
}
/**
* Internal method computing needsUpdate() given a vector of calling resources allowing to prevent from infinite loop
*
* @return a flag indicating if this resource needs to be updated
* @throws ResourceDependencyLoopException
*/
private final boolean _needsUpdate(List<FlexoResource<FlexoResourceData>> callingResources) throws ResourceDependencyLoopException {
FlexoResourceTree tree = buildDependencyTree(callingResources);
if (!tree.isEmpty()) {
/*StringBuilder sb = new StringBuilder(this+" ").append('(').append(new SimpleDateFormat("dd/MM HH:mm:ss SSS").format(getLastUpdate())).append(") needs update because of ");
for (FlexoResourceTree node : tree.getChildNodes()) {
sb.append((node.getRootNode().getNeedsUpdateReason().equals(FlexoLocalization.localizedForKey("up_to_date")))
? node.getRootNode() +new SimpleDateFormat("dd/MM HH:mm:ss SSS").format(node.getRootNode().getLastUpdate()): node.getRootNode().getNeedsUpdateReason()).append(",");
}*/
_needsUpdateReason = tree.toString();
} else {
_needsUpdateReason = FlexoLocalization.localizedForKey("up_to_date");
}
return !tree.isEmpty();
}
/**
* Internal method computing update() given a vector of calling resources allowing to prevent from infinite loop
*
* @return a tree representing all resources that were updated
* @throws ResourceDependencyLoopException
* @throws FlexoException
* @throws ProjectLoadingCancelledException
* @throws FileNotFoundException
* @throws LoadResourceException
*/
private final FlexoResourceTree _update(List<FlexoResource<FlexoResourceData>> callingResources)
throws ResourceDependencyLoopException, LoadResourceException, FileNotFoundException, ProjectLoadingCancelledException,
FlexoException {
// First update all dependancies model
FlexoResourceTree updatedResources = performUpdateDependanciesModel(callingResources);
// Perform updating knowing which resources were updated
performUpdating(updatedResources);
return updatedResources;
}
/**
* Called by the resource manager on a resource to tell that an other resource
*
* <pre>
* aResource
* </pre>
*
* from which this resource depends, has been more recently modified and thus provides a way to synchonize this resource from
*
* <pre>
* aResource
* </pre>
*
* @param aResource
* the resource from which this resource depends
* @throws FlexoException
*/
/*public void backwardSynchronizeWith(FlexoResource aResource) throws FlexoException
{
// Does nothing here: must be overriden in subclasses !
if (logger.isLoggable(Level.FINEST))
logger.finest("Resource " + getResourceIdentifier() + " : ignore backward synchronization for " + aResource.getResourceIdentifier()
+ " : override me in subclasses if required.");
}*/
private String _needsUpdateReason = null;
/**
* debug
*
* @return
*/
public String getNeedsUpdateReason() {
return _needsUpdateReason;
}
public Date getLastSynchronizedWithResource(FlexoResource aResource) {
LastSynchronizedWithResourceEntry entry = _lastSynchronizedForResources.get(aResource);
if (entry != null) {
return entry.date;
}
// return null;
return new Date(0);
}
public void setLastSynchronizedWithResource(FlexoResource aResource, Date aDate) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("setLastSynchronizedWithResource " + aResource + " with " + aDate);
}
_lastSynchronizedForResources.put(aResource, new LastSynchronizedWithResourceEntry(this, aResource, aDate));
for (FlexoResource<?> alteredResource : getAlteredResources()) {
alteredResource.resetLastSynchronizedWithResource(this);
}
notifyResourceChanged();
}
public void resetLastSynchronizedWithResource(FlexoResource aResource) {
if (logger.isLoggable(Level.FINEST)) {
logger.finest("resetLastSynchronizedWithResource " + aResource);
}
/*_lastSynchronizedForResources.remove(aResource);
// And propagate it again
for (Enumeration en=getAlteredResources().elements(); en.hasMoreElements();) {
FlexoResource alteredResource = (FlexoResource)en.nextElement();
alteredResource.resetLastSynchronizedWithResource(this);
}
notifyResourceChanged();*/
resetLastSynchronizedWithResource(aResource, new Vector<FlexoResource>());
}
private void resetLastSynchronizedWithResource(FlexoResource aResource, Vector<FlexoResource> alreadyDone) {
if (!alreadyDone.contains(this)) {
_lastSynchronizedForResources.remove(aResource);
alreadyDone.add(this);
// And propagate it again
for (Enumeration en = getAlteredResources().elements(); en.hasMoreElements();) {
FlexoResource alteredResource = (FlexoResource) en.nextElement();
alteredResource.resetLastSynchronizedWithResource(this, alreadyDone);
}
}
notifyResourceChanged();
}
public Hashtable<FlexoResource<?>, LastSynchronizedWithResourceEntry> getLastSynchronizedForResources() {
return _lastSynchronizedForResources;
}
public void setLastSynchronizedForResources(Hashtable<FlexoResource<?>, LastSynchronizedWithResourceEntry> lastSynchronizedForResources) {
this._lastSynchronizedForResources = lastSynchronizedForResources;
}
public void setLastSynchronizedForResourcesForKey(LastSynchronizedWithResourceEntry entry, FlexoResource key) {
entry.originResource = this;
_lastSynchronizedForResources.put(key, entry);
}
public void removeLastSynchronizedForResourcesWithKey(FlexoResource key) {
_lastSynchronizedForResources.remove(key);
}
public static class LastSynchronizedWithResourceEntry implements XMLSerializable {
protected FlexoResource<? extends FlexoResourceData> originResource;
private FlexoResource<? extends FlexoResourceData> resource;
Date date;
public LastSynchronizedWithResourceEntry() {
super();
}
public LastSynchronizedWithResourceEntry(FlexoResource<? extends FlexoResourceData> anOriginResource,
FlexoResource<? extends FlexoResourceData> aResource, Date aDate) {
this();
date = aDate;
resource = aResource;
originResource = anOriginResource;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public FlexoResource<? extends FlexoResourceData> getResource() {
return resource;
}
public FlexoResource<? extends FlexoResourceData> getOriginResource() {
return originResource;
}
public void setResource(FlexoResource<? extends FlexoResourceData> resource) {
this.resource = resource;
}
public String getSerializationIdentifier() {
return originResource.getSerializationIdentifier() + "_" + getResource().getSerializationIdentifier();
}
}
public String getSerializationIdentifier() {
return getUserIdentifier() + "_" + getResourceIdentifier();
}
private String userIdentifier;
public String getUserIdentifier() {
if (userIdentifier == null) {
return FlexoModelObject.getCurrentUserIdentifier();
}
return userIdentifier;
}
public void setUserIdentifier(String aUserIdentifier) {
userIdentifier = aUserIdentifier;
}
public String getURI() {
if (getProject() != null) {
return getProject().getURI() + "#" + getResourceIdentifier();
} else {
return null;
}
}
private boolean isActive = true;
public void activate() {
isActive = true;
notifyResourceStatusChanged();
}
public void deactivate() {
isActive = false;
notifyResourceStatusChanged();
}
public boolean isActive() {
return isActive;
}
public final boolean isInactive() {
return !isActive();
}
public enum DependencyAlgorithmScheme {
Pessimistic, Optimistic
}
// CPU-expensive
public boolean deeplyDependsOf(FlexoResource resource) {
return deeplyDependsOf(resource, new HashSet<FlexoResource>());
}
// CPU-expensive
public boolean deeplyDependsOf(FlexoResource resource, HashSet<FlexoResource> calling) {
if (!calling.contains(this)) {
calling.add(this);
} else {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Loop detected " + calling + "\n" + this);
}
return true;
}
if (getDependentResources().contains(resource)) {
return true;
}
for (FlexoResource r : getDependentResources()) {
if (r == this) {
if (logger.isLoggable(Level.WARNING)) {
logger.info("Loop dependancy found for resource " + r + " " + r.getFullyQualifiedName());
}
continue;
}
if (r.deeplyDependsOf(resource, calling)) {
return true;
}
}
calling.remove(this);
return false;
}
/**
* Return dependancy computing between this resource, and an other resource, asserting that this resource is contained in this
* resource's dependant resources
*
* @param resource
* @param dependancyScheme
* @return
*/
protected final boolean dependsOf(FlexoResource resource, DependencyAlgorithmScheme dependancyScheme) {
if (dependancyScheme == DependencyAlgorithmScheme.Pessimistic) {
return true;
} else if (dependancyScheme == DependencyAlgorithmScheme.Optimistic) {
Date requestDate = getRequestDateToBeUsedForOptimisticDependencyChecking(resource);
return optimisticallyDependsOf(resource, requestDate);
}
return true;
}
/**
* Return dependancy computing between this resource, and an other resource, asserting that this resource is contained in this
* resource's dependant resources
*
* Please override this method when required
*
* @param resource
* @param requestDate
* TODO
* @param dependancyScheme
* @return
*/
public boolean optimisticallyDependsOf(FlexoResource resource, Date requestDate) {
return true;
}
/**
* Please override this when required
*
* @param resource
* @return
*/
protected Date getRequestDateToBeUsedForOptimisticDependencyChecking(FlexoResource resource) {
return new Date();
}
public boolean isRegistered() {
return getProject() != null && getProject().resourceForKey(getFullyQualifiedName()) == this;
}
// =============================================================
// ======================== Validation =========================
// =============================================================
/**
* Returns fully qualified name for this object
*
* @return
*/
@Override
public String getFullyQualifiedName() {
return getResourceIdentifier();
}
/**
* Overrides getAllEmbeddedValidableObjects
*
* @see org.openflexo.foundation.validation.Validable#getAllEmbeddedValidableObjects()
*/
@Override
public Vector<Validable> getAllEmbeddedValidableObjects() {
Vector<Validable> v = new Vector<Validable>();
v.add(this);
return v;
}
/**
* Overrides getDefaultValidationModel
*
* @see org.openflexo.foundation.validation.Validable#getDefaultValidationModel()
*/
@Override
public ValidationModel getDefaultValidationModel() {
return getProject().getDefaultValidationModel();
}
/**
* Overrides isValid
*
* @see org.openflexo.foundation.validation.Validable#isValid()
*/
@Override
public boolean isValid() {
return isValid(getDefaultValidationModel());
}
/**
* Overrides isValid
*
* @see org.openflexo.foundation.validation.Validable#isValid(org.openflexo.foundation.validation.ValidationModel)
*/
@Override
public boolean isValid(ValidationModel validationModel) {
return validationModel.isValid(this);
}
/**
* Overrides validate
*
* @see org.openflexo.foundation.validation.Validable#validate()
*/
@Override
public ValidationReport validate() {
return validate(getDefaultValidationModel());
}
/**
* Overrides validate
*
* @see org.openflexo.foundation.validation.Validable#validate(org.openflexo.foundation.validation.ValidationModel)
*/
@Override
public ValidationReport validate(ValidationModel validationModel) {
return validationModel.validate(this);
}
/**
* Overrides validate
*
* @see org.openflexo.foundation.validation.Validable#validate(org.openflexo.foundation.validation.ValidationReport)
*/
@Override
public void validate(ValidationReport report) {
validate(report, getDefaultValidationModel());
}
/**
* Overrides validate
*
* @see org.openflexo.foundation.validation.Validable#validate(org.openflexo.foundation.validation.ValidationReport,
* org.openflexo.foundation.validation.ValidationModel)
*/
@Override
public void validate(ValidationReport report, ValidationModel validationModel) {
validationModel.validate(this, report);
}
/**
* @return
*/
public boolean deeplyDependsOfItSelf() {
for (FlexoResource r : getDependentResources()) {
if (r.deeplyDependsOf(this)) {
return true;
}
}
return false;
}
public final void performUpdating() {
}
public void notifyDependantResourceChange(FlexoResource origin) {
getDependentResources().update();
}
/**
* Checks if this resource is in an acceptable state. The resource should return false if it wants to be removed. Must return true
* otherwise.
*
* @return true if the resource can be kept, false if the resource must be deleted.
*/
public boolean checkIntegrity() {
return true;
}
}