/*
* (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.utils;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openflexo.foundation.FlexoModelObject;
import org.openflexo.foundation.FlexoObject;
import org.openflexo.foundation.rm.FlexoProject;
import org.openflexo.foundation.rm.FlexoProjectReference;
import org.openflexo.foundation.rm.FlexoStorageResource;
import org.openflexo.foundation.rm.FlexoStorageResource.ResourceLoadingListener;
import org.openflexo.foundation.rm.FlexoXMLStorageResource;
import org.openflexo.foundation.rm.ProjectData;
import org.openflexo.logging.FlexoLogger;
import org.openflexo.xmlcode.StringConvertable;
import org.openflexo.xmlcode.StringEncoder.Converter;
public class FlexoModelObjectReference<O extends FlexoModelObject> extends FlexoObject implements
StringConvertable<FlexoModelObjectReference<O>>, ResourceLoadingListener, PropertyChangeListener {
private static final Logger logger = FlexoLogger.getLogger(FlexoModelObjectReference.class.getPackage().getName());
public static interface ReferenceOwner {
public void notifyObjectLoaded(FlexoModelObjectReference<?> reference);
public void objectCantBeFound(FlexoModelObjectReference<?> reference);
public void objectDeleted(FlexoModelObjectReference<?> reference);
public void objectSerializationIdChanged(FlexoModelObjectReference<?> reference);
public FlexoProject getProject();
}
public enum ReferenceStatus {
RESOLVED, UNRESOLVED, NOT_FOUND, RESOURCE_NOT_FOUND, DELETED
}
private static final String SEPARATOR = "#";
private static final String PROJECT_SEPARATOR = "|";
/**
* @return
*/
public static String getSerializationRepresentationForObject(FlexoModelObject modelObject, boolean serializeClassName) {
return modelObject.getProject().getURI() + PROJECT_SEPARATOR
+ modelObject.getXMLResourceData().getFlexoResource().getResourceIdentifier() + SEPARATOR
+ modelObject.getSerializationIdentifier() + (serializeClassName ? SEPARATOR + modelObject.getClass().getName() : "");
}
private String resourceIdentifier;
private String userIdentifier;
private String className;
private long flexoID;
private String enclosingProjectIdentifier;
/** The project of the referring object. */
private FlexoProject referringProject;
private ReferenceOwner owner;
private O modelObject;
private boolean serializeClassName = false;
private ReferenceStatus status = ReferenceStatus.UNRESOLVED;
private FlexoXMLStorageResource resource;
private boolean deleted = false;
private String modelObjectIdentifier;
public FlexoModelObjectReference(O object, ReferenceOwner owner) {
this(object);
setOwner(owner);
}
public FlexoModelObjectReference(O object) {
this.modelObject = object;
this.modelObject.addToReferencers(this);
this.status = ReferenceStatus.RESOLVED;
/**
* We also initialize the string representation for the following reason: Let's say the user creates a reference to the given
* <code>object</code> and then later on, the user deletes that <code>object</code> but the reference owner does not remove its
* reference, we will have to serialize this without any data which will be a disaster (we should expect NullPointer,
* ArrayIndexOutOfBounds, etc...
*/
if (modelObject.getXMLResourceData() != null) {
this.resourceIdentifier = modelObject.getXMLResourceData().getFlexoResource().getResourceIdentifier();
}
this.userIdentifier = modelObject.getUserIdentifier();
this.flexoID = modelObject.getFlexoID();
this.className = modelObject.getClass().getName();
}
@Override
public String toString() {
return "FlexoModelObjectReference resource=" + resourceIdentifier + " modelObject=" + modelObject + " status=" + status + " owner="
+ owner + " userIdentifier=" + userIdentifier + " className=" + className + " flexoID=" + flexoID;
}
public FlexoModelObjectReference(FlexoProject project, String modelObjectIdentifier) {
this.referringProject = project;
this.modelObjectIdentifier = modelObjectIdentifier;
if (referringProject != null) {
referringProject.addToObjectReferences(this);
}
try {
int indexOf = modelObjectIdentifier.indexOf(PROJECT_SEPARATOR);
if (indexOf > 0) {
enclosingProjectIdentifier = modelObjectIdentifier.substring(0, indexOf);
modelObjectIdentifier = modelObjectIdentifier.substring(indexOf + PROJECT_SEPARATOR.length());
}
String[] s = modelObjectIdentifier.split(SEPARATOR);
this.resourceIdentifier = s[0];
this.userIdentifier = s[1].substring(0, s[1].lastIndexOf(FlexoModelObject.ID_SEPARATOR));
this.flexoID = Long.valueOf(s[1].substring(s[1].lastIndexOf(FlexoModelObject.ID_SEPARATOR)
+ FlexoModelObject.ID_SEPARATOR.length()));
if (s.length == 3) {
this.className = s[2];
serializeClassName = true;
}
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (RuntimeException e) {
e.printStackTrace();
}
}
public void delete() {
delete(true);
}
public void delete(boolean notify) {
if (!deleted) {
deleted = true;
if (getReferringProject() != null) {
getReferringProject().removeObjectReferences(this);
}
if (getResource(false) != null) {
getResource(false).removeResourceLoadingListener(this);
getResource(false).getPropertyChangeSupport().removePropertyChangeListener("name", this);
}
if (modelObject != null) {
modelObject.removeFromReferencers(this);
}
if (owner != null) {
owner.objectDeleted(this);
}
owner = null;
modelObject = null;
}
}
public O getObject() {
return getObject(false);
}
public String getClassName() {
return className;
}
public Class<O> getKlass() {
if (getClassName() != null) {
try {
return (Class<O>) Class.forName(getClassName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (ClassCastException e) {
e.printStackTrace();
if (logger.isLoggable(Level.WARNING)) {
logger.warning("There seems to be a problem in the code. Attempt to retrieve " + getClassName()
+ " but was something else (see stacktrace)");
}
}
}
return null;
}
public O getObject(boolean force) {
if (modelObject == null) {
modelObject = findObject(force);
if (modelObject != null) {
modelObject.addToReferencers(this);
}
if (owner != null) {
if (modelObject != null) {
status = ReferenceStatus.RESOLVED;
owner.notifyObjectLoaded(this);
} else if (getResource(force) == null || getResource(force).isLoaded() && !getResource(force).getIsLoading()) {
if (getResource(force) == null) {
status = ReferenceStatus.RESOURCE_NOT_FOUND;
} else {
status = ReferenceStatus.NOT_FOUND;
}
if (force) {
owner.objectCantBeFound(this);
}
}
}
}
return modelObject;
}
private O findObject(boolean force) {
if (getEnclosingProject(force) != null) {
FlexoXMLStorageResource res = getResource(force);
if (res == null) {
return null;
}
if (force && !res.isLoaded()) {
res.getResourceData();
}
if (res.isLoaded() && !res.getIsLoading()) {
return (O) getEnclosingProject(force).findObject(userIdentifier, flexoID);
}
}
return null;
}
public FlexoXMLStorageResource getResource(boolean force) {
if (resourceIdentifier == null || getEnclosingProject(force) == null) {
return null;
}
if (resource == null) {
FlexoProject enclosingProject = getEnclosingProject(force);
if (enclosingProject != null) {
resource = (FlexoXMLStorageResource) enclosingProject.resourceForKey(resourceIdentifier);
if (resource == null && enclosingProjectIdentifier == null) {
}
if (resource != null) {
resource.addResourceLoadingListener(this);
resource.getPropertyChangeSupport().addPropertyChangeListener("name", this);
}
}
}
return resource;
}
public String getResourceIdentifier() {
if (resource != null) {
return resource.getResourceIdentifier();
} else {
return resourceIdentifier;
}
}
@Override
public Converter getConverter() {
if (getReferringProject() != null) {
return getReferringProject().getObjectReferenceConverter();
}
return null;
}
private FlexoProject getEnclosingProject(boolean force) {
if (modelObject != null) {
return modelObject.getProject();
} else {
if (enclosingProjectIdentifier != null) {
if (getReferringProject() != null) {
if (getReferringProject().getURI().equals(enclosingProjectIdentifier)) {
return getReferringProject();
}
ProjectData data = getReferringProject().getProjectData();
if (data != null) {
FlexoProjectReference projectReference = data.getProjectReferenceWithURI(enclosingProjectIdentifier);
if (projectReference != null) {
return projectReference.getReferredProject(force);
} else {
return getReferringProject();
}
}
}
} else {
return getReferringProject();
}
return null;
}
}
public long getFlexoID() {
return flexoID;
}
public String getEnclosingProjectIdentifier() {
if (modelObject != null) {
return modelObject.getProject().getProjectURI();
} else {
return enclosingProjectIdentifier;
}
}
private FlexoProject getReferringProject() {
if (owner != null) {
return owner.getProject();
} else {
return referringProject;
}
}
public ReferenceOwner getOwner() {
return owner;
}
public void setOwner(ReferenceOwner owner) {
if (this.owner != owner) {
if (this.owner != null && this.owner.getProject() != null) {
this.owner.getProject().removeObjectReferences(this);
}
this.owner = owner;
if (this.owner != null && this.owner.getProject() != null) {
this.owner.getProject().addToObjectReferences(this);
} else {
if (owner != null) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("No project found for " + owner + " " + getStringRepresentation());
}
}
}
}
}
public String getStringRepresentation() {
if (modelObject != null) {
return getSerializationRepresentationForObject(modelObject, serializeClassName);
} else {
return (enclosingProjectIdentifier != null ? enclosingProjectIdentifier + PROJECT_SEPARATOR : "") + resourceIdentifier
+ SEPARATOR + userIdentifier + FlexoModelObject.ID_SEPARATOR + flexoID
+ (serializeClassName ? SEPARATOR + className : "");
}
}
public void notifyObjectDeletion() {
status = ReferenceStatus.DELETED;
try {
if (getOwner() != null) {
getOwner().objectDeleted(this);
}
} finally {
modelObject = null;// If this is not done, we may end-up with memory leaks.
}
}
public void notifySerializationIdHasChanged() {
try {
if (getOwner() != null) {
getOwner().objectSerializationIdChanged(this);
}
} finally {
modelObject = null;// If this is not done, we may end-up with memory leaks.
}
}
@Override
public void notifyResourceHasBeenLoaded(FlexoStorageResource resource) {
findObject(false);
}
@Override
public void notifyResourceWillBeLoaded(FlexoStorageResource resource) {
// Nothing to do
}
/**
* Tells wheter the class name of the referenced model object should be serialized or not.
*
* @return true if the class name of the referenced model object should be serialized, false otherwise.
*/
public boolean getSerializeClassName() {
return serializeClassName;
}
/**
* Sets wheter the class name of the referenced model object should be serialized or not.
*/
public void setSerializeClassName(boolean serializeClassName) {
this.serializeClassName = serializeClassName;
}
public ReferenceStatus getStatus() {
if (getResource(false) == null && (status == ReferenceStatus.RESOLVED || status == ReferenceStatus.UNRESOLVED)) {
status = ReferenceStatus.RESOURCE_NOT_FOUND;
}
return status;
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getSource() == resource && "name".equals(evt.getPropertyName())) {
resourceIdentifier = resource.getResourceIdentifier();
if (getOwner() != null) {
getOwner().objectSerializationIdChanged(this);
}
}
}
public void _setEnclosingProjectIdentifier(String uri) {
if (enclosingProjectIdentifier == null) {
enclosingProjectIdentifier = uri;
if (getOwner() != null) {
getOwner().objectSerializationIdChanged(this);
}
}
}
}