Package org.apache.openjpa.xmlstore

Source Code of org.apache.openjpa.xmlstore.ObjectData

/*
* 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.openjpa.xmlstore;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.apache.openjpa.event.OrphanedKeyAction;
import org.apache.openjpa.kernel.FetchConfiguration;
import org.apache.openjpa.kernel.OpenJPAStateManager;
import org.apache.openjpa.kernel.StoreContext;
import org.apache.openjpa.meta.ClassMetaData;
import org.apache.openjpa.meta.FieldMetaData;
import org.apache.openjpa.meta.JavaTypes;
import org.apache.openjpa.meta.ValueMetaData;
import org.apache.openjpa.util.Proxy;
import org.apache.openjpa.util.UnsupportedException;
import serp.util.Numbers;

/**
* In-memory form of data in datastore backing a single persistent object.
*/
public final class ObjectData
    implements Cloneable {

    private Object _oid;
    private Object[] _data;
    private Long _version;
    private ClassMetaData _meta;

    /**
     * Create the object without underlying data. Just pass in type specific
     * metadata and the oid.
     */
    public ObjectData(Object oid, ClassMetaData meta) {
        _oid = oid;
        _meta = meta;
        _data = new Object[meta.getFields().length];
    }

    /**
     * Getter for oid.
     */
    public Object getId() {
        return _oid;
    }

    /**
     * Get the data for the field with the given index.
     */
    public Object getField(int num) {
        return _data[num];
    }

    /**
     * Set the data for the field with the given index.
     */
    public void setField(int num, Object val) {
        _data[num] = val;
    }

    /**
     * Set the version number of the object.
     */
    public void setVersion(Long version) {
        _version = version;
    }

    /**
     * Get the version number of the object.
     */
    public Long getVersion() {
        return _version;
    }

    /**
     * Get the metadata associated with the type of persistent object for
     * which this data applies.
     */
    public ClassMetaData getMetaData() {
        return _meta;
    }

    /**
     * Load the data and version information for this object into the
     * given state manager. Only fields in the given fetch configuration are
     * loaded.
     */
    public void load(OpenJPAStateManager sm, FetchConfiguration fetch) {
        if (sm.getVersion() == null)
            sm.setVersion(_version);

        FieldMetaData[] fmds = _meta.getFields();
        for (int i = 0; i < fmds.length; i++)
            if (!sm.getLoaded().get(i) && fetch.requiresFetch(fmds[i])
                != FetchConfiguration.FETCH_NONE)
                sm.store(i, toLoadable(sm, fmds[i], _data[i], fetch));
    }

    /**
     * Load the data and version information for this object into the
     * given state manager. Only fields in the given bit set will be loaded.
     */
    public void load(OpenJPAStateManager sm, BitSet fields,
        FetchConfiguration fetch) {
        if (sm.getVersion() == null)
            sm.setVersion(_version);

        FieldMetaData[] fmds = _meta.getFields();
        for (int i = 0; i < fmds.length; i++)
            if (fields.get(i))
                sm.store(i, toLoadable(sm, fmds[i], _data[i], fetch));
    }

    /**
     * Convert the stored value <code>val</code> into a value for loading
     * into a state manager.
     */
    private static Object toLoadable(OpenJPAStateManager sm,
        FieldMetaData fmd, Object val, FetchConfiguration fetch) {
        if (val == null)
            return null;

        Collection c;
        switch (fmd.getTypeCode()) {
            case JavaTypes.COLLECTION:
                // the stored value must be a collection
                c = (Collection) val;

                // the state manager will create a proxy collection of the needed
                // type depending on the declared type of the user's field; the
                // proxy will perform dirty tracking, etc
                Collection c2 = (Collection) sm.newFieldProxy(fmd.getIndex());

                // populate the proxy collection with our stored data, converting
                // it to the right type from its stored form
                for (Iterator itr = c.iterator(); itr.hasNext();)
                    c2.add(toNestedLoadable(sm, fmd.getElement(), itr.next(),
                        fetch));
                return c2;

            case JavaTypes.ARRAY:
                // the stored value must be a collection; we put arrays into
                // collections for storage
                c = (Collection) val;

                // create a new array of the right type; unlike collections in
                // the case above, arrays cannot be proxied
                Object a = Array.newInstance(fmd.getElement().getType(),
                    c.size());

                // populate the array with our stored data, converting it to the
                // right type from its stored form
                int idx = 0;
                for (Iterator itr = c.iterator(); itr.hasNext(); idx++)
                    Array.set(a, idx, toNestedLoadable(sm, fmd.getElement(),
                        itr.next(), fetch));
                return a;

            case JavaTypes.MAP:
                // the stored value must be a map
                Map m = (Map) val;

                // the state manager will create a proxy map of the needed
                // type depending on the declared type of the user's field; the
                // proxy will perform dirty tracking, etc
                Map m2 = (Map) sm.newFieldProxy(fmd.getIndex());

                // populate the proxy map with our stored data, converting
                // it to the right type from its stored form
                for (Iterator itr = m.entrySet().iterator(); itr.hasNext();) {
                    Map.Entry e = (Map.Entry) itr.next();
                    m2.put(toNestedLoadable(sm, fmd.getKey(), e.getKey(),fetch),
                        toNestedLoadable(sm, fmd.getElement(), e.getValue(),
                            fetch));
                }
                return m2;

            default:
                // just convert the stored value into its loadable equivalent.
                return toNestedLoadable(sm, fmd, val, fetch);
        }
    }

    /**
     * Convert the given stored value <code>val</code> to a value for loading
     * into a state manager. The value <code>val</code> must be a singular
     * value; it cannot be a container.
     */
    private static Object toNestedLoadable(OpenJPAStateManager sm,
        ValueMetaData vmd, Object val, FetchConfiguration fetch) {
        if (val == null)
            return null;

        switch (vmd.getTypeCode()) {
            // clone the date to prevent direct modification of our stored value
            case JavaTypes.DATE:
                return ((Date) val).clone();

            case JavaTypes.PC:
            case JavaTypes.PC_UNTYPED:
                // for relations to other persistent objects, we store the related
                // object's oid -- convert it back into a persistent instance
                StoreContext ctx = sm.getContext();
                Object pc = ctx.find(val, fetch, null, null, 0);
                if (pc != null)
                    return pc;
                OrphanedKeyAction action = ctx.getConfiguration().
                    getOrphanedKeyActionInstance();
                return action.orphan(val, sm, vmd);
            default:
                return val;
        }
    }

    /**
     * Store the data and version information for this object from the
     * given state manager. Only dirty fields will be stored.
     */
    public void store(OpenJPAStateManager sm) {
        _version = (Long) sm.getVersion();

        // if the version has not been set in the state manager (only true
        // when the object is new), set the version number to 0
        if (_version == null)
            _version = Numbers.valueOf(0L);

        // run through each persistent field in the state manager and store it
        FieldMetaData[] fmds = _meta.getFields();
        for (int i = 0; i < fmds.length; i++) {
            if (sm.getDirty().get(i)
                && fmds[i].getManagement() == fmds[i].MANAGE_PERSISTENT)
                _data[i] = toStorable(fmds[i], sm.fetch(i), sm.getContext());
        }
    }

    /**
     * Convert the given field value <code>val</code> to a form we can store.
     */
    private static Object toStorable(FieldMetaData fmd, Object val,
        StoreContext ctx) {
        if (val == null)
            return null;

        Collection c;
        switch (fmd.getTypeCode()) {
            case JavaTypes.COLLECTION:
                c = (Collection) val;

                // create a collection to copy the elements into for storage, and
                // populate it with converted element values
                Collection c2 = new ArrayList();
                for (Iterator itr = c.iterator(); itr.hasNext();)
                    c2.add(toNestedStorable(fmd.getElement(), itr.next(), ctx));
                return c2;

            case JavaTypes.ARRAY:
                // create a collection to copy the elements into for storage, and
                // populate it with converted element values
                c = new ArrayList();
                for (int i = 0, len = Array.getLength(val); i < len; i++)
                    c.add(toNestedStorable(fmd.getElement(), Array.get(val, i),
                        ctx));
                return c;

            case JavaTypes.MAP:
                Map m = (Map) val;

                // create a map to copy the entries into for storage, and
                // populate it with converted entry values
                Map m2 = new HashMap();
                for (Iterator itr = m.entrySet().iterator(); itr.hasNext();) {
                    Map.Entry e = (Map.Entry) itr.next();
                    m2.put(toNestedStorable(fmd.getKey(), e.getKey(), ctx),
                        toNestedStorable(fmd.getElement(), e.getValue(), ctx));
                }
                return m2;

            default:
                // just convert the loaded value into its storable equivalent
                return toNestedStorable(fmd, val, ctx);
        }
    }

    /**
     * Convert the given loaded value <code>val</code> to a value for storing.
     * The value <code>val</code> must be a singular value; it cannot be a
     * container.
     */
    private static Object toNestedStorable(ValueMetaData vmd, Object val,
        StoreContext ctx) {
        if (val == null)
            return null;

        switch (vmd.getTypeCode()) {
            case JavaTypes.DATE:
                // if the date is a proxy (since Dates are second class
                // objects (SCOs) they can be proxied for dirty tracking,
                // etc) then copy the value out of it for storage
                if (val instanceof Proxy)
                    return ((Proxy) val).copy(val);
                return ((Date) val).clone();

            case JavaTypes.PC:
            case JavaTypes.PC_UNTYPED:
                return ctx.getObjectId(val);

            case JavaTypes.COLLECTION:
            case JavaTypes.ARRAY:
            case JavaTypes.MAP:
                // nested relation types (e.g. collections of collections)
                // are not currently supported
                throw new UnsupportedException("This store does not support "
                    + "nested containers (e.g. collections of collections).");

            default:
                return val;
        }
    }

    /**
     * Clone this data.
     */
    public Object clone() {
        ObjectData data = new ObjectData(_oid, _meta);
        data.setVersion(_version);

        // copy each field
        FieldMetaData[] fmds = _meta.getFields();
        for (int i = 0; i < fmds.length; i++) {
            Object val = _data[i];
            if (val == null) {
                data.setField(i, null);
                continue;
            }

            switch (fmds[i].getTypeCode()) {
                case JavaTypes.COLLECTION:
                case JavaTypes.ARRAY:
                    data.setField(i, new ArrayList((Collection) val));
                    break;
                case JavaTypes.MAP:
                    data.setField(i, new HashMap((Map) val));
                    break;
                default:
                    data.setField(i, val);
            }
        }
        return data;
    }

    public String toString() {
        StringBuffer buf = new StringBuffer();
        buf.append("Class: (" + _meta.getDescribedType().getName() + ")\n");
        buf.append("Object Id: (" + _oid + ")\n");
        buf.append("Version: (" + _version + ")\n");
        FieldMetaData[] fmds = _meta.getFields();
        for (int i = 0; i < fmds.length; i++) {
            buf.append("  Field: (" + i + ")\n");
            buf.append("  Name: (" + fmds[i].getName() + ")\n");
            buf.append("  Value: (" + _data[i] + ")\n");
    }
    return buf.toString ();
  }
}
TOP

Related Classes of org.apache.openjpa.xmlstore.ObjectData

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.