blob: 27bbbe7c17f138f571f0af3a01d235cfc17dbd8d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ignite.internal.binary;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.Map;
import org.apache.ignite.IgniteException;
import org.apache.ignite.binary.BinaryObject;
import org.apache.ignite.binary.BinaryObjectBuilder;
import org.apache.ignite.binary.BinaryObjectException;
import org.apache.ignite.binary.BinaryType;
import org.apache.ignite.internal.binary.builder.BinaryObjectBuilderImpl;
import org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerInaccessibleClassException;
import org.apache.ignite.internal.util.typedef.X;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.SB;
import org.apache.ignite.lang.IgniteUuid;
import org.apache.ignite.thread.IgniteThread;
import org.jetbrains.annotations.Nullable;
/**
* Internal binary object interface.
*/
public abstract class BinaryObjectExImpl implements BinaryObjectEx {
/**
* @return Length.
*/
public abstract int length();
/**
* @return Object start.
*/
public abstract int start();
/**
* @return {@code True} if object is array based.
*/
public abstract boolean hasArray();
/**
* @return Object array if object is array based, otherwise {@code null}.
*/
public abstract byte[] array();
/**
* @return Object offheap address is object is offheap based, otherwise 0.
*/
public abstract long offheapAddress();
/**
* Gets field value.
*
* @param fieldId Field ID.
* @return Field value.
* @throws org.apache.ignite.binary.BinaryObjectException In case of any other error.
*/
@Nullable public abstract <F> F field(int fieldId) throws BinaryObjectException;
/** {@inheritDoc} */
@Override public int enumOrdinal() throws BinaryObjectException {
throw new BinaryObjectException("Object is not enum.");
}
/** {@inheritDoc} */
@Override public String enumName() throws BinaryObjectException {
throw new BinaryObjectException("Object is not enum.");
}
/**
* Get offset of data begin.
*
* @return Field value.
*/
public abstract int dataStartOffset();
/**
* Get offset of the footer begin.
*
* @return Field value.
*/
public abstract int footerStartOffset();
/**
* Get field by offset.
*
* @param order Field offset.
* @return Field value.
*/
@Nullable public abstract <F> F fieldByOrder(int order);
/**
* Create field comparator.
*
* @return Comparator.
*/
public abstract BinarySerializedFieldComparator createFieldComparator();
/**
* Writes field value defined by the given field offset to the given byte buffer.
*
* @param fieldOffset Field offset.
* @return Boolean flag indicating whether the field was successfully written to the buffer, {@code false}
* if there is no enough space for the field in the buffer.
*/
protected abstract boolean writeFieldByOrder(int fieldOffset, ByteBuffer buf);
/**
* @param ctx Reader context.
* @param fieldName Field name.
* @return Field value.
*/
@Nullable protected abstract <F> F field(BinaryReaderHandles ctx, String fieldName);
/**
* @return {@code True} if object has schema.
*/
public abstract boolean hasSchema();
/**
* Get schema ID.
*
* @return Schema ID.
*/
public abstract int schemaId();
/**
* Create schema for object.
*
* @return Schema.
*/
public abstract BinarySchema createSchema();
/**
* Get binary context.
*
* @return Binary context.
*/
public abstract BinaryContext context();
/** {@inheritDoc} */
@Override public BinaryObjectBuilder toBuilder() throws BinaryObjectException {
return BinaryObjectBuilderImpl.wrap(this);
}
/** {@inheritDoc} */
@Override public BinaryObject clone() throws CloneNotSupportedException {
return (BinaryObject)super.clone();
}
/** {@inheritDoc} */
@Override public boolean equals(Object other) {
if (other == this)
return true;
if (!(other instanceof BinaryObject))
return false;
BinaryIdentityResolver identity = context().identity(typeId());
return identity.equals(this, (BinaryObject)other);
}
/** {@inheritDoc} */
@Override public String toString() {
try {
BinaryReaderHandles ctx = new BinaryReaderHandles();
ctx.put(start(), this);
return toString(ctx, new IdentityHashMap<BinaryObject, Integer>());
}
catch (BinaryObjectException e) {
throw new IgniteException("Failed to create string representation of binary object.", e);
}
}
/**
* @param ctx Reader context.
* @param handles Handles for already traversed objects.
* @return String representation.
*/
private String toString(BinaryReaderHandles ctx, IdentityHashMap<BinaryObject, Integer> handles) {
int idHash = System.identityHashCode(this);
int hash = hashCode();
BinaryType meta;
IgniteThread.onForbidBinaryMetadataRequestSectionEntered();
try {
meta = rawType();
}
catch (BinaryObjectException ignore) {
meta = null;
}
finally {
IgniteThread.onForbidBinaryMetadataRequestSectionLeft();
}
if (meta == null || !S.includeSensitive())
return S.toString(S.includeSensitive() ? BinaryObject.class.getSimpleName() : "BinaryObject",
"idHash", idHash, false,
"hash", hash, false,
"typeId", typeId(), true);
handles.put(this, idHash);
SB buf = new SB(meta.typeName());
if (meta.fieldNames() != null) {
buf.a(" [idHash=").a(idHash).a(", hash=").a(hash);
for (String name : meta.fieldNames()) {
Object val = fieldForToString(ctx, name);
buf.a(", ").a(name).a('=');
appendValue(val, buf, ctx, handles);
}
buf.a(']');
}
return buf.toString();
}
/** */
private Object fieldForToString(BinaryReaderHandles ctx, String name) {
try {
return field(ctx, name);
}
catch (Exception e) {
OptimizedMarshallerInaccessibleClassException e1 =
X.cause(e, OptimizedMarshallerInaccessibleClassException.class);
String msg = "Failed to create a string representation";
return e1 != null
? "(" + msg + ": class not found " + e1.inaccessibleClass() + ")"
: "(" + msg + ")";
}
}
/**
* @param val Value to append.
* @param buf Buffer to append to.
* @param ctx Reader context.
* @param handles Handles for already traversed objects.
*/
private void appendValue(Object val, SB buf, BinaryReaderHandles ctx,
IdentityHashMap<BinaryObject, Integer> handles) {
if (val instanceof byte[])
buf.a(Arrays.toString((byte[])val));
else if (val instanceof short[])
buf.a(Arrays.toString((short[])val));
else if (val instanceof int[])
buf.a(Arrays.toString((int[])val));
else if (val instanceof long[])
buf.a(Arrays.toString((long[])val));
else if (val instanceof float[])
buf.a(Arrays.toString((float[])val));
else if (val instanceof double[])
buf.a(Arrays.toString((double[])val));
else if (val instanceof char[])
buf.a(Arrays.toString((char[])val));
else if (val instanceof boolean[])
buf.a(Arrays.toString((boolean[])val));
else if (val instanceof BigDecimal[])
buf.a(Arrays.toString((BigDecimal[])val));
else if (val instanceof IgniteUuid)
buf.a(val);
else if (val instanceof BinaryObjectExImpl) {
BinaryObjectExImpl po = (BinaryObjectExImpl)val;
Integer idHash0 = handles.get(val);
if (idHash0 != null) { // Circular reference.
BinaryType meta0 = po.rawType();
assert meta0 != null;
buf.a(meta0.typeName()).a(" [hash=").a(idHash0).a(", ...]");
}
else
buf.a(po.toString(ctx, handles));
}
else if (val instanceof Object[]) {
Object[] arr = (Object[])val;
buf.a('[');
for (int i = 0; i < arr.length; i++) {
Object o = arr[i];
appendValue(o, buf, ctx, handles);
if (i < arr.length - 1)
buf.a(", ");
}
}
else if (val instanceof Iterable) {
Iterable<Object> col = (Iterable<Object>)val;
buf.a(col.getClass().getSimpleName()).a(" {");
Iterator it = col.iterator();
while (it.hasNext()) {
Object o = it.next();
appendValue(o, buf, ctx, handles);
if (it.hasNext())
buf.a(", ");
}
buf.a('}');
}
else if (val instanceof Map) {
Map<Object, Object> map = (Map<Object, Object>)val;
buf.a(map.getClass().getSimpleName()).a(" {");
Iterator<Map.Entry<Object, Object>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Object, Object> e = it.next();
appendValue(e.getKey(), buf, ctx, handles);
buf.a('=');
appendValue(e.getValue(), buf, ctx, handles);
if (it.hasNext())
buf.a(", ");
}
buf.a('}');
}
else
buf.a(val);
}
/**
* Check if object graph has circular references.
*
* @return {@code true} if object has circular references.
*/
public boolean hasCircularReferences() {
try {
BinaryReaderHandles ctx = new BinaryReaderHandles();
ctx.put(start(), this);
return hasCircularReferences(ctx, new IdentityHashMap<BinaryObject, Integer>());
}
catch (BinaryObjectException e) {
throw new IgniteException("Failed to check binary object for circular references", e);
}
}
/**
* @param ctx Reader context.
* @param handles Handles for already traversed objects.
* @return {@code true} if has circular reference.
*/
private boolean hasCircularReferences(BinaryReaderHandles ctx, IdentityHashMap<BinaryObject, Integer> handles) {
BinaryType meta;
try {
meta = rawType();
}
catch (BinaryObjectException ignore) {
meta = null;
}
if (meta == null)
return false;
int idHash = System.identityHashCode(this);
handles.put(this, idHash);
if (meta.fieldNames() != null) {
ctx.put(start(), this);
for (String name : meta.fieldNames()) {
Object val = field(ctx, name);
if (val instanceof BinaryObjectExImpl) {
BinaryObjectExImpl po = (BinaryObjectExImpl)val;
Integer idHash0 = handles.get(val);
// Check for circular reference.
if (idHash0 != null || po.hasCircularReferences(ctx, handles))
return true;
}
}
}
return false;
}
}