1 /*-
2  * Copyright (c) 2002, 2020 Oracle and/or its affiliates.  All rights reserved.
3  *
4  * See the file LICENSE for license information.
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 <a href="../model/Entity.html#simpleTypes">simple
49      * type</a> instance, or null.
50      *
51      * @param superObject the instance of the superclass, or null if the
52      * superclass is {@code Object}.
53      *
54      * @throws IllegalArgumentException if the type argument is an array type.
55      */
RawObject(RawType type, Map<String, Object> values, RawObject superObject)56     public RawObject(RawType type,
57                      Map<String, Object> values,
58                      RawObject superObject) {
59         if (type == null || values == null) {
60             throw new NullPointerException();
61         }
62         this.type = type;
63         this.values = values;
64         this.superObject = superObject;
65     }
66 
67     /**
68      * Creates a raw object with the given array elements for an array type.
69      *
70      * @param type the type of this raw object.
71      *
72      * @param elements an array of elements.  Each element in the array is a
73      * {@link RawObject}, a <a href="../model/Entity.html#simpleTypes">simple
74      * type</a> instance, or null.
75      *
76      * @throws IllegalArgumentException if the type argument is not an array
77      * type.
78      */
RawObject(RawType type, Object[] elements)79     public RawObject(RawType type, Object[] elements) {
80         if (type == null || elements == null) {
81             throw new NullPointerException();
82         }
83         this.type = type;
84         this.elements = elements;
85     }
86 
87     /**
88      * Creates a raw object with the given enum value for an enum type.
89      *
90      * @param type the type of this raw object.
91      *
92      * @param enumConstant the String value of this enum constant; must be
93      * one of the Strings returned by {@link RawType#getEnumConstants}.
94      *
95      * @throws IllegalArgumentException if the type argument is not an array
96      * type.
97      */
RawObject(RawType type, String enumConstant)98     public RawObject(RawType type, String enumConstant) {
99         if (type == null || enumConstant == null) {
100             throw new NullPointerException();
101         }
102         this.type = type;
103         this.enumConstant = enumConstant;
104     }
105 
106     /**
107      * Returns the raw type information for this raw object.
108      *
109      * <p>Note that if this object is unevolved, the returned type may be
110      * different from the current type returned by {@link
111      * EntityModel#getRawType EntityModel.getRawType} for the same class name.
112      * This can only occur in a {@link Conversion#convert
113      * Conversion.convert}.</p>
114      *
115      * @return the RawType.
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 <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      * @return the map of field name to value, or null.
134      */
getValues()135     public Map<String, Object> getValues() {
136         return values;
137     }
138 
139     /**
140      * Returns the array of elements for an array type, or null for a complex
141      * type or an enum type.  Each element in the array is a {@link RawObject},
142      * a <a href="../model/Entity.html#simpleTypes">simple type</a> instance,
143      * or null.
144      *
145      * @return the array of elements, or null.
146      */
getElements()147     public Object[] getElements() {
148         return elements;
149     }
150 
151     /**
152      * Returns the enum constant String for an enum type, or null for a complex
153      * type or an array type.  The String returned will be one of the Strings
154      * returned by {@link RawType#getEnumConstants}.
155      *
156      * @return the enum constant String, or null.
157      */
getEnum()158     public String getEnum() {
159         return enumConstant;
160     }
161 
162     /**
163      * Returns the instance of the superclass, or null if the superclass is
164      * {@code Object} or {@code Enum}.
165      *
166      * @return the instance of the superclass, or null.
167      */
getSuper()168     public RawObject getSuper() {
169         return superObject;
170     }
171 
172     @Override
equals(Object other)173     public boolean equals(Object other) {
174         if (other == this) {
175             return true;
176         }
177         if (!(other instanceof RawObject)) {
178             return false;
179         }
180         RawObject o = (RawObject) other;
181         if (type != o.type) {
182             return false;
183         }
184         if (!Arrays.deepEquals(elements, o.elements)) {
185             return false;
186         }
187         if (enumConstant != null) {
188             if (!enumConstant.equals(o.enumConstant)) {
189                 return false;
190             }
191         } else {
192             if (o.enumConstant != null) {
193                 return false;
194             }
195         }
196         if (values != null) {
197             if (!values.equals(o.values)) {
198                 return false;
199             }
200         } else {
201             if (o.values != null) {
202                 return false;
203             }
204         }
205         if (superObject != null) {
206             if (!superObject.equals(o.superObject)) {
207                 return false;
208             }
209         } else {
210             if (o.superObject != null) {
211                 return false;
212             }
213         }
214         return true;
215     }
216 
217     @Override
hashCode()218     public int hashCode() {
219         return System.identityHashCode(type) +
220                Arrays.deepHashCode(elements) +
221                (enumConstant != null ? enumConstant.hashCode() : 0) +
222                (values != null ? values.hashCode() : 0) +
223                (superObject != null ? superObject.hashCode() : 0);
224     }
225 
226     /**
227      * Returns an XML representation of the raw object.
228      */
229     @Override
toString()230     public String toString() {
231         StringBuilder buf = new StringBuilder(500);
232         formatRawObject(buf, "", null, false);
233         return buf.toString();
234     }
235 
formatRawObject(StringBuilder buf, String indent, String id, boolean isSuper)236     private void formatRawObject(StringBuilder buf,
237                                  String indent,
238                                  String id,
239                                  boolean isSuper) {
240         if (type.isEnum()) {
241             buf.append(indent);
242             buf.append("<Enum");
243             formatId(buf, id);
244             buf.append(" class=\"");
245             buf.append(type.getClassName());
246             buf.append("\" typeId=\"");
247             buf.append(type.getId());
248             buf.append("\">");
249             buf.append(enumConstant);
250             buf.append("</Enum>\n");
251         } else {
252             String indent2 = indent + INDENT;
253             String endTag;
254             buf.append(indent);
255             if (type.isArray()) {
256                 buf.append("<Array");
257                 endTag = "</Array>";
258             } else if (isSuper) {
259                 buf.append("<Super");
260                 endTag = "</Super>";
261             } else {
262                 buf.append("<Object");
263                 endTag = "</Object>";
264             }
265             formatId(buf, id);
266             if (type.isArray()) {
267                 buf.append(" length=\"");
268                 buf.append(elements.length);
269                 buf.append('"');
270             }
271             buf.append(" class=\"");
272             buf.append(type.getClassName());
273             buf.append("\" typeId=\"");
274             buf.append(type.getId());
275             buf.append("\">\n");
276 
277             if (superObject != null) {
278                 superObject.formatRawObject(buf, indent2, null, true);
279             }
280             if (type.isArray()) {
281                 for (int i = 0; i < elements.length; i += 1) {
282                     formatValue(buf, indent2, String.valueOf(i), elements[i]);
283                 }
284             } else {
285                 TreeSet<String> keys = new TreeSet<String>(values.keySet());
286                 for (String name : keys) {
287                     formatValue(buf, indent2, name, values.get(name));
288                 }
289             }
290             buf.append(indent);
291             buf.append(endTag);
292             buf.append("\n");
293         }
294     }
295 
formatValue(StringBuilder buf, String indent, String id, Object val)296     private static void formatValue(StringBuilder buf,
297                                     String indent,
298                                     String id,
299                                     Object val) {
300         if (val == null) {
301             buf.append(indent);
302             buf.append("<Null");
303             formatId(buf, id);
304             buf.append("/>\n");
305         } else if (val instanceof RawObject) {
306             ((RawObject) val).formatRawObject(buf, indent, id, false);
307         } else {
308             buf.append(indent);
309             buf.append("<Value");
310             formatId(buf, id);
311             buf.append(" class=\"");
312             buf.append(val.getClass().getName());
313             buf.append("\">");
314             buf.append(val.toString());
315             buf.append("</Value>\n");
316         }
317     }
318 
formatId(StringBuilder buf, String id)319     private static void formatId(StringBuilder buf, String id) {
320         if (id != null) {
321             if (Character.isDigit(id.charAt(0))) {
322                 buf.append(" index=\"");
323             } else {
324                 buf.append(" field=\"");
325             }
326             buf.append(id);
327             buf.append('"');
328         }
329     }
330 }
331