summaryrefslogtreecommitdiff
path: root/lang/java/src/com/sleepycat/persist/raw/RawObject.java
blob: 0cdc953c44c6f178f8583f1e8ab1e7486528c007 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
/*-
 * See the file LICENSE for redistribution information.
 *
 * Copyright (c) 2002, 2015 Oracle and/or its affiliates.  All rights reserved.
 *
 */

package com.sleepycat.persist.raw;

import java.util.Arrays;
import java.util.Map;
import java.util.TreeSet;

import com.sleepycat.persist.evolve.Conversion;
import com.sleepycat.persist.model.EntityModel;

/**
 * A raw instance that can be used with a {@link RawStore} or {@link
 * Conversion}.  A <code>RawObject</code> is used to represent instances of
 * complex types (persistent classes with fields), arrays, and enum values.  It
 * is not used to represent non-enum simple types, which are represented as
 * simple objects.  This includes primitives, which are represented as
 * instances of their wrapper class.
 *
 * <p>{@code RawObject} objects are thread-safe.  Multiple threads may safely
 * call the methods of a shared {@code RawObject} object.</p>
 *
 * @author Mark Hayes
 */
public class RawObject {

    private static final String INDENT = "  ";

    private RawType type;
    private Map<String, Object> values;
    private Object[] elements;
    private String enumConstant;
    private RawObject superObject;

    /**
     * Creates a raw object with a given set of field values for a complex
     * type.
     *
     * @param type the type of this raw object.
     *
     * @param values a map of field name to value for each declared field in
     * the class, or null to create an empty map.  Each value in the map is a
     * {@link RawObject}, a {@link <a
     * href="../model/Entity.html#simpleTypes">simple type</a>} instance, or
     * null.
     *
     * @param superObject the instance of the superclass, or null if the
     * superclass is {@code Object}.
     *
     * @throws IllegalArgumentException if the type argument is an array type.
     */
    public RawObject(RawType type,
                     Map<String, Object> values,
                     RawObject superObject) {
        if (type == null || values == null) {
            throw new NullPointerException();
        }
        this.type = type;
        this.values = values;
        this.superObject = superObject;
    }

    /**
     * Creates a raw object with the given array elements for an array type.
     *
     * @param type the type of this raw object.
     *
     * @param elements an array of elements.  Each element in the array is a
     * {@link RawObject}, a {@link <a
     * href="../model/Entity.html#simpleTypes">simple type</a>} instance, or
     * null.
     *
     * @throws IllegalArgumentException if the type argument is not an array
     * type.
     */
    public RawObject(RawType type, Object[] elements) {
        if (type == null || elements == null) {
            throw new NullPointerException();
        }
        this.type = type;
        this.elements = elements;
    }

    /**
     * Creates a raw object with the given enum value for an enum type.
     *
     * @param type the type of this raw object.
     *
     * @param enumConstant the String value of this enum constant; must be
     * one of the Strings returned by {@link RawType#getEnumConstants}.
     *
     * @throws IllegalArgumentException if the type argument is not an array
     * type.
     */
    public RawObject(RawType type, String enumConstant) {
        if (type == null || enumConstant == null) {
            throw new NullPointerException();
        }
        this.type = type;
        this.enumConstant = enumConstant;
    }

    /**
     * Returns the raw type information for this raw object.
     *
     * <p>Note that if this object is unevolved, the returned type may be
     * different from the current type returned by {@link
     * EntityModel#getRawType EntityModel.getRawType} for the same class name.
     * This can only occur in a {@link Conversion#convert
     * Conversion.convert}.</p>
     */
    public RawType getType() {
        return type;
    }

    /**
     * Returns a map of field name to value for a complex type, or null for an
     * array type or an enum type.  The map contains a String key for each
     * declared field in the class.  Each value in the map is a {@link
     * RawObject}, a {@link <a href="../model/Entity.html#simpleTypes">simple
     * type</a>} instance, or null.
     *
     * <p>There will be an entry in the map for every field declared in this
     * type, as determined by {@link RawType#getFields} for the type returned
     * by {@link #getType}.  Values in the map may be null for fields with
     * non-primitive types.</p>
     */
    public Map<String, Object> getValues() {
        return values;
    }

    /**
     * Returns the array of elements for an array type, or null for a complex
     * type or an enum type.  Each element in the array is a {@link RawObject},
     * a {@link <a href="../model/Entity.html#simpleTypes">simple type</a>}
     * instance, or null.
     */
    public Object[] getElements() {
        return elements;
    }

    /**
     * Returns the enum constant String for an enum type, or null for a complex
     * type or an array type.  The String returned will be one of the Strings
     * returned by {@link RawType#getEnumConstants}.
     */
    public String getEnum() {
        return enumConstant;
    }

    /**
     * Returns the instance of the superclass, or null if the superclass is
     * {@code Object} or {@code Enum}.
     */
    public RawObject getSuper() {
        return superObject;
    }

    @Override
    public boolean equals(Object other) {
        if (other == this) {
            return true;
        }
        if (!(other instanceof RawObject)) {
            return false;
        }
        RawObject o = (RawObject) other;
        if (type != o.type) {
            return false;
        }
        if (!Arrays.deepEquals(elements, o.elements)) {
            return false;
        }
        if (enumConstant != null) {
            if (!enumConstant.equals(o.enumConstant)) {
                return false;
            }
        } else {
            if (o.enumConstant != null) {
                return false;
            }
        }
        if (values != null) {
            if (!values.equals(o.values)) {
                return false;
            }
        } else {
            if (o.values != null) {
                return false;
            }
        }
        if (superObject != null) {
            if (!superObject.equals(o.superObject)) {
                return false;
            }
        } else {
            if (o.superObject != null) {
                return false;
            }
        }
        return true;
    }

    @Override
    public int hashCode() {
        return System.identityHashCode(type) +
               Arrays.deepHashCode(elements) +
               (enumConstant != null ? enumConstant.hashCode() : 0) +
               (values != null ? values.hashCode() : 0) +
               (superObject != null ? superObject.hashCode() : 0);
    }

    /**
     * Returns an XML representation of the raw object.
     */
    @Override
    public String toString() {
        StringBuilder buf = new StringBuilder(500);
        formatRawObject(buf, "", null, false);
        return buf.toString();
    }

    private void formatRawObject(StringBuilder buf,
                                 String indent,
                                 String id,
                                 boolean isSuper) {
        if (type.isEnum()) {
            buf.append(indent);
            buf.append("<Enum");
            formatId(buf, id);
            buf.append(" class=\"");
            buf.append(type.getClassName());
            buf.append("\" typeId=\"");
            buf.append(type.getId());
            buf.append("\">");
            buf.append(enumConstant);
            buf.append("</Enum>\n");
        } else {
            String indent2 = indent + INDENT;
            String endTag;
            buf.append(indent);
            if (type.isArray()) {
                buf.append("<Array");
                endTag = "</Array>";
            } else if (isSuper) {
                buf.append("<Super");
                endTag = "</Super>";
            } else {
                buf.append("<Object");
                endTag = "</Object>";
            }
            formatId(buf, id);
            if (type.isArray()) {
                buf.append(" length=\"");
                buf.append(elements.length);
                buf.append('"');
            }
            buf.append(" class=\"");
            buf.append(type.getClassName());
            buf.append("\" typeId=\"");
            buf.append(type.getId());
            buf.append("\">\n");

            if (superObject != null) {
                superObject.formatRawObject(buf, indent2, null, true);
            }
            if (type.isArray()) {
                for (int i = 0; i < elements.length; i += 1) {
                    formatValue(buf, indent2, String.valueOf(i), elements[i]);
                }
            } else {
                TreeSet<String> keys = new TreeSet<String>(values.keySet());
                for (String name : keys) {
                    formatValue(buf, indent2, name, values.get(name));
                }
            }
            buf.append(indent);
            buf.append(endTag);
            buf.append("\n");
        }
    }

    private static void formatValue(StringBuilder buf,
                                    String indent,
                                    String id,
                                    Object val) {
        if (val == null) {
            buf.append(indent);
            buf.append("<Null");
            formatId(buf, id);
            buf.append("/>\n");
        } else if (val instanceof RawObject) {
            ((RawObject) val).formatRawObject(buf, indent, id, false);
        } else {
            buf.append(indent);
            buf.append("<Value");
            formatId(buf, id);
            buf.append(" class=\"");
            buf.append(val.getClass().getName());
            buf.append("\">");
            buf.append(val.toString());
            buf.append("</Value>\n");
        }
    }

    private static void formatId(StringBuilder buf, String id) {
        if (id != null) {
            if (Character.isDigit(id.charAt(0))) {
                buf.append(" index=\"");
            } else {
                buf.append(" field=\"");
            }
            buf.append(id);
            buf.append('"');
        }
    }
}