/*- * See the file LICENSE for redistribution information. * * Copyright (c) 2002, 2015 Oracle and/or its affiliates. All rights reserved. * */ package com.sleepycat.persist.impl; import java.lang.reflect.Array; import java.util.IdentityHashMap; import java.util.Map; import java.util.Set; import com.sleepycat.compat.DbCompat; import com.sleepycat.db.DatabaseEntry; import com.sleepycat.persist.model.EntityModel; import com.sleepycat.persist.raw.RawObject; /** * An array of objects having a specified number of dimensions. All * multidimensional arrays are handled by this class, since even a primitive * array of more than one dimension is an array of objects, where the component * objects may be primitive arrays. The {@link PrimitiveArrayFormat} class * handles primitive arrays of one dimension only. * * In this class, and {@link PrimitiveArrayFormat}, we resort to using * reflection to allocate multidimensional arrays. If there is a need for it, * reflection could be avoided in the future by generating code as new array * formats are encountered. * * @author Mark Hayes */ public class ObjectArrayFormat extends Format { private static final long serialVersionUID = 4317004346690441892L; private Format componentFormat; private int nDimensions; private transient Format useComponentFormat; ObjectArrayFormat(Catalog catalog, Class type) { super(catalog, type); String name = getClassName(); for (nDimensions = 0; name.charAt(nDimensions) == '['; nDimensions += 1) { } } @Override public boolean isArray() { return true; } @Override public int getDimensions() { return nDimensions; } @Override public Format getComponentType() { return (useComponentFormat != null) ? useComponentFormat : componentFormat; } @Override void collectRelatedFormats(Catalog catalog, Map<String, Format> newFormats) { Class cls = getType().getComponentType(); catalog.createFormat(cls, newFormats); } @Override void initialize(Catalog catalog, EntityModel model, int initVersion) { /* Set the component format for a new (never initialized) format. */ if (componentFormat == null) { Class cls = getType().getComponentType(); componentFormat = catalog.getFormat(cls.getName()); } useComponentFormat = componentFormat.getLatestVersion(); } @Override boolean isAssignableTo(Format format) { if (super.isAssignableTo(format)) { return true; } if (format instanceof ObjectArrayFormat) { ObjectArrayFormat other = (ObjectArrayFormat) format; if (useComponentFormat.isAssignableTo(other.useComponentFormat)) { return true; } } return false; } @Override Object newArray(int len) { return Array.newInstance(getType(), len); } @Override public Object newInstance(EntityInput input, boolean rawAccess) { int len = input.readArrayLength(); if (rawAccess) { return new RawObject(this, new Object[len]); } else { return useComponentFormat.newArray(len); } } @Override public Object readObject(Object o, EntityInput input, boolean rawAccess) throws RefreshException { Object[] a; if (rawAccess) { a = ((RawObject) o).getElements(); } else { a = (Object[]) o; } if (useComponentFormat.getId() == Format.ID_STRING) { for (int i = 0; i < a.length; i += 1) { a[i] = input.readStringObject(); } } else { for (int i = 0; i < a.length; i += 1) { a[i] = input.readObject(); } } return o; } @Override void writeObject(Object o, EntityOutput output, boolean rawAccess) throws RefreshException { Object[] a; if (rawAccess) { a = ((RawObject) o).getElements(); } else { a = (Object[]) o; } output.writeArrayLength(a.length); if (useComponentFormat.getId() == Format.ID_STRING) { for (int i = 0; i < a.length; i += 1) { output.writeString((String)a[i]); } } else { for (int i = 0; i < a.length; i += 1) { output.writeObject(a[i], useComponentFormat); } } } @Override Object convertRawObject(Catalog catalog, boolean rawAccess, RawObject rawObject, IdentityHashMap converted) throws RefreshException { RawArrayInput input = new RawArrayInput (catalog, rawAccess, converted, rawObject, useComponentFormat); Object a = newInstance(input, rawAccess); converted.put(rawObject, a); return readObject(a, input, rawAccess); } @Override void skipContents(RecordInput input) throws RefreshException { int len = input.readPackedInt(); for (int i = 0; i < len; i += 1) { input.skipField(useComponentFormat); } } @Override void copySecMultiKey(RecordInput input, Format keyFormat, Set results) throws RefreshException { int len = input.readPackedInt(); for (int i = 0; i < len; i += 1) { KeyLocation loc = input.getKeyLocation(useComponentFormat); if (loc == null) { throw new IllegalArgumentException ("Secondary key values in array may not be null"); } if (loc.format != useComponentFormat) { throw DbCompat.unexpectedState (useComponentFormat.getClassName()); } int off1 = loc.input.getBufferOffset(); useComponentFormat.skipContents(loc.input); int off2 = loc.input.getBufferOffset(); DatabaseEntry entry = new DatabaseEntry (loc.input.getBufferBytes(), off1, off2 - off1); results.add(entry); } } @Override boolean evolve(Format newFormat, Evolver evolver) { /* * When the class name of the component changes, we need a new format * that references it. Otherwise, don't propogate changes from * components upward to their arrays. */ Format latest = componentFormat.getLatestVersion(); if (latest != componentFormat && !latest.getClassName().equals(componentFormat.getClassName())) { evolver.useEvolvedFormat(this, newFormat, newFormat); } else { evolver.useOldFormat(this, newFormat); } return true; } }