001: /*-
002: * See the file LICENSE for redistribution information.
003: *
004: * Copyright (c) 2002,2008 Oracle. All rights reserved.
005: *
006: * $Id: RawObject.java,v 1.12.2.2 2008/01/07 15:14:21 cwl Exp $
007: */
008:
009: package com.sleepycat.persist.raw;
010:
011: import java.util.Arrays;
012: import java.util.Map;
013: import java.util.TreeSet;
014:
015: import com.sleepycat.persist.evolve.Conversion;
016: import com.sleepycat.persist.model.EntityModel;
017:
018: /**
019: * A raw instance that can be used with a {@link RawStore} or {@link
020: * Conversion}. A <code>RawObject</code> is used to represent instances of
021: * complex types (persistent classes with fields), arrays, and enum values. It
022: * is not used to represent non-enum simple types, which are represented as
023: * simple objects. This includes primitives, which are represented as simple
024: * objects using their wrapper class.
025: *
026: * <p>{@code RawObject} objects are thread-safe. Multiple threads may safely
027: * call the methods of a shared {@code RawObject} object.</p>
028: *
029: * @author Mark Hayes
030: */
031: public class RawObject {
032:
033: private static final String INDENT = " ";
034:
035: private RawType type;
036: private Map<String, Object> values;
037: private Object[] elements;
038: private String enumConstant;
039: private RawObject super Object;
040:
041: /**
042: * Creates a raw object with a given set of field values for a complex
043: * type.
044: *
045: * @param type the type of this raw object.
046: *
047: * @param values a map of field name to value for each declared field in
048: * the class, or null to create an empty map. Each value in the map is a
049: * {@link RawObject}, a {@link <a
050: * href="../model/Entity.html#simpleTypes">simple type</a>} instance, or
051: * null.
052: *
053: * @param superObject the instance of the superclass, or null if the
054: * superclass is {@code Object}.
055: *
056: * @throws IllegalArgumentException if the type argument is an array type.
057: */
058: public RawObject(RawType type, Map<String, Object> values,
059: RawObject super Object) {
060: if (type == null || values == null) {
061: throw new NullPointerException();
062: }
063: this .type = type;
064: this .values = values;
065: this .super Object = super Object;
066: }
067:
068: /**
069: * Creates a raw object with the given array elements for an array type.
070: *
071: * @param type the type of this raw object.
072: *
073: * @param elements an array of elements. Each element in the array is a
074: * {@link RawObject}, a {@link <a
075: * href="../model/Entity.html#simpleTypes">simple type</a>} instance, or
076: * null.
077: *
078: * @throws IllegalArgumentException if the type argument is not an array
079: * type.
080: */
081: public RawObject(RawType type, Object[] elements) {
082: if (type == null || elements == null) {
083: throw new NullPointerException();
084: }
085: this .type = type;
086: this .elements = elements;
087: }
088:
089: /**
090: * Creates a raw object with the given enum value for an enum type.
091: *
092: * @param type the type of this raw object.
093: *
094: * @param enumConstant the String value of this enum constant; must be
095: * one of the Strings returned by {@link RawType#getEnumConstants}.
096: *
097: * @throws IllegalArgumentException if the type argument is not an array
098: * type.
099: */
100: public RawObject(RawType type, String enumConstant) {
101: if (type == null || enumConstant == null) {
102: throw new NullPointerException();
103: }
104: this .type = type;
105: this .enumConstant = enumConstant;
106: }
107:
108: /**
109: * Returns the raw type information for this raw object.
110: *
111: * <p>Note that if this object is unevolved, the returned type may be
112: * different from the current type returned by {@link
113: * EntityModel#getRawType EntityModel.getRawType} for the same class name.
114: * This can only occur in a {@link Conversion#convert
115: * Conversion.convert}.</p>
116: */
117: public RawType getType() {
118: return type;
119: }
120:
121: /**
122: * Returns a map of field name to value for a complex type, or null for an
123: * array type or an enum type. The map contains a String key for each
124: * declared field in the class. Each value in the map is a {@link
125: * RawObject}, a {@link <a href="../model/Entity.html#simpleTypes">simple
126: * type</a>} instance, or null.
127: *
128: * <p>There will be an entry in the map for every field declared in this
129: * type, as determined by {@link RawType#getFields} for the type returned
130: * by {@link #getType}. Values in the map may be null for fields with
131: * non-primitive types.</p>
132: */
133: public Map<String, Object> getValues() {
134: return values;
135: }
136:
137: /**
138: * Returns the array of elements for an array type, or null for a complex
139: * type or an enum type. Each element in the array is a {@link RawObject},
140: * a {@link <a href="../model/Entity.html#simpleTypes">simple type</a>}
141: * instance, or null.
142: */
143: public Object[] getElements() {
144: return elements;
145: }
146:
147: /**
148: * Returns the enum constant String for an enum type, or null for a complex
149: * type or an array type. The String returned will be one of the Strings
150: * returned by {@link RawType#getEnumConstants}.
151: */
152: public String getEnum() {
153: return enumConstant;
154: }
155:
156: /**
157: * Returns the instance of the superclass, or null if the superclass is
158: * {@code Object} or {@code Enum}.
159: */
160: public RawObject getSuper() {
161: return super Object;
162: }
163:
164: @Override
165: public boolean equals(Object other) {
166: if (other == this ) {
167: return true;
168: }
169: if (!(other instanceof RawObject)) {
170: return false;
171: }
172: RawObject o = (RawObject) other;
173: if (type != o.type) {
174: return false;
175: }
176: if (!Arrays.deepEquals(elements, o.elements)) {
177: return false;
178: }
179: if (values != null) {
180: if (!values.equals(o.values)) {
181: return false;
182: }
183: } else {
184: if (o.values != null) {
185: return false;
186: }
187: }
188: if (super Object != null) {
189: if (!super Object.equals(o.super Object)) {
190: return false;
191: }
192: } else {
193: if (o.super Object != null) {
194: return false;
195: }
196: }
197: return true;
198: }
199:
200: @Override
201: public int hashCode() {
202: return System.identityHashCode(type)
203: + Arrays.deepHashCode(elements)
204: + (values != null ? values.hashCode() : 0)
205: + (super Object != null ? super Object.hashCode() : 0);
206: }
207:
208: @Override
209: public String toString() {
210: StringBuffer buf = new StringBuffer(500);
211: formatRawObject(buf, "", null, false);
212: return buf.toString();
213: }
214:
215: private void formatRawObject(StringBuffer buf, String indent,
216: String id, boolean isSuper) {
217: String indent2 = indent + INDENT;
218: String endTag;
219: buf.append(indent);
220: if (type.isArray()) {
221: buf.append("<Array");
222: endTag = "</Array>";
223: } else if (type.isEnum()) {
224: buf.append("<Enum");
225: endTag = "</Enum>";
226: } else if (isSuper) {
227: buf.append("<Super");
228: endTag = "</Super>";
229: } else {
230: buf.append("<Object");
231: endTag = "</Object>";
232: }
233: if (id != null) {
234: formatId(buf, id);
235: }
236: if (type.isArray()) {
237: buf.append(" length=\"");
238: buf.append(elements.length);
239: buf.append('"');
240: }
241: buf.append(" class=\"");
242: buf.append(type.getClassName());
243: buf.append("\">\n");
244:
245: if (super Object != null) {
246: super Object.formatRawObject(buf, indent2, null, true);
247: }
248: if (type.isArray()) {
249: for (int i = 0; i < elements.length; i += 1) {
250: formatValue(buf, indent2, String.valueOf(i),
251: elements[i]);
252: }
253: } else if (type.isEnum()) {
254: buf.append(enumConstant);
255: } else {
256: TreeSet<String> keys = new TreeSet<String>(values.keySet());
257: for (String name : keys) {
258: formatValue(buf, indent2, name, values.get(name));
259: }
260: }
261: buf.append(indent);
262: buf.append(endTag);
263: buf.append("\n");
264: }
265:
266: private static void formatValue(StringBuffer buf, String indent,
267: String id, Object val) {
268: if (val == null) {
269: buf.append(indent);
270: buf.append("<Null");
271: formatId(buf, id);
272: buf.append("/>\n");
273: } else if (val instanceof RawObject) {
274: ((RawObject) val).formatRawObject(buf, indent, id, false);
275: } else {
276: buf.append(indent);
277: buf.append("<Value");
278: formatId(buf, id);
279: buf.append(" class=\"");
280: buf.append(val.getClass().getName());
281: buf.append("\">");
282: buf.append(val.toString());
283: buf.append("</Value>\n");
284: }
285: }
286:
287: private static void formatId(StringBuffer buf, String id) {
288: if (Character.isDigit(id.charAt(0))) {
289: buf.append(" index=\"");
290: } else {
291: buf.append(" field=\"");
292: }
293: buf.append(id);
294: buf.append('"');
295: }
296: }
|