001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright
003: * notice. All rights reserved.
004: */
005: package com.tc.objectserver.managedobject;
006:
007: import org.apache.commons.lang.ArrayUtils;
008:
009: import com.tc.object.LiteralValues;
010: import com.tc.object.ObjectID;
011: import com.tc.object.dna.api.DNA;
012: import com.tc.object.dna.api.DNACursor;
013: import com.tc.object.dna.api.DNAWriter;
014: import com.tc.object.dna.api.PhysicalAction;
015: import com.tc.objectserver.mgmt.ManagedObjectFacade;
016: import com.tc.objectserver.mgmt.PhysicalManagedObjectFacade;
017: import com.tc.text.PrettyPrintable;
018: import com.tc.text.PrettyPrinter;
019: import com.tc.util.Assert;
020:
021: import java.io.IOException;
022: import java.io.ObjectInput;
023: import java.io.ObjectOutput;
024: import java.lang.reflect.Array;
025: import java.util.Arrays;
026: import java.util.HashMap;
027: import java.util.Map;
028: import java.util.Set;
029:
030: public class ArrayManagedObjectState extends LogicalManagedObjectState
031: implements PrettyPrintable {
032: private static final LiteralValues LITERAL_VALUES = (LiteralValues) new LiteralValues();
033:
034: private Object arrayData;
035: private int size = DNA.NULL_ARRAY_SIZE;
036: private boolean isPrimitive;
037: private int literalType;
038:
039: ArrayManagedObjectState(long classID) {
040: super (classID);
041: }
042:
043: protected ArrayManagedObjectState(ObjectInput in)
044: throws IOException {
045: super (in);
046: }
047:
048: public void apply(ObjectID objectID, DNACursor cursor,
049: BackReferences includeIDs) throws IOException {
050: ManagedObjectChangeListener listener = getListener();
051:
052: while (cursor.next()) {
053: PhysicalAction a = cursor.getPhysicalAction();
054:
055: if (a.isArrayElement()) {
056: int index = a.getArrayIndex();
057: Object value = a.getObject();
058: informListener(objectID, listener, index, value,
059: includeIDs);
060: setArrayElement(arrayData, index, value, literalType);
061: } else if (a.isEntireArray()) {
062: initArray(a.getObject());
063: if (!isPrimitive) {
064: Object[] objArray = (Object[]) arrayData;
065: for (int i = 0, n = size; i < n; i++) {
066: informListener(objectID, listener, i,
067: objArray[i], includeIDs);
068: }
069: }
070: } else if (a.isSubArray()) {
071: int startPos = a.getArrayIndex();
072: Object value = a.getObject();
073: int length = Array.getLength(value);
074: informListener(objectID, listener, startPos, length,
075: value, includeIDs);
076: System.arraycopy(value, 0, arrayData, startPos, length);
077: } else {
078: throw Assert.failure("unknown action type");
079: }
080: }
081: }
082:
083: private void initArray(Object array) {
084: arrayData = array;
085: size = Array.getLength(arrayData);
086: Class clazz = arrayData.getClass().getComponentType();
087: literalType = LITERAL_VALUES.valueForClassName(clazz.getName());
088: isPrimitive = clazz.isPrimitive();
089: }
090:
091: private static void setArrayElement(Object array, int index,
092: Object value, int type) {
093: switch (type) {
094: case LiteralValues.BOOLEAN:
095: ((boolean[]) array)[index] = ((Boolean) value)
096: .booleanValue();
097: break;
098: case LiteralValues.BYTE:
099: ((byte[]) array)[index] = ((Byte) value).byteValue();
100: break;
101: case LiteralValues.CHARACTER:
102: ((char[]) array)[index] = ((Character) value).charValue();
103: break;
104: case LiteralValues.DOUBLE:
105: ((double[]) array)[index] = ((Double) value).doubleValue();
106: break;
107: case LiteralValues.FLOAT:
108: ((float[]) array)[index] = ((Float) value).floatValue();
109: break;
110: case LiteralValues.INTEGER:
111: ((int[]) array)[index] = ((Integer) value).intValue();
112: break;
113: case LiteralValues.LONG:
114: ((long[]) array)[index] = ((Long) value).longValue();
115: break;
116: case LiteralValues.SHORT:
117: ((short[]) array)[index] = ((Short) value).shortValue();
118: break;
119: default:
120: ((Object[]) array)[index] = value;
121: break;
122: }
123: }
124:
125: /*
126: * This method should be called before the new value is applied
127: */
128: private void informListener(ObjectID objectID,
129: ManagedObjectChangeListener listener, int startPos,
130: int length, Object value, BackReferences includeIDs) {
131: if (!isPrimitive) {
132: Object[] oldArray = (Object[]) arrayData;
133: Object[] newArray = (Object[]) value;
134: for (int i = 0; i < length; i++) {
135: Object oldVal = oldArray[startPos + i];
136: Object newVal = newArray[i];
137: ObjectID oldValue = oldVal instanceof ObjectID ? (ObjectID) oldVal
138: : ObjectID.NULL_ID;
139: ObjectID newValue = newVal instanceof ObjectID ? (ObjectID) newVal
140: : ObjectID.NULL_ID;
141: listener.changed(objectID, oldValue, newValue);
142: includeIDs.addBackReference(newValue, objectID);
143: }
144: }
145: }
146:
147: private void informListener(ObjectID objectID,
148: ManagedObjectChangeListener listener, int index,
149: Object value, BackReferences includeIDs) {
150: if (!isPrimitive) {
151: Object[] objectArray = (Object[]) arrayData;
152: Object oldVal = objectArray[index];
153: ObjectID oldValue = oldVal instanceof ObjectID ? (ObjectID) oldVal
154: : ObjectID.NULL_ID;
155: ObjectID newValue = value instanceof ObjectID ? (ObjectID) value
156: : ObjectID.NULL_ID;
157: listener.changed(objectID, oldValue, newValue);
158: includeIDs.addBackReference(newValue, objectID);
159: }
160: }
161:
162: protected void addAllObjectReferencesTo(Set refs) {
163: if (!isPrimitive) {
164: addAllObjectReferencesFromIteratorTo(Arrays.asList(
165: (Object[]) arrayData).iterator(), refs);
166: }
167: }
168:
169: /*
170: * This method is overridden to give Arrays ability to be partial in L1
171: */
172: public void addObjectReferencesTo(ManagedObjectTraverser traverser) {
173: if (!isPrimitive) {
174: traverser.addReachableObjectIDs(getObjectReferences());
175: }
176: }
177:
178: public void dehydrate(ObjectID objectID, DNAWriter writer) {
179: writer.addEntireArray(arrayData);
180: writer.setArrayLength(size);
181: }
182:
183: public ManagedObjectFacade createFacade(ObjectID objectID,
184: String className, int limit) {
185: if (limit < 0) {
186: limit = size;
187: } else {
188: limit = Math.min(limit, size);
189: }
190:
191: Map dataCopy = new HashMap(limit);
192:
193: for (int i = 0; i < limit; i++) {
194: // XXX: Yuck...don't use reflection (need to separate primitive and object array state impls first through)
195: dataCopy.put(String.valueOf(i), Array.get(arrayData, i));
196: }
197:
198: boolean isArray = true;
199: ObjectID parent = ObjectID.NULL_ID;
200: boolean isInner = false;
201:
202: return new PhysicalManagedObjectFacade(objectID, parent,
203: className, dataCopy, isInner, limit, isArray);
204: }
205:
206: public PrettyPrinter prettyPrint(PrettyPrinter out) {
207: PrettyPrinter rv = out;
208: out = out.print(getClass().getName()).duplicateAndIndent()
209: .println();
210: out.indent().print("size: " + size).println();
211: out.indent().print("data: " + ArrayUtils.toString(arrayData))
212: .println();
213: return rv;
214: }
215:
216: public byte getType() {
217: return ARRAY_TYPE;
218: }
219:
220: protected void basicWriteTo(ObjectOutput out) throws IOException {
221: out.writeObject(arrayData);
222: }
223:
224: static ArrayManagedObjectState readFrom(ObjectInput in)
225: throws IOException, ClassNotFoundException {
226: ArrayManagedObjectState amos = new ArrayManagedObjectState(in);
227: amos.initArray(in.readObject());
228: return amos;
229: }
230:
231: protected boolean basicEquals(LogicalManagedObjectState other) {
232: ArrayManagedObjectState amo = (ArrayManagedObjectState) other;
233: return size == amo.size && isPrimitive == amo.isPrimitive
234: && literalType == amo.literalType
235: && equals(arrayData, amo.arrayData, literalType);
236: }
237:
238: private static boolean equals(Object a1, Object a2, int type) {
239: switch (type) {
240: case LiteralValues.BOOLEAN:
241: return Arrays.equals((boolean[]) a1, (boolean[]) a2);
242: case LiteralValues.BYTE:
243: return Arrays.equals((byte[]) a1, (byte[]) a2);
244: case LiteralValues.CHARACTER:
245: return Arrays.equals((char[]) a1, (char[]) a2);
246: case LiteralValues.DOUBLE:
247: return Arrays.equals((double[]) a1, (double[]) a2);
248: case LiteralValues.FLOAT:
249: return Arrays.equals((float[]) a1, (float[]) a2);
250: case LiteralValues.INTEGER:
251: return Arrays.equals((int[]) a1, (int[]) a2);
252: case LiteralValues.LONG:
253: return Arrays.equals((long[]) a1, (long[]) a2);
254: case LiteralValues.SHORT:
255: return Arrays.equals((short[]) a1, (short[]) a2);
256: default:
257: return Arrays.equals((Object[]) a1, (Object[]) a2);
258: }
259: }
260:
261: }
|