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