All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.nakedobjects.nos.remote.command.marshal.BasicObjectDeserializer Maven / Gradle / Ivy

There is a newer version: 3.0.3
Show newest version
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