001    /*
002     * Cumulus4j - Securing your data in the cloud - http://cumulus4j.org
003     * Copyright (C) 2011 NightLabs Consulting GmbH
004     *
005     * This program is free software: you can redistribute it and/or modify
006     * it under the terms of the GNU Affero General Public License as
007     * published by the Free Software Foundation, either version 3 of the
008     * License, or (at your option) any later version.
009     *
010     * This program is distributed in the hope that it will be useful,
011     * but WITHOUT ANY WARRANTY; without even the implied warranty of
012     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013     * GNU Affero General Public License for more details.
014     *
015     * You should have received a copy of the GNU Affero General Public License
016     * along with this program.  If not, see <http://www.gnu.org/licenses/>.
017     */
018    package org.cumulus4j.store.model;
019    
020    import java.io.Serializable;
021    import java.util.Collections;
022    import java.util.Date;
023    import java.util.HashMap;
024    import java.util.Map;
025    
026    /**
027     * <p>
028     * Container holding the values of a persistent object.
029     * </p>
030     * <p>
031     * Objects to be stored in the database, are first represented by an instance of {@link ObjectContainer} and
032     * then serialised into a byte-array, which is finally encrypted and put into a {@link DataEntry}'s {@link DataEntry#getValue() value}.
033     * </p>
034     * <p>
035     * Note, that references to other objects
036     * are either not stored at all (in a "mapped-by"-relationship) or stored via the other object's
037     * {@link DataEntry#getDataEntryID() dataEntryID}; a persistent object is never stored as-is.
038     * </p>
039     *
040     * @author Marco หงุ่ยตระกูล-Schulze - marco at nightlabs dot de
041     */
042    public class ObjectContainer
043    implements Serializable, Cloneable
044    {
045            private static final long serialVersionUID = 4L;
046    
047            /**
048             * Stores the concrete value of a field mapped by the persistent {@link FieldMeta#getFieldID() fieldID}.
049             * If the value originally was a persistable object, it is replaced by its object-ID.
050             */
051            private Map<Long, Object> fieldID2value = new HashMap<Long, Object>();
052    
053            private Object version;
054    
055            public ObjectContainer() { }
056    
057    // TODO maybe do custom serialisation/deserialisation in order to better keep compatibility? Or should we maybe instead use
058    // ObjectContainer2, ObjectContainer3 etc. (i.e. other classes)?
059    //      private void writeObject(java.io.ObjectOutputStream out)
060    //      throws IOException
061    //      {
062    //              out.defaultWriteObject();
063    //      }
064    //
065    //      private void readObject(java.io.ObjectInputStream in)
066    //      throws IOException, ClassNotFoundException
067    //      {
068    //              in.defaultReadObject();
069    //      }
070    //
071    //      @SuppressWarnings("unused") // this method seems to be so new to Java that the Eclipse compiler doesn't know it yet and shows a warning.
072    //      private void readObjectNoData()
073    //      throws ObjectStreamException
074    //      {
075    //              // no special handling necessary
076    //      }
077    
078            /**
079             * Get a value.
080             * @param fieldID the field's persistent ID, i.e. a reference to {@link FieldMeta#getFieldID() FieldMeta.fieldID}.
081             * @return the value or <code>null</code>.
082             */
083            public Object getValue(long fieldID)
084            {
085                    return fieldID2value.get(fieldID);
086            }
087    
088            /**
089             * Set a value.
090             * @param fieldID the field's persistent ID, i.e. a reference to {@link FieldMeta#getFieldID() FieldMeta.fieldID}.
091             * @param value either the raw value or the object-ID of a persistable object. Persistable objects are never stored
092             * directly in an <code>ObjectContainer</code>.
093             */
094            public void setValue(long fieldID, Object value)
095            {
096                    if (value == null)
097                            fieldID2value.remove(fieldID);
098                    else
099                            fieldID2value.put(fieldID, value);
100            }
101    
102            protected void setValues(Map<Long, Object> fieldID2value) {
103                    if (fieldID2value == null)
104                            throw new IllegalArgumentException("fieldID2value == null");
105    
106                    this.fieldID2value.putAll(fieldID2value);
107            }
108    
109            public Map<Long, Object> getFieldID2value() {
110                    return Collections.unmodifiableMap(fieldID2value);
111            }
112    
113            /**
114             * Get the object's version or <code>null</code>, if the persistence-capable class has no versioning enabled.
115             * The version can be a {@link Long}, a {@link Date} or anything else supported by DataNucleus/JDO.
116             * @return the object's version or <code>null</code>.
117             */
118            public Object getVersion() {
119                    return version;
120            }
121            public void setVersion(Object version) {
122                    this.version = version;
123            }
124    
125            @Override
126            public ObjectContainer clone()
127            {
128                    ObjectContainer clone;
129                    try {
130                            clone = (ObjectContainer) super.clone();
131                    } catch (CloneNotSupportedException e) {
132                            throw new RuntimeException(e); // should never happen => wrap as RuntimeException!
133                    }
134                    clone.fieldID2value = new HashMap<Long, Object>(this.fieldID2value);
135                    return clone;
136            }
137    }