Imported Upstream version 5.3.21
[platform/upstream/libdb.git] / lang / java / src / com / sleepycat / persist / impl / ObjectArrayFormat.java
1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2002, 2012 Oracle and/or its affiliates.  All rights reserved.
5  *
6  */
7
8 package com.sleepycat.persist.impl;
9
10 import java.lang.reflect.Array;
11 import java.util.Map;
12 import java.util.Set;
13
14 import com.sleepycat.compat.DbCompat;
15 import com.sleepycat.db.DatabaseEntry;
16 import com.sleepycat.persist.model.EntityModel;
17 import com.sleepycat.persist.raw.RawObject;
18 import java.util.IdentityHashMap;
19
20 /**
21  * An array of objects having a specified number of dimensions.  All
22  * multidimensional arrays are handled by this class, since even a primitive
23  * array of more than one dimension is an array of objects, where the component
24  * objects may be primitive arrays.  The {@link PrimitiveArrayFormat} class
25  * handles primitive arrays of one dimension only.
26  *
27  * In this class, and {@link PrimitiveArrayFormat}, we resort to using
28  * reflection to allocate multidimensional arrays.  If there is a need for it,
29  * reflection could be avoided in the future by generating code as new array
30  * formats are encountered.
31  *
32  * @author Mark Hayes
33  */
34 public class ObjectArrayFormat extends Format {
35
36     private static final long serialVersionUID = 4317004346690441892L;
37
38     private Format componentFormat;
39     private int nDimensions;
40     private transient Format useComponentFormat;
41
42     ObjectArrayFormat(Catalog catalog, Class type) {
43         super(catalog, type);
44         String name = getClassName();
45         for (nDimensions = 0;
46              name.charAt(nDimensions) == '[';
47              nDimensions += 1) {
48         }
49     }
50
51     @Override
52     public boolean isArray() {
53         return true;
54     }
55
56     @Override
57     public int getDimensions() {
58         return nDimensions;
59     }
60
61     @Override
62     public Format getComponentType() {
63         return (useComponentFormat != null) ?
64             useComponentFormat : componentFormat;
65     }
66
67     @Override
68     void collectRelatedFormats(Catalog catalog,
69                                Map<String, Format> newFormats) {
70         Class cls = getType().getComponentType();
71         catalog.createFormat(cls, newFormats);
72     }
73
74     @Override
75     void initialize(Catalog catalog, EntityModel model, int initVersion) {
76         /* Set the component format for a new (never initialized) format. */
77         if (componentFormat == null) {
78             Class cls = getType().getComponentType();
79             componentFormat = catalog.getFormat(cls.getName());
80         }
81         useComponentFormat = componentFormat.getLatestVersion();
82     }
83
84     @Override
85     boolean isAssignableTo(Format format) {
86         if (super.isAssignableTo(format)) {
87             return true;
88         }
89         if (format instanceof ObjectArrayFormat) {
90             ObjectArrayFormat other = (ObjectArrayFormat) format;
91             if (useComponentFormat.isAssignableTo(other.useComponentFormat)) {
92                 return true;
93             }
94         }
95         return false;
96     }
97
98     @Override
99     Object newArray(int len) {
100         return Array.newInstance(getType(), len);
101     }
102
103     @Override
104     public Object newInstance(EntityInput input, boolean rawAccess) {
105         int len = input.readArrayLength();
106         if (rawAccess) {
107             return new RawObject(this, new Object[len]);
108         } else {
109             return useComponentFormat.newArray(len);
110         }
111     }
112
113     @Override
114     public Object readObject(Object o, EntityInput input, boolean rawAccess)
115         throws RefreshException {
116         
117         Object[] a;
118         if (rawAccess) {
119             a = ((RawObject) o).getElements();
120         } else {
121             a = (Object[]) o;
122         }
123         if (useComponentFormat.getId() == Format.ID_STRING) {
124             for (int i = 0; i < a.length; i += 1) {
125                 a[i] = input.readStringObject();
126             }
127         } else {
128             for (int i = 0; i < a.length; i += 1) {
129                 a[i] = input.readObject();
130             }
131         }
132         return o;
133     }
134
135     @Override
136     void writeObject(Object o, EntityOutput output, boolean rawAccess)
137         throws RefreshException {
138
139         Object[] a;
140         if (rawAccess) {
141             a = ((RawObject) o).getElements();
142         } else {
143             a = (Object[]) o;
144         }
145         output.writeArrayLength(a.length);
146         if (useComponentFormat.getId() == Format.ID_STRING) {
147             for (int i = 0; i < a.length; i += 1) {
148                 output.writeString((String)a[i]);
149             }
150         } else {
151             for (int i = 0; i < a.length; i += 1) {
152                 output.writeObject(a[i], useComponentFormat);
153             }
154         }
155     }
156
157     @Override
158     Object convertRawObject(Catalog catalog,
159                             boolean rawAccess,
160                             RawObject rawObject,
161                             IdentityHashMap converted)
162         throws RefreshException {
163
164         RawArrayInput input = new RawArrayInput
165             (catalog, rawAccess, converted, rawObject, useComponentFormat);
166         Object a = newInstance(input, rawAccess);
167         converted.put(rawObject, a);
168         return readObject(a, input, rawAccess);
169     }
170
171     @Override
172     void skipContents(RecordInput input)
173         throws RefreshException {
174
175         int len = input.readPackedInt();
176         for (int i = 0; i < len; i += 1) {
177             input.skipField(useComponentFormat);
178         }
179     }
180
181     @Override
182     void copySecMultiKey(RecordInput input, Format keyFormat, Set results)
183         throws RefreshException {
184
185         int len = input.readPackedInt();
186         for (int i = 0; i < len; i += 1) {
187             KeyLocation loc = input.getKeyLocation(useComponentFormat);
188             if (loc == null) {
189                 throw new IllegalArgumentException
190                     ("Secondary key values in array may not be null");
191             }
192             if (loc.format != useComponentFormat) {
193                 throw DbCompat.unexpectedState
194                     (useComponentFormat.getClassName());
195             }
196             int off1 = loc.input.getBufferOffset();
197             useComponentFormat.skipContents(loc.input);
198             int off2 = loc.input.getBufferOffset();
199             DatabaseEntry entry = new DatabaseEntry
200                 (loc.input.getBufferBytes(), off1, off2 - off1);
201             results.add(entry);
202         }
203     }
204
205     @Override
206     boolean evolve(Format newFormat, Evolver evolver) {
207
208         /*
209          * When the class name of the component changes, we need a new format
210          * that references it.  Otherwise, don't propogate changes from
211          * components upward to their arrays.
212          */
213         Format latest = componentFormat.getLatestVersion();
214         if (latest != componentFormat &&
215             !latest.getClassName().equals(componentFormat.getClassName())) {
216             evolver.useEvolvedFormat(this, newFormat, newFormat);
217         } else {
218             evolver.useOldFormat(this, newFormat);
219         }
220         return true;
221     }
222 }