org.nakedobjects.nos.remote.command.marshal.BasicObjectDeserializer Maven / Gradle / Ivy
package org.nakedobjects.nos.remote.command.marshal;
import java.util.Enumeration;
import org.apache.log4j.Logger;
import org.nakedobjects.noa.adapter.Naked;
import org.nakedobjects.noa.adapter.NakedCollection;
import org.nakedobjects.noa.adapter.NakedObject;
import org.nakedobjects.noa.adapter.NakedObjectLoader;
import org.nakedobjects.noa.adapter.NakedValue;
import org.nakedobjects.noa.adapter.Oid;
import org.nakedobjects.noa.adapter.Persistable;
import org.nakedobjects.noa.adapter.ResolveState;
import org.nakedobjects.noa.reflect.NakedObjectField;
import org.nakedobjects.noa.reflect.OneToManyAssociation;
import org.nakedobjects.noa.reflect.OneToOneAssociation;
import org.nakedobjects.noa.reflect.ValueAssociation;
import org.nakedobjects.noa.spec.NakedObjectSpecification;
import org.nakedobjects.nof.core.context.NakedObjectsContext;
import org.nakedobjects.nof.core.util.UnknownTypeException;
import org.nakedobjects.nof.reflect.remote.NakedObjectsRemoteException;
import org.nakedobjects.nof.reflect.remote.data.CollectionData;
import org.nakedobjects.nof.reflect.remote.data.Data;
import org.nakedobjects.nof.reflect.remote.data.IdentityData;
import org.nakedobjects.nof.reflect.remote.data.KnownObjects;
import org.nakedobjects.nof.reflect.remote.data.NullData;
import org.nakedobjects.nof.reflect.remote.data.ObjectData;
import org.nakedobjects.nof.reflect.remote.data.ReferenceData;
import org.nakedobjects.nof.reflect.remote.data.ValueData;
class BasicObjectDeserializer {
private static final Logger LOG = Logger.getLogger(BasicObjectDeserializer.class);
private DataStructure dataStructure;
public void setDataStructure(DataStructure dataStructure) {
this.dataStructure = dataStructure;
}
private ResolveState nextState(final ResolveState initialState, final boolean complete) {
ResolveState state = null;
if (initialState == ResolveState.RESOLVED) {
state = ResolveState.UPDATING;
} else if (initialState == ResolveState.GHOST || initialState == ResolveState.PART_RESOLVED) {
state = complete ? ResolveState.RESOLVING : ResolveState.RESOLVING_PART;
} else if (initialState == ResolveState.TRANSIENT) {
state = ResolveState.SERIALIZING_TRANSIENT;
}
return state;
}
public Naked restore(final Data data) {
if (data instanceof ValueData) {
return restoreValue((ValueData) data);
} else if (data instanceof CollectionData) {
return restoreCollection((CollectionData) data, new KnownObjects());
} else {
return restoreObject(data, new KnownObjects());
}
}
public Naked restore(final Data data, final KnownObjects knownObjects) {
if (data instanceof CollectionData) {
return restoreCollection((CollectionData) data, knownObjects);
} else {
return restoreObject(data, knownObjects);
}
}
private Naked restoreCollection(final CollectionData data, final KnownObjects knownObjects) {
String collectionType = data.getType();
NakedObjectSpecification collectionSpecification = NakedObjectsContext.getReflector().loadSpecification(collectionType);
String elementType = data.getElementype();
NakedObjectSpecification elementSpecification = NakedObjectsContext.getReflector().loadSpecification(elementType);
NakedObjectLoader objectLoader = NakedObjectsContext.getObjectLoader();
/*
* if we are to deal with internal collections then we need to be able to get the collection from it's
* parent via its field
*/
NakedCollection collection;
collection = objectLoader.recreateCollection(collectionSpecification, elementSpecification);
// collection.setOid(oid);
if (data.getElements() == null) {
LOG.debug("restoring empty collection");
return collection;
} else {
ReferenceData[] elements = data.getElements();
LOG.debug("restoring collection " + elements.length + " elements");
Object[] initData = new Object[elements.length];
for (int i = 0; i < elements.length; i++) {
NakedObject element = restoreObject(elements[i], knownObjects);
LOG.debug("restoring collection element :" + element);
initData[i] = element.getObject();
}
collection.init(initData);
return collection;
}
}
private NakedObject restoreObject(final Data data, final KnownObjects knownObjects) {
if (data instanceof NullData) {
return null;
} else if (data instanceof ObjectData) {
return restoreObjectFromObject((ObjectData) data, knownObjects);
} else if (data instanceof IdentityData) {
return restoreObjectFromIdentity((IdentityData) data, knownObjects);
} else {
throw new UnknownTypeException(data);
}
}
private NakedObject restoreObjectFromObject(final ObjectData data, final KnownObjects knownObjects) {
if (knownObjects.containsKey(data)) {
return knownObjects.get(data);
}
Oid oid = data.getOid();
NakedObjectLoader objectLoader = NakedObjectsContext.getObjectLoader();
NakedObject object;
/*
* either create a new transient object, get an existing object and update it if data is for resolved
* object, or create new object and set it
*/
if (objectLoader.isIdentityKnown(oid)) {
object = updateLoadedObject(data, oid, objectLoader, knownObjects);
} else if (oid.isTransient()) {
object = restoreTransient(data, objectLoader, knownObjects);
} else {
object = restorePersistentObject(data, oid, objectLoader, knownObjects);
}
return object;
}
private NakedObject restoreObjectFromIdentity(final IdentityData data, final KnownObjects knownObjects) {
Oid oid = data.getOid();
NakedObjectLoader objectLoader = NakedObjectsContext.getObjectLoader();
NakedObject object;
/*
* either create a new transient object, get an existing object and update it if data is for resolved
* object, or create new object and set it
*/
object = objectLoader.getAdapterFor(oid);
if (object == null) {
NakedObjectSpecification specification = NakedObjectsContext.getReflector().loadSpecification(data.getType());
object = objectLoader.recreateAdapterForPersistent(oid, specification);
}
return object;
}
private NakedObject restorePersistentObject(
final ObjectData data, final Oid oid, final NakedObjectLoader objectLoader, final KnownObjects knownObjects) {
// unknown object; create an instance
NakedObjectSpecification specification = NakedObjectsContext.getReflector().loadSpecification(data.getType());
NakedObject object;
object = objectLoader.recreateAdapterForPersistent(oid, specification);
if (data.getFieldContent() != null) {
object.setOptimisticLock(data.getVersion());
ResolveState state;
state = data.hasCompleteData() ? ResolveState.RESOLVING : ResolveState.RESOLVING_PART;
LOG.debug("restoring existing object (" + state.name() + ") " + object);
setupFields(data, objectLoader, object, state, knownObjects);
}
return object;
}
private NakedObject restoreTransient(
final ObjectData data, final NakedObjectLoader objectLoader, final KnownObjects knownObjects) {
NakedObjectSpecification specification = NakedObjectsContext.getReflector().loadSpecification(data.getType());
NakedObject object;
object = objectLoader.recreateTransientInstance(data.getOid(), specification);
LOG.debug("restore transient object " + object);
knownObjects.put(object, data);
setUpFields(data, object, knownObjects);
return object;
}
private NakedValue restoreValue(final ValueData valueData) {
NakedValue value;
if (valueData.getEncodedValue() == null) {
value = null;
} else {
value = NakedObjectsContext.getObjectLoader().createAdapterForValue(valueData.getEncodedValue());
}
return value;
}
private void setUpCollectionField(
final ObjectData parentData, final NakedObject object, final NakedObjectField field, final CollectionData content, final KnownObjects knownObjects) {
if (!content.hasAllElements()) {
NakedCollection col = (NakedCollection) field.get(object);
if (col.getResolveState() != ResolveState.GHOST) {
LOG.debug("No data for collection: " + field.getId());
if (object.getVersion().different(parentData.getVersion())) {
LOG.debug("clearing collection as versions differ: " + object.getVersion() + " " + parentData.getVersion());
col.init(new Object[0]);
col.changeState(ResolveState.GHOST);
}
}
return;
} else {
int size = content.getElements().length;
NakedObject[] elements = new NakedObject[size];
for (int j = 0; j < elements.length; j++) {
elements[j] = restoreObject(content.getElements()[j], knownObjects);
LOG.debug("adding element to " + field.getId() + ": " + elements[j]);
}
NakedCollection col = (NakedCollection) field.get(object);
ResolveState initialState = col.getResolveState();
ResolveState state = nextState(initialState, content.hasAllElements());
if (state != null) {
NakedObjectsContext.getObjectLoader().start(col, state);
((OneToManyAssociation) field).initCollection(object, elements);
NakedObjectsContext.getObjectLoader().end(col);
} else {
LOG.warn("not initialising collection " + col + " due to current state " + initialState);
}
}
}
private void setupFields(
final ObjectData data, final NakedObjectLoader objectLoader, final NakedObject object, final ResolveState state, final KnownObjects knownObjects) {
if (object.getResolveState().isDeserializable(state)) {
objectLoader.start(object, state);
setUpFields(data, object, knownObjects);
objectLoader.end(object);
}
}
private void setUpFields(final ObjectData data, final NakedObject object, final KnownObjects knownObjects) {
Data[] fieldContent = data.getFieldContent();
if (fieldContent != null && fieldContent.length > 0) {
NakedObjectField[] fields = dataStructure.getFields(object.getSpecification());
if (fields.length != fieldContent.length) {
throw new NakedObjectsRemoteException("Data received for different number of fields; expected " + fields.length
+ ", but was " + fieldContent.length);
}
for (int i = 0; i < fields.length; i++) {
NakedObjectField field = fields[i];
Data fieldData = fieldContent[i];
if (fieldData == null || !field.isPersisted()) {
LOG.debug("no data for field " + field.getId());
continue;
}
if (field.isCollection()) {
setUpCollectionField(data, object, field, (CollectionData) fieldData, knownObjects);
} else if (field.isValue()) {
setUpValueField(object, (ValueAssociation) field, fieldData);
} else {
setUpReferenceField(object, (OneToOneAssociation) field, fieldData, knownObjects);
}
}
}
}
private void setUpReferenceField(
final NakedObject object, final OneToOneAssociation field, final Data data, final KnownObjects knownObjects) {
NakedObject associate;
associate = restoreObject(data, knownObjects);
LOG.debug("setting association for field " + field.getId() + ": " + associate);
field.initAssociation(object, associate);
}
private void setUpValueField(final NakedObject object, final ValueAssociation field, final Data data) {
String value;
if (data instanceof NullData) {
value = null;
} else {
value = ((ValueData) data).getEncodedValue();
}
NakedValue valueAdapter = NakedObjectsContext.getObjectLoader().createValueInstance(field.getSpecification());
valueAdapter.restoreFromEncodedString(value);
LOG.debug("setting value for field " + field.getId() + ": " + valueAdapter);
field.initValue(object, valueAdapter);
}
private NakedObject updateLoadedObject(
final ObjectData data, final Oid oid, final NakedObjectLoader objectLoader, final KnownObjects knownObjects) {
// object known and we have all the latest data; update/resolve the object
NakedObject object;
object = objectLoader.getAdapterFor(oid);
if (data.getFieldContent() != null) {
object.setOptimisticLock(data.getVersion());
ResolveState state = nextState(object.getResolveState(), data.hasCompleteData());
if (state != null) {
LOG.debug("updating existing object (" + state.name() + ") " + object);
setupFields(data, objectLoader, object, state, knownObjects);
NakedObjectsContext.getUpdateNotifer().addChangedObject(object);
}
} else {
if (data.getVersion() != null && data.getVersion().different(object.getVersion())) {
// TODO reload the object
}
}
return object;
}
public void madePersistent(final NakedObject object, final ObjectData update) {
if (update == null) {
return;
}
if (object.getResolveState().isTransient() && object.persistable() != Persistable.TRANSIENT) {
NakedObjectsContext.getObjectLoader().getAdapterFor(update.getOid()); // causes OID to be updated
object.setOptimisticLock(update.getVersion());
object.changeState(ResolveState.RESOLVED);
}
Data[] fieldData = update.getFieldContent();
if (fieldData == null) {
return;
}
NakedObjectField[] fields = dataStructure.getFields(object.getSpecification());
for (int i = 0; i < fieldData.length; i++) {
if (fieldData[i] == null) {
continue;
}
if (fields[i].isObject()) {
Naked field = ((OneToOneAssociation) fields[i]).get(object);
ObjectData fieldContent = (ObjectData) update.getFieldContent()[i];
if (field != null) {
madePersistent((NakedObject) field, fieldContent);
}
} else if (fields[i].isCollection()) {
CollectionData collectionData = (CollectionData) update.getFieldContent()[i];
NakedCollection collection = (NakedCollection) fields[i].get(object);
if (!collection.getResolveState().isPersistent()) {
collection.changeState(ResolveState.RESOLVED);
}
Enumeration elements = ((NakedCollection) fields[i].get(object)).elements();
for (int j = 0; j < collectionData.getElements().length; j++) {
NakedObject element = (NakedObject) elements.nextElement();
if (collectionData.getElements()[j] instanceof ObjectData) {
ObjectData elementData = (ObjectData) collectionData.getElements()[j];
madePersistent(element, elementData);
}
}
}
}
}
}
// Copyright (c) Naked Objects Group Ltd.
© 2015 - 2025 Weber Informatics LLC | Privacy Policy