Package org.apache.isis.viewer.scimpi.dispatcher.context

Source Code of org.apache.isis.viewer.scimpi.dispatcher.context.DefaultOidObjectMapping

/*
*  Licensed to the Apache Software Foundation (ASF) under one
*  or more contributor license agreements.  See the NOTICE file
*  distributed with this work for additional information
*  regarding copyright ownership.  The ASF licenses this file
*  to you under the Apache License, Version 2.0 (the
*  "License"); you may not use this file except in compliance
*  with the License.  You may obtain a copy of the License at
*
*        http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing,
*  software distributed under the License is distributed on an
*  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
*  KIND, either express or implied.  See the License for the
*  specific language governing permissions and limitations
*  under the License.
*/

package org.apache.isis.viewer.scimpi.dispatcher.context;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import org.apache.isis.core.commons.debug.DebugBuilder;
import org.apache.isis.core.commons.exceptions.IsisException;
import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
import org.apache.isis.core.metamodel.adapter.oid.AggregatedOid;
import org.apache.isis.core.metamodel.adapter.oid.Oid;
import org.apache.isis.core.metamodel.facets.collections.modify.CollectionFacet;
import org.apache.isis.core.metamodel.facets.object.encodeable.EncodableFacet;
import org.apache.isis.core.metamodel.spec.ObjectSpecification;
import org.apache.isis.core.metamodel.spec.ObjectSpecification.CreationMode;
import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
import org.apache.isis.runtimes.dflt.runtime.persistence.oidgenerator.simple.SerialOid;
import org.apache.isis.runtimes.dflt.runtime.system.context.IsisContext;
import org.apache.isis.viewer.scimpi.dispatcher.ScimpiException;
import org.apache.isis.viewer.scimpi.dispatcher.context.RequestContext.Scope;

public class DefaultOidObjectMapping implements ObjectMapping {
    private static final Logger LOG = Logger.getLogger(DefaultOidObjectMapping.class);
    private final Map<String, TransientObjectMapping> requestTransients = new HashMap<String, TransientObjectMapping>();
    private final Map<String, TransientObjectMapping> sessionTransients = new HashMap<String, TransientObjectMapping>();
    private Class<? extends Oid> oidType;

    @Override
    public void append(final DebugBuilder debug) {
        append(debug, requestTransients, "request");
        append(debug, sessionTransients, "session");
    }

    protected void append(final DebugBuilder debug, final Map<String, TransientObjectMapping> transients, final String type) {
        final Iterator<String> ids = new HashSet(transients.keySet()).iterator();
        if (ids.hasNext()) {
            debug.appendTitle("Transient objects (" + type + ")");
            while (ids.hasNext()) {
                final String key = ids.next();
                debug.appendln(key, transients.get(key).debug());
            }
        }
    }

    @Override
    public void appendMappings(final DebugBuilder request) {
    }

    @Override
    public void clear() {
        requestTransients.clear();

        final List<String> remove = new ArrayList<String>();
        for (final String id : sessionTransients.keySet()) {
            if (!sessionTransients.get(id).getOid().isTransient()) {
                remove.add(id);
                sessionTransients.put(id, null);
            }
        }
        for (final String id : remove) {
            sessionTransients.remove(id);
        }
    }

    @Override
    public void endSession() {
        sessionTransients.clear();
    }

    @Override
    public String mapTransientObject(final ObjectAdapter object) {
        try {
            final List<ObjectAdapter> savedObject = new ArrayList<ObjectAdapter>();
            final JSONObject data = encodeTransientData(object, savedObject);
            return "D" + data.toString(4);
        } catch (final JSONException e) {
            throw new ScimpiException(e);
        }
    }

    private JSONObject encodeTransientData(final ObjectAdapter object, final List<ObjectAdapter> savedObject) throws JSONException {
        if (savedObject.contains(object)) {
            return null;
        }
        savedObject.add(object);

        final JSONObject data = new JSONObject();
        final ObjectSpecification specification = object.getSpecification();
        data.put("_class", specification.getFullIdentifier());

        final Oid oid = object.getOid();
        String encodedOid;
        if (oid instanceof AggregatedOid) {
            final AggregatedOid aoid = (AggregatedOid) oid;
            final Oid parentOid = aoid.getParentOid();
            final String aggregatedId = aoid.getId();
            encodedOid = Long.toString(((SerialOid) parentOid).getSerialNo(), 16) + "@" + aggregatedId;
        } else if (oid instanceof SerialOid) {
            encodedOid = Long.toString(((SerialOid) oid).getSerialNo(), 16);
        } else {
            throw new ScimpiException("Unsupportred OID type " + oid);
        }

        data.put("_id", encodedOid);

        for (final ObjectAssociation association : specification.getAssociations()) {
            final ObjectAdapter fieldValue = association.get(object);
            final String fieldName = association.getId();
            if (fieldValue == null) {
                data.put(fieldName, (Object) null);
            } else if (association.getSpecification().isEncodeable()) {
                final EncodableFacet encodeableFacet = fieldValue.getSpecification().getFacet(EncodableFacet.class);
                data.put(fieldName, encodeableFacet.toEncodedString(fieldValue));
            } else if (association instanceof OneToManyAssociation) {
                final List<JSONObject> collection = new ArrayList<JSONObject>();
                final CollectionFacet facet = fieldValue.getSpecification().getFacet(CollectionFacet.class);
                for (final ObjectAdapter element : facet.iterable(fieldValue)) {
                    collection.add(encodeTransientData(element, savedObject));
                }
                data.put(fieldName, collection);
            } else {
                if (fieldValue.isTransient() || fieldValue.isAggregated()) {
                    final JSONObject saveData = encodeTransientData(fieldValue, savedObject);
                    if (saveData == null) {
                        data.put(fieldName, mapObject(fieldValue, Scope.INTERACTION));
                    } else {
                        data.put(fieldName, saveData);
                    }
                } else {
                    data.put(fieldName, mapObject(fieldValue, Scope.INTERACTION));
                }
            }
        }
        return data;
    }

    @Override
    public String mapObject(final ObjectAdapter inObject, final Scope scope) {
        // TODO need to ensure that transient objects are remapped each time so
        // that any changes are added to
        // session data
        // continue work here.....here

        ObjectAdapter object = inObject;

        final Oid oid = object.getOid();
        if (oidType == null) {
            oidType = oid.getClass();
        }

        String encodedOid;
        if (oid instanceof AggregatedOid) {
            final AggregatedOid aoid = (AggregatedOid) oid;
            final Oid parentOid = aoid.getParentOid();
            object = IsisContext.getPersistenceSession().getAdapterManager().getAdapterFor(parentOid);
            final String aggregatedId = aoid.getId();
            encodedOid = Long.toString(((SerialOid) parentOid).getSerialNo(), 16) + "@" + aggregatedId;
        } else if (oid instanceof SerialOid) {
            encodedOid = Long.toString(((SerialOid) oid).getSerialNo(), 16);
        } else {
            encodedOid = IsisContext.getPersistenceSession().getOidGenerator().getOidStringifier().enString(oid);
        }

        final boolean isTransient = object.isTransient();
        final String transferableId = (isTransient ? "T" : "P") + object.getSpecification().getFullIdentifier() + "@" + encodedOid;
        LOG.debug("encoded " + oid + " as " + transferableId + " ~ " + encodedOid);

        if (inObject.isTransient()) {

            // TODO cache these in requests so that Mementos are only created
            // once.
            // TODO if Transient/Interaction then return state; other store
            // state in session an return OID
            // string
            final TransientObjectMapping mapping = new TransientObjectMapping(inObject);
            if (scope == Scope.REQUEST) {
                requestTransients.put(transferableId, mapping);
            } else if (scope == Scope.INTERACTION || scope == Scope.SESSION) {
                sessionTransients.put(transferableId, mapping);
            } else {
                throw new ScimpiException("Can't hold globally transient object");
            }
        }
        return transferableId;
    }

    @Override
    public ObjectAdapter mappedTransientObject(final String data) {
        final String objectData = data; // StringEscapeUtils.unescapeHtml(data);
        LOG.debug("data" + objectData);

        try {
            final JSONObject jsonObject = new JSONObject(objectData);
            final ObjectAdapter object = restoreTransientObject(jsonObject);
            return object;
        } catch (final JSONException e) {
            throw new ScimpiException("Problem reading data: " + data, e);
        }
    }

    private ObjectAdapter restoreTransientObject(final JSONObject jsonObject) throws JSONException {
        final String cls = jsonObject.getString("_class");
        final String id = jsonObject.getString("_id");

        ObjectAdapter object;
        final ObjectSpecification specification = IsisContext.getSpecificationLoader().loadSpecification(cls);
        if (specification.isAggregated() && !specification.isCollection()) {
            final String[] split = id.split("@");
            final SerialOid parentOid = SerialOid.createTransient(Long.parseLong(split[0], 16));
            final AggregatedOid oid = new AggregatedOid(parentOid, split[1]);
            object = IsisContext.getPersistenceSession().recreateAdapter(oid, specification);
        } else {
            object = mappedObject("T" + cls + "@" + id);
        }

        for (final ObjectAssociation association : specification.getAssociations()) {
            final String fieldName = association.getId();

            final Object fieldValue = jsonObject.has(fieldName) ? jsonObject.get(fieldName) : null;

            if (association.getSpecification().isEncodeable()) {
                if (fieldValue == null) {
                    ((OneToOneAssociation) association).initAssociation(object, null);
                } else {
                    final EncodableFacet encodeableFacet = association.getSpecification().getFacet(EncodableFacet.class);
                    final ObjectAdapter fromEncodedString = encodeableFacet.fromEncodedString((String) fieldValue);
                    ((OneToOneAssociation) association).initAssociation(object, fromEncodedString);
                }
            } else if (association instanceof OneToManyAssociation) {
                final JSONArray collection = (JSONArray) fieldValue;
                for (int i = 0; i < collection.length(); i++) {
                    final JSONObject jsonElement = (JSONObject) collection.get(i);
                    final ObjectAdapter objectToAdd = restoreTransientObject(jsonElement);
                    ((OneToManyAssociation) association).addElement(object, objectToAdd);
                }

                /*
                 * CollectionFacet facet =
                 * fieldValue.getSpecification().getFacet
                 * (CollectionFacet.class); for (ObjectAdapter element :
                 * facet.iterable(fieldValue)) {
                 * collection.add(saveData(element, savedObject)); }
                 * data.put(fieldName, collection);
                 */
            } else {
                if (fieldValue == null) {
                    ((OneToOneAssociation) association).initAssociation(object, null);
                } else {
                    if (fieldValue instanceof JSONObject) {
                        final ObjectAdapter fieldObject = restoreTransientObject((JSONObject) fieldValue);
                        ((OneToOneAssociation) association).initAssociation(object, fieldObject);
                    } else {
                        final ObjectAdapter field = mappedObject((String) fieldValue);
                        ((OneToOneAssociation) association).initAssociation(object, field);
                    }
                }
            }
        }
        return object;
    }

    @Override
    public ObjectAdapter mappedObject(final String id) {
        final char type = id.charAt(0);
        if ((type == 'T')) {
            TransientObjectMapping mapping = sessionTransients.get(id);
            if (mapping == null) {
                mapping = requestTransients.get(id);
            }
            if (mapping == null) {
                final String[] split = id.split("@");
                final ObjectSpecification spec = IsisContext.getSpecificationLoader().loadSpecification(split[0].substring(1));
                final Object pojo = spec.createObject(CreationMode.NO_INITIALIZE);
                final String oidData = split[1];
                final SerialOid oid = SerialOid.createTransient(Long.valueOf(oidData, 16).longValue());
                return IsisContext.getPersistenceSession().recreateAdapter(oid, pojo);
            }
            final ObjectAdapter mappedTransientObject = mapping.getObject();
            LOG.debug("retrieved " + mappedTransientObject.getOid() + " for " + id);
            return mappedTransientObject;
        } else {
            final String[] split = id.split("@");
            final ObjectSpecification spec = IsisContext.getSpecificationLoader().loadSpecification(split[0].substring(1));

            try {
                final String oidData = split[1];
                LOG.debug("decoding " + oidData);

                ObjectAdapter loadObject;
                Oid oid;
                // HACK - to remove after fix!
                if (oidType == null) {
                    oidType = IsisContext.getPersistenceSession().getServices().get(0).getOid().getClass();
                }
                if (split.length > 2) {
                    final SerialOid parentOid = SerialOid.createPersistent(Long.parseLong(oidData, 16));
                    oid = new AggregatedOid(parentOid, split[2]);
                    IsisContext.getPersistenceSession().loadObject(parentOid, spec);
                    loadObject = IsisContext.getPersistenceSession().getAdapterManager().getAdapterFor(oid);
                } else if (oidType.isAssignableFrom(SerialOid.class)) {
                    oid = SerialOid.createPersistent(Long.parseLong(oidData, 16));
                    loadObject = IsisContext.getPersistenceSession().loadObject(oid, spec);
                } else {
                    oid = IsisContext.getPersistenceSession().getOidGenerator().getOidStringifier().deString(oidData);
                    loadObject = IsisContext.getPersistenceSession().loadObject(oid, spec);
                }

                return loadObject;
            } catch (final SecurityException e) {
                throw new IsisException(e);
            }
        }
    }

    @Override
    public void reloadIdentityMap() {
        final Iterator<TransientObjectMapping> mappings = sessionTransients.values().iterator();
        while (mappings.hasNext()) {
            final TransientObjectMapping mapping = mappings.next();
            mapping.reload();
        }
    }

    @Override
    public void unmapObject(final ObjectAdapter object, final Scope scope) {
        sessionTransients.remove(object.getOid());
        requestTransients.remove(object.getOid());
    }

}
TOP

Related Classes of org.apache.isis.viewer.scimpi.dispatcher.context.DefaultOidObjectMapping

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.