1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2002, 2013 Oracle and/or its affiliates.  All rights reserved.
5  *
6  */
7 
8 package com.sleepycat.persist.raw;
9 
10 import java.util.Arrays;
11 import java.util.Map;
12 import java.util.TreeSet;
13 
14 import com.sleepycat.persist.evolve.Conversion;
15 import com.sleepycat.persist.model.EntityModel;
16 
17 /**
18  * A raw instance that can be used with a {@link RawStore} or {@link
19  * Conversion}.  A <code>RawObject</code> is used to represent instances of
20  * complex types (persistent classes with fields), arrays, and enum values.  It
21  * is not used to represent non-enum simple types, which are represented as
22  * simple objects.  This includes primitives, which are represented as
23  * instances of their wrapper class.
24  *
25  * <p>{@code RawObject} objects are thread-safe.  Multiple threads may safely
26  * call the methods of a shared {@code RawObject} object.</p>
27  *
28  * @author Mark Hayes
29  */
30 public class RawObject {
31 
32     private static final String INDENT = "  ";
33 
34     private RawType type;
35     private Map<String, Object> values;
36     private Object[] elements;
37     private String enumConstant;
38     private RawObject superObject;
39 
40     /**
41      * Creates a raw object with a given set of field values for a complex
42      * type.
43      *
44      * @param type the type of this raw object.
45      *
46      * @param values a map of field name to value for each declared field in
47      * the class, or null to create an empty map.  Each value in the map is a
48      * {@link RawObject}, a {@link <a
49      * href="../model/Entity.html#simpleTypes">simple type</a>} instance, or
50      * null.
51      *
52      * @param superObject the instance of the superclass, or null if the
53      * superclass is {@code Object}.
54      *
55      * @throws IllegalArgumentException if the type argument is an array type.
56      */
RawObject(RawType type, Map<String, Object> values, RawObject superObject)57     public RawObject(RawType type,
58                      Map<String, Object> values,
59                      RawObject superObject) {
60         if (type == null || values == null) {
61             throw new NullPointerException();
62         }
63         this.type = type;
64         this.values = values;
65         this.superObject = superObject;
66     }
67 
68     /**
69      * Creates a raw object with the given array elements for an array type.
70      *
71      * @param type the type of this raw object.
72      *
73      * @param elements an array of elements.  Each element in the array is a
74      * {@link RawObject}, a {@link <a
75      * href="../model/Entity.html#simpleTypes">simple type</a>} instance, or
76      * null.
77      *
78      * @throws IllegalArgumentException if the type argument is not an array
79      * type.
80      */
RawObject(RawType type, Object[] elements)81     public RawObject(RawType type, Object[] elements) {
82         if (type == null || elements == null) {
83             throw new NullPointerException();
84         }
85         this.type = type;
86         this.elements = elements;
87     }
88 
89     /**
90      * Creates a raw object with the given enum value for an enum type.
91      *
92      * @param type the type of this raw object.
93      *
94      * @param enumConstant the String value of this enum constant; must be
95      * one of the Strings returned by {@link RawType#getEnumConstants}.
96      *
97      * @throws IllegalArgumentException if the type argument is not an array
98      * type.
99      */
RawObject(RawType type, String enumConstant)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      */
getType()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      */
getValues()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      */
getElements()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      */
getEnum()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      */
getSuper()160     public RawObject getSuper() {
161         return superObject;
162     }
163 
164     @Override
equals(Object other)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 (enumConstant != null) {
180             if (!enumConstant.equals(o.enumConstant)) {
181                 return false;
182             }
183         } else {
184             if (o.enumConstant != null) {
185                 return false;
186             }
187         }
188         if (values != null) {
189             if (!values.equals(o.values)) {
190                 return false;
191             }
192         } else {
193             if (o.values != null) {
194                 return false;
195             }
196         }
197         if (superObject != null) {
198             if (!superObject.equals(o.superObject)) {
199                 return false;
200             }
201         } else {
202             if (o.superObject != null) {
203                 return false;
204             }
205         }
206         return true;
207     }
208 
209     @Override
hashCode()210     public int hashCode() {
211         return System.identityHashCode(type) +
212                Arrays.deepHashCode(elements) +
213                (enumConstant != null ? enumConstant.hashCode() : 0) +
214                (values != null ? values.hashCode() : 0) +
215                (superObject != null ? superObject.hashCode() : 0);
216     }
217 
218     /**
219      * Returns an XML representation of the raw object.
220      */
221     @Override
toString()222     public String toString() {
223         StringBuilder buf = new StringBuilder(500);
224         formatRawObject(buf, "", null, false);
225         return buf.toString();
226     }
227 
formatRawObject(StringBuilder buf, String indent, String id, boolean isSuper)228     private void formatRawObject(StringBuilder buf,
229                                  String indent,
230                                  String id,
231                                  boolean isSuper) {
232         if (type.isEnum()) {
233             buf.append(indent);
234             buf.append("<Enum");
235             formatId(buf, id);
236             buf.append(" class=\"");
237             buf.append(type.getClassName());
238             buf.append("\" typeId=\"");
239             buf.append(type.getId());
240             buf.append("\">");
241             buf.append(enumConstant);
242             buf.append("</Enum>\n");
243         } else {
244             String indent2 = indent + INDENT;
245             String endTag;
246             buf.append(indent);
247             if (type.isArray()) {
248                 buf.append("<Array");
249                 endTag = "</Array>";
250             } else if (isSuper) {
251                 buf.append("<Super");
252                 endTag = "</Super>";
253             } else {
254                 buf.append("<Object");
255                 endTag = "</Object>";
256             }
257             formatId(buf, id);
258             if (type.isArray()) {
259                 buf.append(" length=\"");
260                 buf.append(elements.length);
261                 buf.append('"');
262             }
263             buf.append(" class=\"");
264             buf.append(type.getClassName());
265             buf.append("\" typeId=\"");
266             buf.append(type.getId());
267             buf.append("\">\n");
268 
269             if (superObject != null) {
270                 superObject.formatRawObject(buf, indent2, null, true);
271             }
272             if (type.isArray()) {
273                 for (int i = 0; i < elements.length; i += 1) {
274                     formatValue(buf, indent2, String.valueOf(i), elements[i]);
275                 }
276             } else {
277                 TreeSet<String> keys = new TreeSet<String>(values.keySet());
278                 for (String name : keys) {
279                     formatValue(buf, indent2, name, values.get(name));
280                 }
281             }
282             buf.append(indent);
283             buf.append(endTag);
284             buf.append("\n");
285         }
286     }
287 
formatValue(StringBuilder buf, String indent, String id, Object val)288     private static void formatValue(StringBuilder buf,
289                                     String indent,
290                                     String id,
291                                     Object val) {
292         if (val == null) {
293             buf.append(indent);
294             buf.append("<Null");
295             formatId(buf, id);
296             buf.append("/>\n");
297         } else if (val instanceof RawObject) {
298             ((RawObject) val).formatRawObject(buf, indent, id, false);
299         } else {
300             buf.append(indent);
301             buf.append("<Value");
302             formatId(buf, id);
303             buf.append(" class=\"");
304             buf.append(val.getClass().getName());
305             buf.append("\">");
306             buf.append(val.toString());
307             buf.append("</Value>\n");
308         }
309     }
310 
formatId(StringBuilder buf, String id)311     private static void formatId(StringBuilder buf, String id) {
312         if (id != null) {
313             if (Character.isDigit(id.charAt(0))) {
314                 buf.append(" index=\"");
315             } else {
316                 buf.append(" field=\"");
317             }
318             buf.append(id);
319             buf.append('"');
320         }
321     }
322 }
323