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.fieldmanager;
019
020 import java.lang.reflect.Array;
021 import java.util.Collection;
022 import java.util.HashMap;
023 import java.util.Map;
024
025 import javax.jdo.PersistenceManager;
026
027 import org.cumulus4j.store.ObjectContainerHelper;
028 import org.cumulus4j.store.model.ClassMeta;
029 import org.cumulus4j.store.model.FieldMeta;
030 import org.cumulus4j.store.model.ObjectContainer;
031 import org.datanucleus.exceptions.NucleusDataStoreException;
032 import org.datanucleus.metadata.AbstractClassMetaData;
033 import org.datanucleus.metadata.AbstractMemberMetaData;
034 import org.datanucleus.metadata.Relation;
035 import org.datanucleus.store.ExecutionContext;
036 import org.datanucleus.store.ObjectProvider;
037 import org.datanucleus.store.fieldmanager.AbstractFieldManager;
038 import org.datanucleus.store.types.sco.SCO;
039 import org.datanucleus.store.types.sco.SCOUtils;
040
041 /**
042 * Manager for the process of persisting a user object into the datastore, handling the translation from the
043 * users own object into the DataEntry object.
044 * @author Marco หงุ่ยตระกูล-Schulze - marco at nightlabs dot de
045 */
046 public class StoreFieldManager extends AbstractFieldManager
047 {
048 private ObjectProvider op;
049 private PersistenceManager pmData;
050 private ExecutionContext ec;
051 private ClassMeta classMeta;
052 private AbstractClassMetaData dnClassMetaData;
053 private ObjectContainer objectContainer;
054
055 public StoreFieldManager(
056 ObjectProvider op,
057 PersistenceManager pmData,
058 ClassMeta classMeta,
059 AbstractClassMetaData dnClassMetaData,
060 ObjectContainer objectContainer // populated by this class
061 )
062 {
063 this.op = op;
064 this.pmData = pmData;
065 this.ec = op.getExecutionContext();
066 this.classMeta = classMeta;
067 this.dnClassMetaData = dnClassMetaData;
068 this.objectContainer = objectContainer;
069 }
070
071 private long getFieldID(int fieldNumber)
072 {
073 AbstractMemberMetaData mmd = dnClassMetaData.getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber);
074
075 FieldMeta fieldMeta = classMeta.getFieldMeta(mmd.getClassName(), mmd.getName());
076 if (fieldMeta == null)
077 throw new IllegalStateException("Unknown field! class=" + dnClassMetaData.getFullClassName() + " fieldNumber=" + fieldNumber + " fieldName=" + mmd.getName());
078
079 return fieldMeta.getFieldID();
080 }
081
082 @Override
083 public void storeBooleanField(int fieldNumber, boolean value) {
084 objectContainer.setValue(getFieldID(fieldNumber), value);
085 }
086
087 @Override
088 public void storeByteField(int fieldNumber, byte value) {
089 objectContainer.setValue(getFieldID(fieldNumber), value);
090 }
091
092 @Override
093 public void storeCharField(int fieldNumber, char value) {
094 objectContainer.setValue(getFieldID(fieldNumber), value);
095 }
096
097 @Override
098 public void storeDoubleField(int fieldNumber, double value) {
099 objectContainer.setValue(getFieldID(fieldNumber), value);
100 }
101
102 @Override
103 public void storeFloatField(int fieldNumber, float value) {
104 objectContainer.setValue(getFieldID(fieldNumber), value);
105 }
106
107 @Override
108 public void storeIntField(int fieldNumber, int value) {
109 objectContainer.setValue(getFieldID(fieldNumber), value);
110 }
111
112 @Override
113 public void storeLongField(int fieldNumber, long value) {
114 objectContainer.setValue(getFieldID(fieldNumber), value);
115 }
116
117 @Override
118 public void storeShortField(int fieldNumber, short value) {
119 objectContainer.setValue(getFieldID(fieldNumber), value);
120 }
121
122 @Override
123 public void storeStringField(int fieldNumber, String value) {
124 objectContainer.setValue(getFieldID(fieldNumber), value);
125 }
126
127 @Override
128 public void storeObjectField(int fieldNumber, Object value)
129 {
130 AbstractMemberMetaData mmd = dnClassMetaData.getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber);
131
132 FieldMeta fieldMeta = classMeta.getFieldMeta(mmd.getClassName(), mmd.getName());
133 if (fieldMeta == null)
134 throw new IllegalStateException("Unknown field! class=" + dnClassMetaData.getFullClassName() + " fieldNumber=" + fieldNumber + " fieldName=" + mmd.getName());
135
136 if (value == null) {
137 objectContainer.setValue(fieldMeta.getFieldID(), null);
138 return;
139 }
140
141 int relationType = mmd.getRelationType(ec.getClassLoaderResolver());
142
143 // Replace any SCO field that isn't already a wrapper, with its wrapper object
144 boolean[] secondClassMutableFieldFlags = dnClassMetaData.getSCOMutableMemberFlags();
145 if (secondClassMutableFieldFlags[fieldNumber] && !(value instanceof SCO))
146 value = op.wrapSCOField(fieldNumber, value, false, true, true);
147
148 if (relationType == Relation.NONE)
149 {
150 if (mmd.hasCollection()) {
151 // Replace the special DN collection by a simple array.
152 Collection<?> collection = (Collection<?>)value;
153 Object[] values = collection.toArray(new Object[collection.size()]);
154 objectContainer.setValue(fieldMeta.getFieldID(), values);
155 }
156 else if (mmd.hasMap()) {
157 // replace the special DN Map by a simple HashMap.
158 Map<?,?> valueMap = (Map<?, ?>) value;
159
160 Map<Object, Object> map;
161 @SuppressWarnings("unchecked")
162 Class<? extends Map<Object, Object>> instanceType = SCOUtils.getContainerInstanceType(mmd.getType(), mmd.getOrderMetaData() != null);
163 try {
164 map = instanceType.newInstance();
165 } catch (InstantiationException e) {
166 throw new NucleusDataStoreException(e.getMessage(), e);
167 } catch (IllegalAccessException e) {
168 throw new NucleusDataStoreException(e.getMessage(), e);
169 }
170
171 map.putAll(valueMap);
172
173 objectContainer.setValue(fieldMeta.getFieldID(), map);
174 }
175 else // arrays are not managed (no special DN instances) and thus stored 'as is'...
176 objectContainer.setValue(fieldMeta.getFieldID(), value);
177 }
178 else if (Relation.isRelationSingleValued(relationType))
179 {
180 // Persistable object - persist the related object and store the identity in the cell
181 Object valuePC = ec.persistObjectInternal(value, op, fieldNumber, -1);
182 if (mmd.getMappedBy() == null) {
183 Object valueID = ObjectContainerHelper.entityToReference(ec, pmData, valuePC);
184 objectContainer.setValue(fieldMeta.getFieldID(), valueID);
185 }
186 }
187 else if (Relation.isRelationMultiValued(relationType))
188 {
189 // Collection/Map/Array
190 if (mmd.hasCollection())
191 {
192 Collection<?> collection = (Collection<?>)value;
193 Object[] ids = mmd.getMappedBy() != null ? null : new Object[collection.size()];
194 int idx = -1;
195 for (Object element : collection) {
196 Object elementPC = ec.persistObjectInternal(element, op, fieldNumber, -1);
197 if (ids != null) {
198 Object elementID = ObjectContainerHelper.entityToReference(ec, pmData, elementPC);
199 ids[++idx] = elementID;
200 }
201 }
202
203 if (ids != null)
204 objectContainer.setValue(fieldMeta.getFieldID(), ids);
205 }
206 else if (mmd.hasMap())
207 {
208 boolean keyIsPersistent = mmd.getMap().keyIsPersistent();
209 boolean valueIsPersistent = mmd.getMap().valueIsPersistent();
210
211 Map<?,?> valueMap = (Map<?, ?>) value;
212 Map<Object,Object> idMap = mmd.getMappedBy() != null ? null : new HashMap<Object, Object>(valueMap.size());
213 for (Map.Entry<?, ?> me : valueMap.entrySet()) {
214 Object k = me.getKey();
215 Object v = me.getValue();
216
217 if (keyIsPersistent) {
218 Object kpc = ec.persistObjectInternal(k, op, fieldNumber, -1);
219
220 if (idMap != null)
221 k = ObjectContainerHelper.entityToReference(ec, pmData, kpc);
222 }
223
224 if (valueIsPersistent) {
225 Object vpc = ec.persistObjectInternal(v, op, fieldNumber, -1);
226
227 if (idMap != null)
228 v = ObjectContainerHelper.entityToReference(ec, pmData, vpc);
229 }
230
231 if (idMap != null)
232 idMap.put(k, v);
233 }
234
235 if (idMap != null)
236 objectContainer.setValue(fieldMeta.getFieldID(), idMap);
237 }
238 else if (mmd.hasArray())
239 {
240 if (mmd.getMappedBy() != null)
241 throw new UnsupportedOperationException("NYI");
242
243 Object[] ids = new Object[Array.getLength(value)];
244 for (int i=0;i<Array.getLength(value);i++)
245 {
246 Object element = Array.get(value, i);
247 Object elementPC = ec.persistObjectInternal(element, op, fieldNumber, -1);
248 Object elementID = ObjectContainerHelper.entityToReference(ec, pmData, elementPC);
249 ids[i] = elementID;
250 }
251 objectContainer.setValue(fieldMeta.getFieldID(), ids);
252 }
253 }
254 else
255 throw new IllegalStateException("Unexpected relationType: " + relationType);
256 }
257
258 }