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

org.nakedobjects.plugins.remoting.shared.ObjectEncoderDeserializer Maven / Gradle / Ivy

package org.nakedobjects.plugins.remoting.shared;

import java.util.Enumeration;

import org.apache.log4j.Logger;
import org.nakedobjects.metamodel.adapter.NakedObject;
import org.nakedobjects.metamodel.adapter.ResolveState;
import org.nakedobjects.metamodel.adapter.oid.Oid;
import org.nakedobjects.metamodel.commons.exceptions.UnknownTypeException;
import org.nakedobjects.metamodel.facets.collections.modify.CollectionFacet;
import org.nakedobjects.metamodel.facets.object.encodeable.EncodeableFacet;
import org.nakedobjects.metamodel.spec.NakedObjectSpecification;
import org.nakedobjects.metamodel.spec.Persistability;
import org.nakedobjects.metamodel.spec.feature.NakedObjectAssociation;
import org.nakedobjects.metamodel.spec.feature.OneToManyAssociation;
import org.nakedobjects.metamodel.spec.feature.OneToOneAssociation;
import org.nakedobjects.metamodel.specloader.SpecificationLoader;
import org.nakedobjects.metamodel.util.CollectionFacetUtils;
import org.nakedobjects.plugins.remoting.shared.data.CollectionData;
import org.nakedobjects.plugins.remoting.shared.data.Data;
import org.nakedobjects.plugins.remoting.shared.data.EncodeableObjectData;
import org.nakedobjects.plugins.remoting.shared.data.IdentityData;
import org.nakedobjects.plugins.remoting.shared.data.KnownObjects;
import org.nakedobjects.plugins.remoting.shared.data.NullData;
import org.nakedobjects.plugins.remoting.shared.data.ObjectData;
import org.nakedobjects.plugins.remoting.shared.data.ReferenceData;
import org.nakedobjects.runtime.context.NakedObjectsContext;
import org.nakedobjects.runtime.persistence.PersistenceSession;
import org.nakedobjects.runtime.persistence.PersistenceSessionHydrator;
import org.nakedobjects.runtime.persistence.PersistorUtil;
import org.nakedobjects.runtime.persistence.adaptermanager.AdapterManager;
import org.nakedobjects.runtime.transaction.updatenotifier.UpdateNotifier;

class ObjectEncoderDeserializer {
    
    private static final Logger LOG = Logger.getLogger(ObjectEncoderDeserializer.class);
    
    
    private ObjectEncoderDataStructure dataStructure;
    

    /////////////////////////////////////////////////////////
    // DataStructure
    /////////////////////////////////////////////////////////

    public void setDataStructure(final ObjectEncoderDataStructure dataStructure) {
        this.dataStructure = dataStructure;
    }

    
    /////////////////////////////////////////////////////////
    // restore
    /////////////////////////////////////////////////////////

    public NakedObject restore(final Data data) {
        if (data instanceof CollectionData) {
            return restoreCollection((CollectionData) data, new KnownObjects());
        } else {
            return restoreObject(data, new KnownObjects());
        }
    }

    public NakedObject restore(final Data data, final KnownObjects knownObjects) {
        if (data instanceof CollectionData) {
            return restoreCollection((CollectionData) data, knownObjects);
        } else {
            return restoreObject(data, knownObjects);
        }
    }

    /////////////////////////////////////////////////////////
    // Helper: restoreCollection
    /////////////////////////////////////////////////////////

    private NakedObject restoreCollection(final CollectionData data, final KnownObjects knownObjects) {
        final String collectionType = data.getType();
        final NakedObjectSpecification collectionSpecification = getSpecificationLoader().loadSpecification(
                collectionType);

        /*
         * 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
         */
        NakedObject collection = getPersistenceSession().createInstance(collectionSpecification);
        if (data.getElements() == null) {
            LOG.debug("restoring empty collection");
            return collection;
        } else {
            final ReferenceData[] elements = data.getElements();
            LOG.debug("restoring collection " + elements.length + " elements");
            final NakedObject[] initData = new NakedObject[elements.length];
            for (int i = 0; i < elements.length; i++) {
                final NakedObject element = restoreObject(elements[i], knownObjects);
                LOG.debug("restoring collection element :" + element);
                initData[i] = element;
            }
            final CollectionFacet facet = CollectionFacetUtils.getCollectionFacetFromSpec(collection);
            facet.init(collection, initData);
            return collection;
        }
    }

    /////////////////////////////////////////////////////////
    // Helper: restoreObject
    /////////////////////////////////////////////////////////

    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 if (data instanceof EncodeableObjectData) {
            return restoreEncodable((EncodeableObjectData) data);
        } else {
            throw new UnknownTypeException(data);
        }
    }

    private NakedObject restoreObjectFromIdentity(final IdentityData data, final KnownObjects knownObjects) {
        final Oid oid = data.getOid();
        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 = getAdapterManager().getAdapterFor(oid);
        if (object == null) {
            final NakedObjectSpecification specification = getSpecificationLoader().loadSpecification(data.getType());
            object = getHydrator().recreateAdapter(oid, specification);
        }
        return object;
    }



    private NakedObject restoreObjectFromObject(final ObjectData data, final KnownObjects knownObjects) {
        if (knownObjects.containsKey(data)) {
            return knownObjects.get(data);
        }

        final Oid oid = data.getOid();
        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 = getAdapterManager().getAdapterFor(oid);
        if (object != null) {
            updateLoadedObject(data, object, knownObjects);
        } else if (oid.isTransient()) {
            object = restoreTransient(data, knownObjects);
        } else {
            object = restorePersistentObject(data, oid, knownObjects);
        }

        return object;
    }


    private NakedObject restoreTransient(final ObjectData data, final KnownObjects knownObjects) {
        final NakedObjectSpecification specification = getSpecificationLoader().loadSpecification(data.getType());

        NakedObject object;
        object = getHydrator().recreateAdapter(data.getOid(), specification);
        if (LOG.isDebugEnabled()) {
            LOG.debug("restore transient object " + object);
        }
        knownObjects.put(object, data);
        setUpFields(data, object, knownObjects);
        return object;
    }


    private NakedObject restorePersistentObject(final ObjectData data, final Oid oid, final KnownObjects knownObjects) {
        // unknown object; create an instance
        final NakedObjectSpecification specification = getSpecificationLoader().loadSpecification(data.getType());

        NakedObject object;
        object = getHydrator().recreateAdapter(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, object, state, knownObjects);
        }
        return object;
    }


    private NakedObject restoreEncodable(final EncodeableObjectData encodeableObjectData) {
        NakedObject value;
        if (encodeableObjectData.getEncodedObjectData() == null) {
            value = null;
        } else {
            final NakedObjectSpecification spec = getSpecificationLoader().loadSpecification(
                    encodeableObjectData.getType());
            final EncodeableFacet encoder = spec.getFacet(EncodeableFacet.class);
            value = encoder.fromEncodedString(encodeableObjectData.getEncodedObjectData());
        }
        return value;
    }


    /////////////////////////////////////////////////////////
    // Helpers: updateLoadedObject
    /////////////////////////////////////////////////////////

    private void updateLoadedObject(final ObjectData data, final NakedObject object, final KnownObjects knownObjects) {
        // object known and we have all the latest data; update/resolve the object
        if (data.getFieldContent() != null) {
            object.setOptimisticLock(data.getVersion());
            final ResolveState state = nextState(object.getResolveState(), data.hasCompleteData());
            if (state != null) {
                LOG.debug("updating existing object (" + state.name() + ") " + object);
                setupFields(data, object, state, knownObjects);
                getUpdateNotifier().addChangedObject(object);
            }
        } else {
            if (data.getVersion() != null && data.getVersion().different(object.getVersion())) {
                // TODO reload the object
            }
        }
    }



    /////////////////////////////////////////////////////////
    // Helpers: setupFields
    /////////////////////////////////////////////////////////

    private void setUpCollectionField(
            final ObjectData parentData,
            final NakedObject object,
            final NakedObjectAssociation field,
            final CollectionData content,
            final KnownObjects knownObjects) {
        if (!content.hasAllElements()) {
            final NakedObject collection = field.get(object);
            if (collection.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());

                    final CollectionFacet facet = CollectionFacetUtils.getCollectionFacetFromSpec(collection);
                    facet.init(collection, new NakedObject[0]);
                    collection.changeState(ResolveState.GHOST);
                }
            }
            return;
        } else {
            final int size = content.getElements().length;
            final 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]);
            }

            final NakedObject col = field.get(object);
            final ResolveState initialState = col.getResolveState();
            final ResolveState state = nextState(initialState, content.hasAllElements());
            if (state != null) {
                PersistorUtil.start(col, state);
                final NakedObject collection = ((OneToManyAssociation) field).get(object);
                final CollectionFacet facet = CollectionFacetUtils.getCollectionFacetFromSpec(collection);
                facet.init(collection, elements);
                PersistorUtil.end(col);
            } else {
                LOG.warn("not initialising collection " + col + " due to current state " + initialState);
            }
        }
    }

    private void setupFields(
            final ObjectData data,
            final NakedObject object,
            final ResolveState state,
            final KnownObjects knownObjects) {
        if (object.getResolveState().isDeserializable(state)) {
            PersistorUtil.start(object, state);
            setUpFields(data, object, knownObjects);
            PersistorUtil.end(object);
        }
    }

    private void setUpFields(final ObjectData data, final NakedObject object, final KnownObjects knownObjects) {
        final Data[] fieldContent = data.getFieldContent();
        if (fieldContent != null && fieldContent.length > 0) {
            final NakedObjectAssociation[] 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++) {
                final NakedObjectAssociation field = fields[i];
                final Data fieldData = fieldContent[i];
                if (fieldData == null || !field.isPersisted()) {
                    LOG.debug("no data for field " + field.getId());
                    continue;
                }

                if (field.isOneToManyAssociation()) {
                    setUpCollectionField(data, object, field, (CollectionData) fieldData, knownObjects);
                } else if (field.getSpecification().isEncodeable()) {
                    setUpEncodedField(object, (OneToOneAssociation) 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 setUpEncodedField(final NakedObject object, final OneToOneAssociation field, final Data data) {
        String value;
        if (data instanceof NullData) {
            field.initAssociation(object, null);
        } else {
            value = ((EncodeableObjectData) data).getEncodedObjectData();
            final EncodeableFacet encoder = field.getSpecification().getFacet(EncodeableFacet.class);
            final NakedObject valueAdapter = encoder.fromEncodedString(value);
            LOG.debug("setting value for field " + field.getId() + ": " + valueAdapter);
            field.initAssociation(object, valueAdapter);
        }
    }


    /////////////////////////////////////////////////////////
    // madePersistent
    /////////////////////////////////////////////////////////

    public void madePersistent(final NakedObject objectAdapter, final ObjectData update) {
        if (update == null) {
            return;
        }

        if (objectAdapter.isTransient() && 
        	objectAdapter.getSpecification().persistability() != Persistability.TRANSIENT) {

        	getAdapterManager().getAdapterFor(update.getOid()); // causes OID to be updated
            objectAdapter.setOptimisticLock(update.getVersion());
            objectAdapter.changeState(ResolveState.RESOLVED);
        }

        final Data[] fieldData = update.getFieldContent();
        if (fieldData == null) {
            return;
        }
        final NakedObjectAssociation[] fields = dataStructure.getFields(objectAdapter.getSpecification());
        for (int i = 0; i < fieldData.length; i++) {
            if (fieldData[i] == null) {
                continue;
            }
            if (fields[i].isOneToOneAssociation()) {
                final NakedObject field = ((OneToOneAssociation) fields[i]).get(objectAdapter);
                final ObjectData fieldContent = (ObjectData) update.getFieldContent()[i];
                if (field != null) {
                    madePersistent(field, fieldContent);
                }
            } else if (fields[i].isOneToManyAssociation()) {
                final CollectionData collectionData = (CollectionData) update.getFieldContent()[i];
                final NakedObject collectionAdapter = fields[i].get(objectAdapter);
                if (!collectionAdapter.isPersistent()) {
                    collectionAdapter.changeState(ResolveState.RESOLVED);
                }
                final CollectionFacet facet = CollectionFacetUtils.getCollectionFacetFromSpec(collectionAdapter);
                final Enumeration elements = facet.elements(collectionAdapter);
                for (int j = 0; j < collectionData.getElements().length; j++) {
                    final NakedObject element = (NakedObject) elements.nextElement();
                    if (collectionData.getElements()[j] instanceof ObjectData) {
                        final ObjectData elementData = (ObjectData) collectionData.getElements()[j];
                        madePersistent(element, elementData);
                    }
                }
            }
        }
    }


    /////////////////////////////////////////////////////////
    // Helpers: nextState
    /////////////////////////////////////////////////////////

    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;
    }


    /////////////////////////////////////////////////////////
    // Dependencies (from singletons)
    /////////////////////////////////////////////////////////

    private SpecificationLoader getSpecificationLoader() {
        return NakedObjectsContext.getSpecificationLoader();
    }

    private UpdateNotifier getUpdateNotifier() {
        return NakedObjectsContext.getUpdateNotifier();
    }

    private PersistenceSession getPersistenceSession() {
        return NakedObjectsContext.getPersistenceSession();
    }

    private AdapterManager getAdapterManager() {
        return getPersistenceSession().getAdapterManager();
    }

    private PersistenceSessionHydrator getHydrator() {
        return getPersistenceSession();
    }





}
// Copyright (c) Naked Objects Group Ltd.




© 2015 - 2025 Weber Informatics LLC | Privacy Policy