/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2002, 2010 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 RawObject
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.
*
*
{@code RawObject} objects are thread-safe. Multiple threads may safely
* call the methods of a shared {@code RawObject} object.
*
* @author Mark Hayes
*/
public class RawObject {
private static final String INDENT = " ";
private RawType type;
private Map 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 simple type} 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 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 simple type} 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.
*
* 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}.
*/
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 simple
* type} instance, or null.
*
* 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.
*/
public Map 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 simple type}
* 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("");
buf.append(enumConstant);
buf.append("\n");
} else {
String indent2 = indent + INDENT;
String endTag;
buf.append(indent);
if (type.isArray()) {
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 keys = new TreeSet(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("\n");
} else if (val instanceof RawObject) {
((RawObject) val).formatRawObject(buf, indent, id, false);
} else {
buf.append(indent);
buf.append("");
buf.append(val.toString());
buf.append("\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('"');
}
}
}