blob: 553d8e5b79bd96c878f3a72d66b23a083182bb82 [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.io.ByteArrayInputStream;
import java.io.Externalizable;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListSet;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteSystemProperties;
import org.apache.ignite.binary.BinaryCollectionFactory;
import org.apache.ignite.binary.BinaryInvalidTypeException;
import org.apache.ignite.binary.BinaryMapFactory;
import org.apache.ignite.binary.BinaryObject;
import org.apache.ignite.binary.BinaryObjectException;
import org.apache.ignite.binary.BinaryRawReader;
import org.apache.ignite.binary.BinaryRawWriter;
import org.apache.ignite.binary.BinaryType;
import org.apache.ignite.binary.Binarylizable;
import org.apache.ignite.internal.binary.builder.BinaryLazyValue;
import org.apache.ignite.internal.binary.streams.BinaryInputStream;
import org.apache.ignite.internal.processors.cache.CacheObjectByteArrayImpl;
import org.apache.ignite.internal.processors.cache.CacheObjectImpl;
import org.apache.ignite.internal.processors.cache.KeyCacheObjectImpl;
import org.apache.ignite.internal.processors.cacheobject.UserCacheObjectByteArrayImpl;
import org.apache.ignite.internal.processors.cacheobject.UserCacheObjectImpl;
import org.apache.ignite.internal.processors.cacheobject.UserKeyCacheObjectImpl;
import org.apache.ignite.internal.util.MutableSingletonList;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.lang.IgniteBiTuple;
import org.apache.ignite.lang.IgniteUuid;
import org.jetbrains.annotations.Nullable;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.apache.ignite.IgniteSystemProperties.IGNITE_BINARY_MARSHALLER_USE_STRING_SERIALIZATION_VER_2;
/**
* Binary utils.
*/
public class BinaryUtils {
/** */
public static final Map<Class<?>, Byte> PLAIN_CLASS_TO_FLAG = new HashMap<>();
/** */
public static final Map<Byte, Class<?>> FLAG_TO_CLASS = new HashMap<>();
/** */
public static final boolean USE_STR_SERIALIZATION_VER_2 = IgniteSystemProperties.getBoolean(
IGNITE_BINARY_MARSHALLER_USE_STRING_SERIALIZATION_VER_2, false);
/** Map from class to associated write replacer. */
public static final Map<Class, BinaryWriteReplacer> CLS_TO_WRITE_REPLACER = new HashMap<>();
/** {@code true} if serialized value of this type cannot contain references to objects. */
private static final boolean[] PLAIN_TYPE_FLAG = new boolean[102];
/** Binary classes. */
private static final Collection<Class<?>> BINARY_CLS = new HashSet<>();
/** Class for SingletonList obtained at runtime. */
public static final Class<? extends Collection> SINGLETON_LIST_CLS = Collections.singletonList(null).getClass();
/** Flag: user type. */
public static final short FLAG_USR_TYP = 0x0001;
/** Flag: only raw data exists. */
public static final short FLAG_HAS_SCHEMA = 0x0002;
/** Flag indicating that object has raw data. */
public static final short FLAG_HAS_RAW = 0x0004;
/** Flag: offsets take 1 byte. */
public static final short FLAG_OFFSET_ONE_BYTE = 0x0008;
/** Flag: offsets take 2 bytes. */
public static final short FLAG_OFFSET_TWO_BYTES = 0x0010;
/** Flag: compact footer, no field IDs. */
public static final short FLAG_COMPACT_FOOTER = 0x0020;
/** Flag: raw data contains .NET type information. Always 0 in Java. Keep it here for information only. */
@SuppressWarnings("unused")
public static final short FLAG_CUSTOM_DOTNET_TYPE = 0x0040;
/** Offset which fits into 1 byte. */
public static final int OFFSET_1 = 1;
/** Offset which fits into 2 bytes. */
public static final int OFFSET_2 = 2;
/** Offset which fits into 4 bytes. */
public static final int OFFSET_4 = 4;
/** Field ID length. */
public static final int FIELD_ID_LEN = 4;
/** Whether to skip TreeMap/TreeSet wrapping. */
public static final boolean WRAP_TREES =
!IgniteSystemProperties.getBoolean(IgniteSystemProperties.IGNITE_BINARY_DONT_WRAP_TREE_STRUCTURES);
/** Whether to sort field in binary objects (doesn't affect Binarylizable). */
public static final boolean FIELDS_SORTED_ORDER =
IgniteSystemProperties.getBoolean(IgniteSystemProperties.IGNITE_BINARY_SORT_OBJECT_FIELDS);
/** Field type names. */
private static final String[] FIELD_TYPE_NAMES;
/** FNV1 hash offset basis. */
private static final int FNV1_OFFSET_BASIS = 0x811C9DC5;
/** FNV1 hash prime. */
private static final int FNV1_PRIME = 0x01000193;
/*
* Static class initializer.
*/
static {
PLAIN_CLASS_TO_FLAG.put(Byte.class, GridBinaryMarshaller.BYTE);
PLAIN_CLASS_TO_FLAG.put(Short.class, GridBinaryMarshaller.SHORT);
PLAIN_CLASS_TO_FLAG.put(Integer.class, GridBinaryMarshaller.INT);
PLAIN_CLASS_TO_FLAG.put(Long.class, GridBinaryMarshaller.LONG);
PLAIN_CLASS_TO_FLAG.put(Float.class, GridBinaryMarshaller.FLOAT);
PLAIN_CLASS_TO_FLAG.put(Double.class, GridBinaryMarshaller.DOUBLE);
PLAIN_CLASS_TO_FLAG.put(Character.class, GridBinaryMarshaller.CHAR);
PLAIN_CLASS_TO_FLAG.put(Boolean.class, GridBinaryMarshaller.BOOLEAN);
PLAIN_CLASS_TO_FLAG.put(BigDecimal.class, GridBinaryMarshaller.DECIMAL);
PLAIN_CLASS_TO_FLAG.put(String.class, GridBinaryMarshaller.STRING);
PLAIN_CLASS_TO_FLAG.put(UUID.class, GridBinaryMarshaller.UUID);
PLAIN_CLASS_TO_FLAG.put(Date.class, GridBinaryMarshaller.DATE);
PLAIN_CLASS_TO_FLAG.put(Timestamp.class, GridBinaryMarshaller.TIMESTAMP);
PLAIN_CLASS_TO_FLAG.put(Time.class, GridBinaryMarshaller.TIME);
PLAIN_CLASS_TO_FLAG.put(byte[].class, GridBinaryMarshaller.BYTE_ARR);
PLAIN_CLASS_TO_FLAG.put(short[].class, GridBinaryMarshaller.SHORT_ARR);
PLAIN_CLASS_TO_FLAG.put(int[].class, GridBinaryMarshaller.INT_ARR);
PLAIN_CLASS_TO_FLAG.put(long[].class, GridBinaryMarshaller.LONG_ARR);
PLAIN_CLASS_TO_FLAG.put(float[].class, GridBinaryMarshaller.FLOAT_ARR);
PLAIN_CLASS_TO_FLAG.put(double[].class, GridBinaryMarshaller.DOUBLE_ARR);
PLAIN_CLASS_TO_FLAG.put(char[].class, GridBinaryMarshaller.CHAR_ARR);
PLAIN_CLASS_TO_FLAG.put(boolean[].class, GridBinaryMarshaller.BOOLEAN_ARR);
PLAIN_CLASS_TO_FLAG.put(BigDecimal[].class, GridBinaryMarshaller.DECIMAL_ARR);
PLAIN_CLASS_TO_FLAG.put(String[].class, GridBinaryMarshaller.STRING_ARR);
PLAIN_CLASS_TO_FLAG.put(UUID[].class, GridBinaryMarshaller.UUID_ARR);
PLAIN_CLASS_TO_FLAG.put(Date[].class, GridBinaryMarshaller.DATE_ARR);
PLAIN_CLASS_TO_FLAG.put(Timestamp[].class, GridBinaryMarshaller.TIMESTAMP_ARR);
PLAIN_CLASS_TO_FLAG.put(Time[].class, GridBinaryMarshaller.TIME_ARR);
for (Map.Entry<Class<?>, Byte> entry : PLAIN_CLASS_TO_FLAG.entrySet())
FLAG_TO_CLASS.put(entry.getValue(), entry.getKey());
PLAIN_CLASS_TO_FLAG.put(byte.class, GridBinaryMarshaller.BYTE);
PLAIN_CLASS_TO_FLAG.put(short.class, GridBinaryMarshaller.SHORT);
PLAIN_CLASS_TO_FLAG.put(int.class, GridBinaryMarshaller.INT);
PLAIN_CLASS_TO_FLAG.put(long.class, GridBinaryMarshaller.LONG);
PLAIN_CLASS_TO_FLAG.put(float.class, GridBinaryMarshaller.FLOAT);
PLAIN_CLASS_TO_FLAG.put(double.class, GridBinaryMarshaller.DOUBLE);
PLAIN_CLASS_TO_FLAG.put(char.class, GridBinaryMarshaller.CHAR);
PLAIN_CLASS_TO_FLAG.put(boolean.class, GridBinaryMarshaller.BOOLEAN);
for (byte b : new byte[] {
GridBinaryMarshaller.BYTE, GridBinaryMarshaller.SHORT, GridBinaryMarshaller.INT, GridBinaryMarshaller.LONG, GridBinaryMarshaller.FLOAT, GridBinaryMarshaller.DOUBLE,
GridBinaryMarshaller.CHAR, GridBinaryMarshaller.BOOLEAN, GridBinaryMarshaller.DECIMAL, GridBinaryMarshaller.STRING, GridBinaryMarshaller.UUID, GridBinaryMarshaller.DATE, GridBinaryMarshaller.TIMESTAMP, GridBinaryMarshaller.TIME,
GridBinaryMarshaller.BYTE_ARR, GridBinaryMarshaller.SHORT_ARR, GridBinaryMarshaller.INT_ARR, GridBinaryMarshaller.LONG_ARR, GridBinaryMarshaller.FLOAT_ARR, GridBinaryMarshaller.DOUBLE_ARR, GridBinaryMarshaller.TIME_ARR,
GridBinaryMarshaller.CHAR_ARR, GridBinaryMarshaller.BOOLEAN_ARR, GridBinaryMarshaller.DECIMAL_ARR, GridBinaryMarshaller.STRING_ARR, GridBinaryMarshaller.UUID_ARR, GridBinaryMarshaller.DATE_ARR, GridBinaryMarshaller.TIMESTAMP_ARR,
GridBinaryMarshaller.ENUM, GridBinaryMarshaller.ENUM_ARR, GridBinaryMarshaller.NULL}) {
PLAIN_TYPE_FLAG[b] = true;
}
BINARY_CLS.add(Byte.class);
BINARY_CLS.add(Short.class);
BINARY_CLS.add(Integer.class);
BINARY_CLS.add(Long.class);
BINARY_CLS.add(Float.class);
BINARY_CLS.add(Double.class);
BINARY_CLS.add(Character.class);
BINARY_CLS.add(Boolean.class);
BINARY_CLS.add(String.class);
BINARY_CLS.add(UUID.class);
BINARY_CLS.add(Date.class);
BINARY_CLS.add(Timestamp.class);
BINARY_CLS.add(Time.class);
BINARY_CLS.add(BigDecimal.class);
BINARY_CLS.add(byte[].class);
BINARY_CLS.add(short[].class);
BINARY_CLS.add(int[].class);
BINARY_CLS.add(long[].class);
BINARY_CLS.add(float[].class);
BINARY_CLS.add(double[].class);
BINARY_CLS.add(char[].class);
BINARY_CLS.add(boolean[].class);
BINARY_CLS.add(String[].class);
BINARY_CLS.add(UUID[].class);
BINARY_CLS.add(Date[].class);
BINARY_CLS.add(Timestamp[].class);
BINARY_CLS.add(Time[].class);
BINARY_CLS.add(BigDecimal[].class);
FIELD_TYPE_NAMES = new String[104];
FIELD_TYPE_NAMES[GridBinaryMarshaller.BYTE] = "byte";
FIELD_TYPE_NAMES[GridBinaryMarshaller.SHORT] = "short";
FIELD_TYPE_NAMES[GridBinaryMarshaller.INT] = "int";
FIELD_TYPE_NAMES[GridBinaryMarshaller.LONG] = "long";
FIELD_TYPE_NAMES[GridBinaryMarshaller.BOOLEAN] = "boolean";
FIELD_TYPE_NAMES[GridBinaryMarshaller.FLOAT] = "float";
FIELD_TYPE_NAMES[GridBinaryMarshaller.DOUBLE] = "double";
FIELD_TYPE_NAMES[GridBinaryMarshaller.CHAR] = "char";
FIELD_TYPE_NAMES[GridBinaryMarshaller.UUID] = "UUID";
FIELD_TYPE_NAMES[GridBinaryMarshaller.DECIMAL] = "decimal";
FIELD_TYPE_NAMES[GridBinaryMarshaller.STRING] = "String";
FIELD_TYPE_NAMES[GridBinaryMarshaller.DATE] = "Date";
FIELD_TYPE_NAMES[GridBinaryMarshaller.TIMESTAMP] = "Timestamp";
FIELD_TYPE_NAMES[GridBinaryMarshaller.TIME] = "Time";
FIELD_TYPE_NAMES[GridBinaryMarshaller.ENUM] = "Enum";
FIELD_TYPE_NAMES[GridBinaryMarshaller.OBJ] = "Object";
FIELD_TYPE_NAMES[GridBinaryMarshaller.BINARY_OBJ] = "Object";
FIELD_TYPE_NAMES[GridBinaryMarshaller.COL] = "Collection";
FIELD_TYPE_NAMES[GridBinaryMarshaller.MAP] = "Map";
FIELD_TYPE_NAMES[GridBinaryMarshaller.CLASS] = "Class";
FIELD_TYPE_NAMES[GridBinaryMarshaller.BYTE_ARR] = "byte[]";
FIELD_TYPE_NAMES[GridBinaryMarshaller.SHORT_ARR] = "short[]";
FIELD_TYPE_NAMES[GridBinaryMarshaller.INT_ARR] = "int[]";
FIELD_TYPE_NAMES[GridBinaryMarshaller.LONG_ARR] = "long[]";
FIELD_TYPE_NAMES[GridBinaryMarshaller.BOOLEAN_ARR] = "boolean[]";
FIELD_TYPE_NAMES[GridBinaryMarshaller.FLOAT_ARR] = "float[]";
FIELD_TYPE_NAMES[GridBinaryMarshaller.DOUBLE_ARR] = "double[]";
FIELD_TYPE_NAMES[GridBinaryMarshaller.CHAR_ARR] = "char[]";
FIELD_TYPE_NAMES[GridBinaryMarshaller.UUID_ARR] = "UUID[]";
FIELD_TYPE_NAMES[GridBinaryMarshaller.DECIMAL_ARR] = "decimal[]";
FIELD_TYPE_NAMES[GridBinaryMarshaller.STRING_ARR] = "String[]";
FIELD_TYPE_NAMES[GridBinaryMarshaller.DATE_ARR] = "Date[]";
FIELD_TYPE_NAMES[GridBinaryMarshaller.TIMESTAMP_ARR] = "Timestamp[]";
FIELD_TYPE_NAMES[GridBinaryMarshaller.TIME_ARR] = "Time[]";
FIELD_TYPE_NAMES[GridBinaryMarshaller.OBJ_ARR] = "Object[]";
FIELD_TYPE_NAMES[GridBinaryMarshaller.ENUM_ARR] = "Enum[]";
FIELD_TYPE_NAMES[GridBinaryMarshaller.BINARY_ENUM] = "Enum";
if (wrapTrees()) {
CLS_TO_WRITE_REPLACER.put(TreeMap.class, new BinaryTreeMapWriteReplacer());
CLS_TO_WRITE_REPLACER.put(TreeSet.class, new BinaryTreeSetWriteReplacer());
}
}
/**
* Check if user type flag is set.
*
* @param flags Flags.
* @return {@code True} if set.
*/
public static boolean isUserType(short flags) {
return isFlagSet(flags, FLAG_USR_TYP);
}
/**
* Check if raw-only flag is set.
*
* @param flags Flags.
* @return {@code True} if set.
*/
public static boolean hasSchema(short flags) {
return isFlagSet(flags, FLAG_HAS_SCHEMA);
}
/**
* Check if raw-only flag is set.
*
* @param flags Flags.
* @return {@code True} if set.
*/
public static boolean hasRaw(short flags) {
return isFlagSet(flags, FLAG_HAS_RAW);
}
/**
* Check if "no-field-ids" flag is set.
*
* @param flags Flags.
* @return {@code True} if set.
*/
public static boolean isCompactFooter(short flags) {
return isFlagSet(flags, FLAG_COMPACT_FOOTER);
}
/**
* Check whether particular flag is set.
*
* @param flags Flags.
* @param flag Flag.
* @return {@code True} if flag is set in flags.
*/
public static boolean isFlagSet(short flags, short flag) {
return (flags & flag) == flag;
}
/**
* Schema initial ID.
*
* @return ID.
*/
public static int schemaInitialId() {
return FNV1_OFFSET_BASIS;
}
/**
* Update schema ID when new field is added.
*
* @param schemaId Current schema ID.
* @param fieldId Field ID.
* @return New schema ID.
*/
public static int updateSchemaId(int schemaId, int fieldId) {
schemaId = schemaId ^ (fieldId & 0xFF);
schemaId = schemaId * FNV1_PRIME;
schemaId = schemaId ^ ((fieldId >> 8) & 0xFF);
schemaId = schemaId * FNV1_PRIME;
schemaId = schemaId ^ ((fieldId >> 16) & 0xFF);
schemaId = schemaId * FNV1_PRIME;
schemaId = schemaId ^ ((fieldId >> 24) & 0xFF);
schemaId = schemaId * FNV1_PRIME;
return schemaId;
}
/**
* @param typeId Field type ID.
* @return Field type name or {@code null} if unknown.
*/
public static String fieldTypeName(int typeId) {
if(typeId < 0 || typeId >= FIELD_TYPE_NAMES.length)
return null;
return FIELD_TYPE_NAMES[typeId];
}
/**
* Write value with flag. e.g. writePlainObject(writer, (byte)77) will write two byte: {BYTE, 77}.
*
* @param writer W
* @param val Value.
*/
public static void writePlainObject(BinaryWriterExImpl writer, Object val) {
Byte flag = PLAIN_CLASS_TO_FLAG.get(val.getClass());
if (flag == null)
throw new IllegalArgumentException("Can't write object with type: " + val.getClass());
switch (flag) {
case GridBinaryMarshaller.BYTE:
writer.writeByte(flag);
writer.writeByte((Byte)val);
break;
case GridBinaryMarshaller.SHORT:
writer.writeByte(flag);
writer.writeShort((Short)val);
break;
case GridBinaryMarshaller.INT:
writer.writeByte(flag);
writer.writeInt((Integer)val);
break;
case GridBinaryMarshaller.LONG:
writer.writeByte(flag);
writer.writeLong((Long)val);
break;
case GridBinaryMarshaller.FLOAT:
writer.writeByte(flag);
writer.writeFloat((Float)val);
break;
case GridBinaryMarshaller.DOUBLE:
writer.writeByte(flag);
writer.writeDouble((Double)val);
break;
case GridBinaryMarshaller.CHAR:
writer.writeByte(flag);
writer.writeChar((Character)val);
break;
case GridBinaryMarshaller.BOOLEAN:
writer.writeByte(flag);
writer.writeBoolean((Boolean)val);
break;
case GridBinaryMarshaller.DECIMAL:
writer.doWriteDecimal((BigDecimal)val);
break;
case GridBinaryMarshaller.STRING:
writer.doWriteString((String)val);
break;
case GridBinaryMarshaller.UUID:
writer.doWriteUuid((UUID)val);
break;
case GridBinaryMarshaller.DATE:
writer.doWriteDate((Date)val);
break;
case GridBinaryMarshaller.TIMESTAMP:
writer.doWriteTimestamp((Timestamp)val);
break;
case GridBinaryMarshaller.TIME:
writer.doWriteTime((Time)val);
break;
case GridBinaryMarshaller.BYTE_ARR:
writer.doWriteByteArray((byte[])val);
break;
case GridBinaryMarshaller.SHORT_ARR:
writer.doWriteShortArray((short[])val);
break;
case GridBinaryMarshaller.INT_ARR:
writer.doWriteIntArray((int[])val);
break;
case GridBinaryMarshaller.LONG_ARR:
writer.doWriteLongArray((long[])val);
break;
case GridBinaryMarshaller.FLOAT_ARR:
writer.doWriteFloatArray((float[])val);
break;
case GridBinaryMarshaller.DOUBLE_ARR:
writer.doWriteDoubleArray((double[])val);
break;
case GridBinaryMarshaller.CHAR_ARR:
writer.doWriteCharArray((char[])val);
break;
case GridBinaryMarshaller.BOOLEAN_ARR:
writer.doWriteBooleanArray((boolean[])val);
break;
case GridBinaryMarshaller.DECIMAL_ARR:
writer.doWriteDecimalArray((BigDecimal[])val);
break;
case GridBinaryMarshaller.STRING_ARR:
writer.doWriteStringArray((String[])val);
break;
case GridBinaryMarshaller.UUID_ARR:
writer.doWriteUuidArray((UUID[])val);
break;
case GridBinaryMarshaller.DATE_ARR:
writer.doWriteDateArray((Date[])val);
break;
case GridBinaryMarshaller.TIMESTAMP_ARR:
writer.doWriteTimestampArray((Timestamp[])val);
break;
case GridBinaryMarshaller.TIME_ARR:
writer.doWriteTimeArray((Time[])val);
break;
default:
throw new IllegalArgumentException("Can't write object with type: " + val.getClass());
}
}
/**
* @param obj Value to unwrap.
* @return Unwrapped value.
*/
public static Object unwrapLazy(@Nullable Object obj) {
if (obj instanceof BinaryLazyValue)
return ((BinaryLazyValue)obj).value();
return obj;
}
/**
* @param delegate Iterator to delegate.
* @return New iterator.
*/
public static Iterator<Object> unwrapLazyIterator(final Iterator<Object> delegate) {
return new Iterator<Object>() {
@Override public boolean hasNext() {
return delegate.hasNext();
}
@Override public Object next() {
return unwrapLazy(delegate.next());
}
@Override public void remove() {
delegate.remove();
}
};
}
/**
* @return {@code true} if content of serialized value cannot contain references to other object.
*/
public static boolean isPlainType(int type) {
return type > 0 && type < PLAIN_TYPE_FLAG.length && PLAIN_TYPE_FLAG[type];
}
/**
* Checks whether an array type values can or can not contain references to other object.
*
* @param type Array type.
* @return {@code true} if content of serialized array value cannot contain references to other object.
*/
public static boolean isPlainArrayType(int type) {
return (type >= GridBinaryMarshaller.BYTE_ARR && type <= GridBinaryMarshaller.DATE_ARR)
|| type == GridBinaryMarshaller.TIMESTAMP_ARR || type == GridBinaryMarshaller.TIME_ARR;
}
/**
* @param cls Class.
* @return Binary field type.
*/
public static byte typeByClass(Class<?> cls) {
Byte type = PLAIN_CLASS_TO_FLAG.get(cls);
if (type != null)
return type;
if (U.isEnum(cls))
return GridBinaryMarshaller.ENUM;
if (cls.isArray())
return cls.getComponentType().isEnum() || cls.getComponentType() == Enum.class ?
GridBinaryMarshaller.ENUM_ARR : GridBinaryMarshaller.OBJ_ARR;
if (isSpecialCollection(cls))
return GridBinaryMarshaller.COL;
if (isSpecialMap(cls))
return GridBinaryMarshaller.MAP;
return GridBinaryMarshaller.OBJ;
}
/**
* Tells whether provided type is binary.
*
* @param cls Class to check.
* @return Whether type is binary.
*/
public static boolean isBinaryType(Class<?> cls) {
assert cls != null;
return BinaryObject.class.isAssignableFrom(cls) ||
Proxy.class.isAssignableFrom(cls) ||
BINARY_CLS.contains(cls);
}
/**
* @return Whether tree structures should be wrapped.
*/
public static boolean wrapTrees() {
return WRAP_TREES;
}
/**
* @param map Map to check.
* @return {@code True} if this map type is supported.
*/
public static boolean knownMap(Object map) {
Class<?> cls = map == null ? null : map.getClass();
return cls == HashMap.class ||
cls == LinkedHashMap.class ||
(!wrapTrees() && cls == TreeMap.class) ||
cls == ConcurrentHashMap.class;
}
/**
* Attempts to create a new map of the same known type. Will return null if map type is not supported.
*
* @param map Map.
* @return New map of the same type or null.
*/
@SuppressWarnings("unchecked")
public static <K, V> Map<K, V> newKnownMap(Object map) {
Class<?> cls = map == null ? null : map.getClass();
if (cls == HashMap.class)
return U.newHashMap(((Map)map).size());
else if (cls == LinkedHashMap.class)
return U.newLinkedHashMap(((Map)map).size());
else if (!wrapTrees() && cls == TreeMap.class)
return new TreeMap<>(((TreeMap<Object, Object>)map).comparator());
else if (cls == ConcurrentHashMap.class)
return new ConcurrentHashMap<>(((Map)map).size());
return null;
}
/**
* Attempts to create a new map of the same type as {@code map} has. Otherwise returns new {@code HashMap}
* instance.
*
* @param map Original map.
* @return New map.
*/
public static <K, V> Map<K, V> newMap(Map<K, V> map) {
if (map instanceof LinkedHashMap)
return U.newLinkedHashMap(map.size());
else if (map instanceof TreeMap)
return new TreeMap<>(((TreeMap<Object, Object>)map).comparator());
else if (map instanceof ConcurrentHashMap)
return new ConcurrentHashMap<>(map.size());
return U.newHashMap(map.size());
}
/**
* @param col Collection to check.
* @return True if this is a collection of a known type.
*/
public static boolean knownCollection(Object col) {
Class<?> cls = col == null ? null : col.getClass();
return cls == HashSet.class ||
cls == LinkedHashSet.class ||
(!wrapTrees() && cls == TreeSet.class) ||
cls == ConcurrentSkipListSet.class ||
cls == ArrayList.class ||
cls == LinkedList.class ||
cls == SINGLETON_LIST_CLS;
}
/**
* @param obj Object to check.
* @return True if this is an object of a known type.
*/
public static boolean knownCacheObject(Object obj) {
if (obj == null)
return false;
Class<?> cls= obj.getClass();
return cls == KeyCacheObjectImpl.class ||
cls == BinaryObjectImpl.class ||
cls == CacheObjectImpl.class ||
cls == CacheObjectByteArrayImpl.class ||
cls == BinaryEnumObjectImpl.class ||
cls == UserKeyCacheObjectImpl.class ||
cls == UserCacheObjectImpl.class ||
cls == UserCacheObjectByteArrayImpl.class;
}
/**
* @param arr Array to check.
* @return {@code true} if this array is of a known type.
*/
public static boolean knownArray(Object arr) {
if (arr == null)
return false;
Class<?> cls = arr.getClass();
return cls == byte[].class || cls == short[].class || cls == int[].class || cls == long[].class ||
cls == float[].class || cls == double[].class || cls == char[].class || cls == boolean[].class ||
cls == String[].class || cls == UUID[].class || cls == Date[].class || cls == Timestamp[].class ||
cls == BigDecimal[].class || cls == Time[].class;
}
/**
* Attempts to create a new collection of the same known type. Will return null if collection type is unknown.
*
* @param col Collection.
* @return New empty collection.
*/
@SuppressWarnings("unchecked")
public static <V> Collection<V> newKnownCollection(Object col) {
Class<?> cls = col == null ? null : col.getClass();
if (cls == HashSet.class)
return U.newHashSet(((Collection)col).size());
else if (cls == LinkedHashSet.class)
return U.newLinkedHashSet(((Collection)col).size());
else if (!wrapTrees() && cls == TreeSet.class)
return new TreeSet<>(((TreeSet<Object>)col).comparator());
else if (cls == ConcurrentSkipListSet.class)
return new ConcurrentSkipListSet<>(((ConcurrentSkipListSet<Object>)col).comparator());
else if (cls == ArrayList.class)
return new ArrayList<>(((Collection)col).size());
else if (cls == LinkedList.class)
return new LinkedList<>();
else if (cls == SINGLETON_LIST_CLS)
return new MutableSingletonList<>();
return null;
}
/**
* Attempts to create a new set of the same type as {@code set} has. Otherwise returns new {@code HashSet}
* instance.
*
* @param set Original set.
* @return New set.
*/
public static <V> Set<V> newSet(Set<V> set) {
if (set instanceof LinkedHashSet)
return U.newLinkedHashSet(set.size());
else if (set instanceof TreeSet)
return new TreeSet<>(((TreeSet<Object>)set).comparator());
else if (set instanceof ConcurrentSkipListSet)
return new ConcurrentSkipListSet<>(((ConcurrentSkipListSet<Object>)set).comparator());
return U.newHashSet(set.size());
}
/**
* Check protocol version.
*
* @param protoVer Protocol version.
*/
public static void checkProtocolVersion(byte protoVer) {
if (GridBinaryMarshaller.PROTO_VER != protoVer)
throw new BinaryObjectException("Unsupported protocol version: " + protoVer);
}
/**
* Get binary object length.
*
* @param in Input stream.
* @param start Start position.
* @return Length.
*/
public static int length(BinaryPositionReadable in, int start) {
return in.readIntPositioned(start + GridBinaryMarshaller.TOTAL_LEN_POS);
}
/**
* Get footer start of the object.
*
* @param in Input stream.
* @param start Object start position inside the stream.
* @return Footer start.
*/
public static int footerStartRelative(BinaryPositionReadable in, int start) {
short flags = in.readShortPositioned(start + GridBinaryMarshaller.FLAGS_POS);
if (hasSchema(flags))
// Schema exists, use offset.
return in.readIntPositioned(start + GridBinaryMarshaller.SCHEMA_OR_RAW_OFF_POS);
else
// No schema, footer start equals to object end.
return length(in, start);
}
/**
* Get object's footer.
*
* @param in Input stream.
* @param start Start position.
* @return Footer start.
*/
public static int footerStartAbsolute(BinaryPositionReadable in, int start) {
return footerStartRelative(in, start) + start;
}
/**
* Get object's footer.
*
* @param in Input stream.
* @param start Start position.
* @return Footer.
*/
public static IgniteBiTuple<Integer, Integer> footerAbsolute(BinaryPositionReadable in, int start) {
short flags = in.readShortPositioned(start + GridBinaryMarshaller.FLAGS_POS);
int footerEnd = length(in, start);
if (hasSchema(flags)) {
// Schema exists.
int footerStart = in.readIntPositioned(start + GridBinaryMarshaller.SCHEMA_OR_RAW_OFF_POS);
if (hasRaw(flags))
footerEnd -= 4;
assert footerStart <= footerEnd;
return F.t(start + footerStart, start + footerEnd);
}
else
// No schema.
return F.t(start + footerEnd, start + footerEnd);
}
/**
* Get relative raw offset of the object.
*
* @param in Input stream.
* @param start Object start position inside the stream.
* @return Raw offset.
*/
public static int rawOffsetRelative(BinaryPositionReadable in, int start) {
short flags = in.readShortPositioned(start + GridBinaryMarshaller.FLAGS_POS);
int len = length(in, start);
if (hasSchema(flags)) {
// Schema exists.
if (hasRaw(flags))
// Raw offset is set, it is at the very end of the object.
return in.readIntPositioned(start + len - 4);
else
// Raw offset is not set, so just return schema offset.
return in.readIntPositioned(start + GridBinaryMarshaller.SCHEMA_OR_RAW_OFF_POS);
}
else
// No schema, raw offset is located on schema offset position.
return in.readIntPositioned(start + GridBinaryMarshaller.SCHEMA_OR_RAW_OFF_POS);
}
/**
* Get absolute raw offset of the object.
*
* @param in Input stream.
* @param start Object start position inside the stream.
* @return Raw offset.
*/
public static int rawOffsetAbsolute(BinaryPositionReadable in, int start) {
return start + rawOffsetRelative(in, start);
}
/**
* Get offset length for the given flags.
*
* @param flags Flags.
* @return Offset size.
*/
public static int fieldOffsetLength(short flags) {
if ((flags & FLAG_OFFSET_ONE_BYTE) == FLAG_OFFSET_ONE_BYTE)
return OFFSET_1;
else if ((flags & FLAG_OFFSET_TWO_BYTES) == FLAG_OFFSET_TWO_BYTES)
return OFFSET_2;
else
return OFFSET_4;
}
/**
* Get field ID length.
*
* @param flags Flags.
* @return Field ID length.
*/
public static int fieldIdLength(short flags) {
return isCompactFooter(flags) ? 0 : FIELD_ID_LEN;
}
/**
* Get relative field offset.
*
* @param stream Stream.
* @param pos Position.
* @param fieldOffsetSize Field offset size.
* @return Relative field offset.
*/
public static int fieldOffsetRelative(BinaryPositionReadable stream, int pos, int fieldOffsetSize) {
int res;
if (fieldOffsetSize == OFFSET_1)
res = (int)stream.readBytePositioned(pos) & 0xFF;
else if (fieldOffsetSize == OFFSET_2)
res = (int)stream.readShortPositioned(pos) & 0xFFFF;
else
res = stream.readIntPositioned(pos);
return res;
}
/**
* Merge old and new metas.
*
* @param oldMeta Old meta.
* @param newMeta New meta.
* @return New meta if old meta was null, old meta if no changes detected, merged meta otherwise.
* @throws BinaryObjectException If merge failed due to metadata conflict.
*/
public static BinaryMetadata mergeMetadata(@Nullable BinaryMetadata oldMeta, BinaryMetadata newMeta) {
assert newMeta != null;
if (oldMeta == null)
return newMeta;
else {
assert oldMeta.typeId() == newMeta.typeId();
// Check type name.
if (!F.eq(oldMeta.typeName(), newMeta.typeName())) {
throw new BinaryObjectException(
"Two binary types have duplicate type ID [" + "typeId=" + oldMeta.typeId() +
", typeName1=" + oldMeta.typeName() + ", typeName2=" + newMeta.typeName() + ']'
);
}
// Check affinity field names.
if (!F.eq(oldMeta.affinityKeyFieldName(), newMeta.affinityKeyFieldName())) {
throw new BinaryObjectException(
"Binary type has different affinity key fields [" + "typeName=" + newMeta.typeName() +
", affKeyFieldName1=" + oldMeta.affinityKeyFieldName() +
", affKeyFieldName2=" + newMeta.affinityKeyFieldName() + ']'
);
}
// Check enum flag.
if (oldMeta.isEnum() != newMeta.isEnum()) {
if (oldMeta.isEnum())
throw new BinaryObjectException("Binary type already registered as enum: " +
newMeta.typeName());
else
throw new BinaryObjectException("Binary type already registered as non-enum: " +
newMeta.typeName());
}
// Check and merge fields.
Map<String, BinaryFieldMetadata> mergedFields;
if (FIELDS_SORTED_ORDER)
mergedFields = new TreeMap<>(oldMeta.fieldsMap());
else
mergedFields = new LinkedHashMap<>(oldMeta.fieldsMap());
Map<String, BinaryFieldMetadata> newFields = newMeta.fieldsMap();
boolean changed = false;
Map<String, Integer> mergedEnumMap = null;
if (!F.isEmpty(newMeta.enumMap())) {
mergedEnumMap = mergeEnumValues(oldMeta.typeName(), oldMeta.enumMap(), newMeta.enumMap());
changed = mergedEnumMap.size() > oldMeta.enumMap().size();
}
for (Map.Entry<String, BinaryFieldMetadata> newField : newFields.entrySet()) {
BinaryFieldMetadata oldFieldMeta = mergedFields.put(newField.getKey(), newField.getValue());
if (oldFieldMeta == null)
changed = true;
else {
String oldFieldTypeName = fieldTypeName(oldFieldMeta.typeId());
String newFieldTypeName = fieldTypeName(newField.getValue().typeId());
if (!F.eq(oldFieldTypeName, newFieldTypeName)) {
throw new BinaryObjectException(
"Binary type has different field types [" + "typeName=" + oldMeta.typeName() +
", fieldName=" + newField.getKey() +
", fieldTypeName1=" + oldFieldTypeName +
", fieldTypeName2=" + newFieldTypeName + ']'
);
}
}
}
// Check and merge schemas.
Collection<BinarySchema> mergedSchemas = new HashSet<>(oldMeta.schemas());
for (BinarySchema newSchema : newMeta.schemas()) {
if (mergedSchemas.add(newSchema))
changed = true;
}
// Return either old meta if no changes detected, or new merged meta.
return changed ? new BinaryMetadata(oldMeta.typeId(), oldMeta.typeName(), mergedFields,
oldMeta.affinityKeyFieldName(), mergedSchemas, oldMeta.isEnum(), mergedEnumMap) : oldMeta;
}
}
/**
* @param cls Class.
* @return Mode.
*/
@SuppressWarnings("IfMayBeConditional")
public static BinaryWriteMode mode(Class<?> cls) {
assert cls != null;
// Primitives.
if (cls == byte.class)
return BinaryWriteMode.P_BYTE;
else if (cls == boolean.class)
return BinaryWriteMode.P_BOOLEAN;
else if (cls == short.class)
return BinaryWriteMode.P_SHORT;
else if (cls == char.class)
return BinaryWriteMode.P_CHAR;
else if (cls == int.class)
return BinaryWriteMode.P_INT;
else if (cls == long.class)
return BinaryWriteMode.P_LONG;
else if (cls == float.class)
return BinaryWriteMode.P_FLOAT;
else if (cls == double.class)
return BinaryWriteMode.P_DOUBLE;
// Boxed primitives.
else if (cls == Byte.class)
return BinaryWriteMode.BYTE;
else if (cls == Boolean.class)
return BinaryWriteMode.BOOLEAN;
else if (cls == Short.class)
return BinaryWriteMode.SHORT;
else if (cls == Character.class)
return BinaryWriteMode.CHAR;
else if (cls == Integer.class)
return BinaryWriteMode.INT;
else if (cls == Long.class)
return BinaryWriteMode.LONG;
else if (cls == Float.class)
return BinaryWriteMode.FLOAT;
else if (cls == Double.class)
return BinaryWriteMode.DOUBLE;
// The rest types.
else if (cls == BigDecimal.class)
return BinaryWriteMode.DECIMAL;
else if (cls == String.class)
return BinaryWriteMode.STRING;
else if (cls == UUID.class)
return BinaryWriteMode.UUID;
else if (cls == Date.class)
return BinaryWriteMode.DATE;
else if (cls == Timestamp.class)
return BinaryWriteMode.TIMESTAMP;
else if (cls == Time.class)
return BinaryWriteMode.TIME;
else if (cls == byte[].class)
return BinaryWriteMode.BYTE_ARR;
else if (cls == short[].class)
return BinaryWriteMode.SHORT_ARR;
else if (cls == int[].class)
return BinaryWriteMode.INT_ARR;
else if (cls == long[].class)
return BinaryWriteMode.LONG_ARR;
else if (cls == float[].class)
return BinaryWriteMode.FLOAT_ARR;
else if (cls == double[].class)
return BinaryWriteMode.DOUBLE_ARR;
else if (cls == char[].class)
return BinaryWriteMode.CHAR_ARR;
else if (cls == boolean[].class)
return BinaryWriteMode.BOOLEAN_ARR;
else if (cls == BigDecimal[].class)
return BinaryWriteMode.DECIMAL_ARR;
else if (cls == String[].class)
return BinaryWriteMode.STRING_ARR;
else if (cls == UUID[].class)
return BinaryWriteMode.UUID_ARR;
else if (cls == Date[].class)
return BinaryWriteMode.DATE_ARR;
else if (cls == Timestamp[].class)
return BinaryWriteMode.TIMESTAMP_ARR;
else if (cls == Time[].class)
return BinaryWriteMode.TIME_ARR;
else if (cls.isArray())
return cls.getComponentType().isEnum() ? BinaryWriteMode.ENUM_ARR : BinaryWriteMode.OBJECT_ARR;
else if (cls == BinaryObjectImpl.class)
return BinaryWriteMode.BINARY_OBJ;
else if (Binarylizable.class.isAssignableFrom(cls))
return BinaryWriteMode.BINARY;
else if (isSpecialCollection(cls))
return BinaryWriteMode.COL;
else if (isSpecialMap(cls))
return BinaryWriteMode.MAP;
else if (U.isEnum(cls))
return BinaryWriteMode.ENUM;
else if (cls == BinaryEnumObjectImpl.class)
return BinaryWriteMode.BINARY_ENUM;
else if (cls == Class.class)
return BinaryWriteMode.CLASS;
else if (Proxy.class.isAssignableFrom(cls))
return BinaryWriteMode.PROXY;
else
return BinaryWriteMode.OBJECT;
}
/**
* Check if class represents a collection which must be treated specially.
*
* @param cls Class.
* @return {@code True} if this is a special collection class.
*/
public static boolean isSpecialCollection(Class cls) {
return ArrayList.class.equals(cls) || LinkedList.class.equals(cls) || SINGLETON_LIST_CLS.equals(cls) ||
HashSet.class.equals(cls) || LinkedHashSet.class.equals(cls);
}
/**
* Check if class represents a map which must be treated specially.
*
* @param cls Class.
* @return {@code True} if this is a special map class.
*/
public static boolean isSpecialMap(Class cls) {
return HashMap.class.equals(cls) || LinkedHashMap.class.equals(cls);
}
/**
* @return Value.
*/
public static byte[] doReadByteArray(BinaryInputStream in) {
int len = in.readInt();
return in.readByteArray(len);
}
/**
* @return Value.
*/
public static boolean[] doReadBooleanArray(BinaryInputStream in) {
int len = in.readInt();
return in.readBooleanArray(len);
}
/**
* @return Value.
*/
public static short[] doReadShortArray(BinaryInputStream in) {
int len = in.readInt();
return in.readShortArray(len);
}
/**
* @return Value.
*/
public static char[] doReadCharArray(BinaryInputStream in) {
int len = in.readInt();
return in.readCharArray(len);
}
/**
* @return Value.
*/
public static int[] doReadIntArray(BinaryInputStream in) {
int len = in.readInt();
return in.readIntArray(len);
}
/**
* @return Value.
*/
public static long[] doReadLongArray(BinaryInputStream in) {
int len = in.readInt();
return in.readLongArray(len);
}
/**
* @return Value.
*/
public static float[] doReadFloatArray(BinaryInputStream in) {
int len = in.readInt();
return in.readFloatArray(len);
}
/**
* @return Value.
*/
public static double[] doReadDoubleArray(BinaryInputStream in) {
int len = in.readInt();
return in.readDoubleArray(len);
}
/**
* @return Value.
*/
public static BigDecimal doReadDecimal(BinaryInputStream in) {
int scale = in.readInt();
byte[] mag = doReadByteArray(in);
boolean negative = mag[0] < 0;
if (negative)
mag[0] &= 0x7F;
BigInteger intVal = new BigInteger(mag);
if (negative)
intVal = intVal.negate();
return new BigDecimal(intVal, scale);
}
/**
* @return Value.
*/
public static String doReadString(BinaryInputStream in) {
if (!in.hasArray()) {
byte[] arr = doReadByteArray(in);
if (USE_STR_SERIALIZATION_VER_2)
return utf8BytesToStr(arr, 0, arr.length);
else
return new String(arr, UTF_8);
}
int strLen = in.readInt();
int pos = in.position();
// String will copy necessary array part for us.
String res;
if (USE_STR_SERIALIZATION_VER_2) {
res = utf8BytesToStr(in.array(), pos, strLen);
}
else {
res = new String(in.array(), pos, strLen, UTF_8);
}
in.position(pos + strLen);
return res;
}
/**
* @return Value.
*/
public static UUID doReadUuid(BinaryInputStream in) {
return new UUID(in.readLong(), in.readLong());
}
/**
* @return Value.
*/
public static Date doReadDate(BinaryInputStream in) {
long time = in.readLong();
return new Date(time);
}
/**
* @return Value.
*/
public static Timestamp doReadTimestamp(BinaryInputStream in) {
long time = in.readLong();
int nanos = in.readInt();
Timestamp ts = new Timestamp(time);
ts.setNanos(ts.getNanos() + nanos);
return ts;
}
/**
* @return Value.
*/
public static Time doReadTime(BinaryInputStream in) {
long time = in.readLong();
return new Time(time);
}
/**
* @return Value.
* @throws BinaryObjectException In case of error.
*/
public static BigDecimal[] doReadDecimalArray(BinaryInputStream in) throws BinaryObjectException {
int len = in.readInt();
BigDecimal[] arr = new BigDecimal[len];
for (int i = 0; i < len; i++) {
byte flag = in.readByte();
if (flag == GridBinaryMarshaller.NULL)
arr[i] = null;
else {
if (flag != GridBinaryMarshaller.DECIMAL)
throw new BinaryObjectException("Invalid flag value: " + flag);
arr[i] = doReadDecimal(in);
}
}
return arr;
}
/**
* @return Value.
* @throws BinaryObjectException In case of error.
*/
public static String[] doReadStringArray(BinaryInputStream in) throws BinaryObjectException {
int len = in.readInt();
String[] arr = new String[len];
for (int i = 0; i < len; i++) {
byte flag = in.readByte();
if (flag == GridBinaryMarshaller.NULL)
arr[i] = null;
else {
if (flag != GridBinaryMarshaller.STRING)
throw new BinaryObjectException("Invalid flag value: " + flag);
arr[i] = doReadString(in);
}
}
return arr;
}
/**
* @return Value.
* @throws BinaryObjectException In case of error.
*/
public static UUID[] doReadUuidArray(BinaryInputStream in) throws BinaryObjectException {
int len = in.readInt();
UUID[] arr = new UUID[len];
for (int i = 0; i < len; i++) {
byte flag = in.readByte();
if (flag == GridBinaryMarshaller.NULL)
arr[i] = null;
else {
if (flag != GridBinaryMarshaller.UUID)
throw new BinaryObjectException("Invalid flag value: " + flag);
arr[i] = doReadUuid(in);
}
}
return arr;
}
/**
* @return Value.
* @throws BinaryObjectException In case of error.
*/
public static Date[] doReadDateArray(BinaryInputStream in) throws BinaryObjectException {
int len = in.readInt();
Date[] arr = new Date[len];
for (int i = 0; i < len; i++) {
byte flag = in.readByte();
if (flag == GridBinaryMarshaller.NULL)
arr[i] = null;
else {
if (flag != GridBinaryMarshaller.DATE)
throw new BinaryObjectException("Invalid flag value: " + flag);
arr[i] = doReadDate(in);
}
}
return arr;
}
/**
* @return Value.
* @throws BinaryObjectException In case of error.
*/
public static Timestamp[] doReadTimestampArray(BinaryInputStream in) throws BinaryObjectException {
int len = in.readInt();
Timestamp[] arr = new Timestamp[len];
for (int i = 0; i < len; i++) {
byte flag = in.readByte();
if (flag == GridBinaryMarshaller.NULL)
arr[i] = null;
else {
if (flag != GridBinaryMarshaller.TIMESTAMP)
throw new BinaryObjectException("Invalid flag value: " + flag);
arr[i] = doReadTimestamp(in);
}
}
return arr;
}
/**
* @return Value.
* @throws BinaryObjectException In case of error.
*/
public static Time[] doReadTimeArray(BinaryInputStream in) throws BinaryObjectException {
int len = in.readInt();
Time[] arr = new Time[len];
for (int i = 0; i < len; i++) {
byte flag = in.readByte();
if (flag == GridBinaryMarshaller.NULL)
arr[i] = null;
else {
if (flag != GridBinaryMarshaller.TIME)
throw new BinaryObjectException("Invalid flag value: " + flag);
arr[i] = doReadTime(in);
}
}
return arr;
}
/**
* @return Value.
*/
public static BinaryObject doReadBinaryObject(BinaryInputStream in, BinaryContext ctx, boolean detach) {
if (in.offheapPointer() > 0) {
int len = in.readInt();
int pos = in.position();
in.position(in.position() + len);
int start = in.readInt();
return new BinaryObjectOffheapImpl(ctx, in.offheapPointer() + pos, start, len);
}
else {
byte[] arr = doReadByteArray(in);
int start = in.readInt();
BinaryObjectImpl binO = new BinaryObjectImpl(ctx, arr, start);
if (detach) {
binO.detachAllowed(true);
return binO.detach();
}
return binO;
}
}
/**
* @param in Binary input stream.
* @param ctx Binary context.
* @param ldr Class loader.
* @return Class object specified at the input stream.
* @throws BinaryObjectException If failed.
*/
public static Class doReadClass(BinaryInputStream in, BinaryContext ctx, ClassLoader ldr)
throws BinaryObjectException {
return doReadClass(in, ctx, ldr, true);
}
/**
* @param in Binary input stream.
* @param ctx Binary context.
* @param ldr Class loader.
* @param deserialize Doesn't load the class when the flag is {@code false}. Class information is skipped.
* @return Class object specified at the input stream if {@code deserialize == true}. Otherwise returns {@code null}
* @throws BinaryObjectException If failed.
*/
public static Class doReadClass(BinaryInputStream in, BinaryContext ctx, ClassLoader ldr, boolean deserialize)
throws BinaryObjectException {
int typeId = in.readInt();
if (!deserialize) {
// Skip class name at the stream.
if (typeId == GridBinaryMarshaller.UNREGISTERED_TYPE_ID)
doReadClassName(in);
return null;
}
return doReadClass(in, ctx, ldr, typeId);
}
/**
* @return Value.
*/
@SuppressWarnings("ConstantConditions")
public static Object doReadProxy(BinaryInputStream in, BinaryContext ctx, ClassLoader ldr,
BinaryReaderHandlesHolder handles) {
Class<?>[] intfs = new Class<?>[in.readInt()];
for (int i = 0; i < intfs.length; i++)
intfs[i] = doReadClass(in, ctx, ldr);
InvocationHandler ih = (InvocationHandler)doReadObject(in, ctx, ldr, handles);
return Proxy.newProxyInstance(ldr != null ? ldr : U.gridClassLoader(), intfs, ih);
}
/**
* Read plain type.
*
* @param in Input stream.
* @return Plain type.
*/
private static EnumType doReadEnumType(BinaryInputStream in) {
int typeId = in.readInt();
if (typeId != GridBinaryMarshaller.UNREGISTERED_TYPE_ID)
return new EnumType(typeId, null);
else {
String clsName = doReadClassName(in);
return new EnumType(GridBinaryMarshaller.UNREGISTERED_TYPE_ID, clsName);
}
}
/**
* @param in Input stream.
* @return Class name.
*/
public static String doReadClassName(BinaryInputStream in) {
byte flag = in.readByte();
if (flag != GridBinaryMarshaller.STRING)
throw new BinaryObjectException("Failed to read class name [position=" + (in.position() - 1) + ']');
return doReadString(in);
}
/**
* @param in Binary input stream.
* @param ctx Binary context.
* @param ldr Class loader.
* @param typeId Type id.
* @return Class object specified at the input stream.
* @throws BinaryObjectException If failed.
*/
public static Class doReadClass(BinaryInputStream in, BinaryContext ctx, ClassLoader ldr, int typeId)
throws BinaryObjectException {
Class cls;
if (typeId != GridBinaryMarshaller.UNREGISTERED_TYPE_ID)
cls = ctx.descriptorForTypeId(true, typeId, ldr, true).describedClass();
else {
String clsName = doReadClassName(in);
try {
cls = U.forName(clsName, ldr);
}
catch (ClassNotFoundException e) {
throw new BinaryInvalidTypeException("Failed to load the class: " + clsName, e);
}
// forces registering of class by type id, at least locally
ctx.descriptorForClass(cls, true, false);
}
return cls;
}
/**
* Resolve the class.
*
* @param ctx Binary context.
* @param typeId Type ID.
* @param clsName Class name.
* @param ldr Class loaded.
* @return Resovled class.
*/
public static Class resolveClass(BinaryContext ctx, int typeId, @Nullable String clsName,
@Nullable ClassLoader ldr, boolean deserialize) {
Class cls;
if (typeId != GridBinaryMarshaller.UNREGISTERED_TYPE_ID)
cls = ctx.descriptorForTypeId(true, typeId, ldr, deserialize).describedClass();
else {
try {
cls = U.forName(clsName, ldr);
}
catch (ClassNotFoundException e) {
throw new BinaryInvalidTypeException("Failed to load the class: " + clsName, e);
}
// forces registering of class by type id, at least locally
ctx.descriptorForClass(cls, true, false);
}
return cls;
}
/**
* Read binary enum.
*
* @param in Input stream.
* @param ctx Binary context.
* @return Enum.
*/
public static BinaryEnumObjectImpl doReadBinaryEnum(BinaryInputStream in, BinaryContext ctx) {
return doReadBinaryEnum(in, ctx, doReadEnumType(in));
}
/**
* Read binary enum.
*
* @param in Input stream.
* @param ctx Binary context.
* @param type Plain type.
* @return Enum.
*/
private static BinaryEnumObjectImpl doReadBinaryEnum(BinaryInputStream in, BinaryContext ctx,
EnumType type) {
return new BinaryEnumObjectImpl(ctx, type.typeId, type.clsName, in.readInt());
}
/**
* Read binary enum array.
*
* @param in Input stream.
* @param ctx Binary context.
* @return Enum array.
*/
private static Object[] doReadBinaryEnumArray(BinaryInputStream in, BinaryContext ctx) {
int len = in.readInt();
Object[] arr = (Object[])Array.newInstance(BinaryObject.class, len);
for (int i = 0; i < len; i++) {
byte flag = in.readByte();
if (flag == GridBinaryMarshaller.NULL)
arr[i] = null;
else
arr[i] = doReadBinaryEnum(in, ctx, doReadEnumType(in));
}
return arr;
}
/**
* Having target class in place we simply read ordinal and create final representation.
*
* @param cls Enum class.
* @return Value.
*/
public static Enum<?> doReadEnum(BinaryInputStream in, Class<?> cls) throws BinaryObjectException {
assert cls != null;
if (!cls.isEnum())
throw new BinaryObjectException("Class does not represent enum type: " + cls.getName());
int ord = in.readInt();
return BinaryEnumCache.get(cls, ord);
}
/**
* @param cls Enum class.
* @return Value.
*/
public static Object[] doReadEnumArray(BinaryInputStream in, BinaryContext ctx, ClassLoader ldr, Class<?> cls)
throws BinaryObjectException {
int len = in.readInt();
Object[] arr = (Object[])Array.newInstance(cls, len);
for (int i = 0; i < len; i++) {
byte flag = in.readByte();
if (flag == GridBinaryMarshaller.NULL)
arr[i] = null;
else
arr[i] = doReadEnum(in, doReadClass(in, ctx, ldr));
}
return arr;
}
/**
* Read object serialized using optimized marshaller.
*
* @return Result.
*/
public static Object doReadOptimized(BinaryInputStream in, BinaryContext ctx, @Nullable ClassLoader clsLdr) {
int len = in.readInt();
ByteArrayInputStream input = new ByteArrayInputStream(in.array(), in.position(), len);
try {
return ctx.optimizedMarsh().unmarshal(input, U.resolveClassLoader(clsLdr, ctx.configuration()));
}
catch (IgniteCheckedException e) {
throw new BinaryObjectException("Failed to unmarshal object with optimized marshaller", e);
}
finally {
in.position(in.position() + len);
}
}
/**
* @return Object.
* @throws BinaryObjectException In case of error.
*/
@Nullable public static Object doReadObject(BinaryInputStream in, BinaryContext ctx, ClassLoader ldr,
BinaryReaderHandlesHolder handles) throws BinaryObjectException {
return new BinaryReaderExImpl(ctx, in, ldr, handles.handles(), true).deserialize();
}
/**
* @return Unmarshalled value.
* @throws BinaryObjectException In case of error.
*/
@Nullable public static Object unmarshal(BinaryInputStream in, BinaryContext ctx, ClassLoader ldr)
throws BinaryObjectException {
return unmarshal(in, ctx, ldr, new BinaryReaderHandlesHolderImpl());
}
/**
* @return Unmarshalled value.
* @throws BinaryObjectException In case of error.
*/
@Nullable public static Object unmarshal(BinaryInputStream in, BinaryContext ctx, ClassLoader ldr,
BinaryReaderHandlesHolder handles) throws BinaryObjectException {
return unmarshal(in, ctx, ldr, handles, false);
}
/**
* @return Unmarshalled value.
* @throws BinaryObjectException In case of error.
*/
@Nullable public static Object unmarshal(BinaryInputStream in, BinaryContext ctx, ClassLoader ldr,
BinaryReaderHandlesHolder handles, boolean detach) throws BinaryObjectException {
int start = in.position();
byte flag = in.readByte();
switch (flag) {
case GridBinaryMarshaller.NULL:
return null;
case GridBinaryMarshaller.HANDLE: {
int handlePos = start - in.readInt();
Object obj = handles.getHandle(handlePos);
if (obj == null) {
int retPos = in.position();
in.position(handlePos);
obj = unmarshal(in, ctx, ldr, handles);
in.position(retPos);
}
return obj;
}
case GridBinaryMarshaller.OBJ: {
checkProtocolVersion(in.readByte());
int len = length(in, start);
BinaryObjectExImpl po;
if (detach) {
// In detach mode we simply copy object's content.
in.position(start);
po = new BinaryObjectImpl(ctx, in.readByteArray(len), 0);
}
else {
if (in.offheapPointer() == 0)
po = new BinaryObjectImpl(ctx, in.array(), start);
else
po = new BinaryObjectOffheapImpl(ctx, in.offheapPointer(), start,
in.remaining() + in.position());
in.position(start + po.length());
}
handles.setHandle(po, start);
return po;
}
case GridBinaryMarshaller.BYTE:
return in.readByte();
case GridBinaryMarshaller.SHORT:
return in.readShort();
case GridBinaryMarshaller.INT:
return in.readInt();
case GridBinaryMarshaller.LONG:
return in.readLong();
case GridBinaryMarshaller.FLOAT:
return in.readFloat();
case GridBinaryMarshaller.DOUBLE:
return in.readDouble();
case GridBinaryMarshaller.CHAR:
return in.readChar();
case GridBinaryMarshaller.BOOLEAN:
return in.readBoolean();
case GridBinaryMarshaller.DECIMAL:
return doReadDecimal(in);
case GridBinaryMarshaller.STRING:
return doReadString(in);
case GridBinaryMarshaller.UUID:
return doReadUuid(in);
case GridBinaryMarshaller.DATE:
return doReadDate(in);
case GridBinaryMarshaller.TIMESTAMP:
return doReadTimestamp(in);
case GridBinaryMarshaller.TIME:
return doReadTime(in);
case GridBinaryMarshaller.BYTE_ARR:
return doReadByteArray(in);
case GridBinaryMarshaller.SHORT_ARR:
return doReadShortArray(in);
case GridBinaryMarshaller.INT_ARR:
return doReadIntArray(in);
case GridBinaryMarshaller.LONG_ARR:
return doReadLongArray(in);
case GridBinaryMarshaller.FLOAT_ARR:
return doReadFloatArray(in);
case GridBinaryMarshaller.DOUBLE_ARR:
return doReadDoubleArray(in);
case GridBinaryMarshaller.CHAR_ARR:
return doReadCharArray(in);
case GridBinaryMarshaller.BOOLEAN_ARR:
return doReadBooleanArray(in);
case GridBinaryMarshaller.DECIMAL_ARR:
return doReadDecimalArray(in);
case GridBinaryMarshaller.STRING_ARR:
return doReadStringArray(in);
case GridBinaryMarshaller.UUID_ARR:
return doReadUuidArray(in);
case GridBinaryMarshaller.DATE_ARR:
return doReadDateArray(in);
case GridBinaryMarshaller.TIMESTAMP_ARR:
return doReadTimestampArray(in);
case GridBinaryMarshaller.TIME_ARR:
return doReadTimeArray(in);
case GridBinaryMarshaller.OBJ_ARR:
return doReadObjectArray(in, ctx, ldr, handles, false);
case GridBinaryMarshaller.COL:
return doReadCollection(in, ctx, ldr, handles, false, null);
case GridBinaryMarshaller.MAP:
return doReadMap(in, ctx, ldr, handles, false, null);
case GridBinaryMarshaller.BINARY_OBJ:
return doReadBinaryObject(in, ctx, detach);
case GridBinaryMarshaller.ENUM:
case GridBinaryMarshaller.BINARY_ENUM:
return doReadBinaryEnum(in, ctx, doReadEnumType(in));
case GridBinaryMarshaller.ENUM_ARR:
doReadEnumType(in); // Simply skip this part as we do not need it.
return doReadBinaryEnumArray(in, ctx);
case GridBinaryMarshaller.CLASS:
return doReadClass(in, ctx, ldr);
case GridBinaryMarshaller.PROXY:
return doReadProxy(in, ctx, ldr, handles);
case GridBinaryMarshaller.OPTM_MARSH:
return doReadOptimized(in, ctx, ldr);
default:
throw new BinaryObjectException("Invalid flag value: " + flag);
}
}
/**
* @param in Binary input stream.
* @param ctx Binary context.
* @param ldr Class loader.
* @param handles Holder for handles.
* @param deserialize Deep flag.
* @return Value.
* @throws BinaryObjectException In case of error.
*/
public static Object[] doReadObjectArray(BinaryInputStream in, BinaryContext ctx, ClassLoader ldr,
BinaryReaderHandlesHolder handles, boolean deserialize) throws BinaryObjectException {
int hPos = positionForHandle(in);
Class compType = doReadClass(in, ctx, ldr, deserialize);
int len = in.readInt();
Object[] arr = deserialize ? (Object[])Array.newInstance(compType, len) : new Object[len];
handles.setHandle(arr, hPos);
for (int i = 0; i < len; i++)
arr[i] = deserializeOrUnmarshal(in, ctx, ldr, handles, deserialize);
return arr;
}
/**
* @param deserialize Deep flag.
* @param factory Collection factory.
* @return Value.
* @throws BinaryObjectException In case of error.
*/
@SuppressWarnings("unchecked")
public static Collection<?> doReadCollection(BinaryInputStream in, BinaryContext ctx, ClassLoader ldr,
BinaryReaderHandlesHolder handles, boolean deserialize, BinaryCollectionFactory factory)
throws BinaryObjectException {
int hPos = positionForHandle(in);
int size = in.readInt();
assert size >= 0;
byte colType = in.readByte();
Collection<Object> col;
if (factory != null)
col = factory.create(size);
else {
switch (colType) {
case GridBinaryMarshaller.ARR_LIST:
col = new ArrayList<>(size);
break;
case GridBinaryMarshaller.LINKED_LIST:
col = new LinkedList<>();
break;
case GridBinaryMarshaller.SINGLETON_LIST:
col = new MutableSingletonList<>();
break;
case GridBinaryMarshaller.HASH_SET:
col = U.newHashSet(size);
break;
case GridBinaryMarshaller.LINKED_HASH_SET:
col = U.newLinkedHashSet(size);
break;
case GridBinaryMarshaller.USER_SET:
col = U.newHashSet(size);
break;
case GridBinaryMarshaller.USER_COL:
col = new ArrayList<>(size);
break;
default:
throw new BinaryObjectException("Invalid collection type: " + colType);
}
}
handles.setHandle(col, hPos);
for (int i = 0; i < size; i++)
col.add(deserializeOrUnmarshal(in, ctx, ldr, handles, deserialize));
return colType == GridBinaryMarshaller.SINGLETON_LIST ? U.convertToSingletonList(col) : col;
}
/**
* @param deserialize Deep flag.
* @param factory Map factory.
* @return Value.
* @throws BinaryObjectException In case of error.
*/
@SuppressWarnings("unchecked")
public static Map<?, ?> doReadMap(BinaryInputStream in, BinaryContext ctx, ClassLoader ldr,
BinaryReaderHandlesHolder handles, boolean deserialize, BinaryMapFactory factory)
throws BinaryObjectException {
int hPos = positionForHandle(in);
int size = in.readInt();
assert size >= 0;
byte mapType = in.readByte();
Map<Object, Object> map;
if (factory != null)
map = factory.create(size);
else {
switch (mapType) {
case GridBinaryMarshaller.HASH_MAP:
map = U.newHashMap(size);
break;
case GridBinaryMarshaller.LINKED_HASH_MAP:
map = U.newLinkedHashMap(size);
break;
case GridBinaryMarshaller.USER_COL:
map = U.newHashMap(size);
break;
default:
throw new BinaryObjectException("Invalid map type: " + mapType);
}
}
handles.setHandle(map, hPos);
for (int i = 0; i < size; i++) {
Object key = deserializeOrUnmarshal(in, ctx, ldr, handles, deserialize);
Object val = deserializeOrUnmarshal(in, ctx, ldr, handles, deserialize);
map.put(key, val);
}
return map;
}
/**
* Deserialize or unmarshal the object.
*
* @param deserialize Deserialize.
* @return Result.
*/
private static Object deserializeOrUnmarshal(BinaryInputStream in, BinaryContext ctx, ClassLoader ldr,
BinaryReaderHandlesHolder handles, boolean deserialize) {
return deserialize ? doReadObject(in, ctx, ldr, handles) : unmarshal(in, ctx, ldr, handles);
}
/**
* Get position to be used for handle. We assume here that the hdr byte was read, hence subtract -1.
*
* @return Position for handle.
*/
public static int positionForHandle(BinaryInputStream in) {
return in.position() - 1;
}
/**
* Check if class is binarylizable.
*
* @param cls Class.
* @return {@code True} if binarylizable.
*/
public static boolean isBinarylizable(Class cls) {
for (Class c = cls; c != null && !c.equals(Object.class); c = c.getSuperclass()) {
if (Binarylizable.class.isAssignableFrom(c))
return true;
}
return false;
}
/**
* Determines whether class contains custom Java serialization logic.
*
* @param cls Class.
* @return {@code true} if custom Java serialization logic exists, {@code false} otherwise.
*/
@SuppressWarnings("unchecked")
public static boolean isCustomJavaSerialization(Class cls) {
for (Class c = cls; c != null && !c.equals(Object.class); c = c.getSuperclass()) {
if (Externalizable.class.isAssignableFrom(c))
return true;
try {
Method writeObj = c.getDeclaredMethod("writeObject", ObjectOutputStream.class);
Method readObj = c.getDeclaredMethod("readObject", ObjectInputStream.class);
if (!Modifier.isStatic(writeObj.getModifiers()) && !Modifier.isStatic(readObj.getModifiers()) &&
writeObj.getReturnType() == void.class && readObj.getReturnType() == void.class)
return true;
}
catch (NoSuchMethodException ignored) {
// No-op.
}
}
return false;
}
/**
* Create qualified field name.
*
* @param cls Class.
* @param fieldName Field name.
* @return Qualified field name.
*/
public static String qualifiedFieldName(Class cls, String fieldName) {
return cls.getName() + "." + fieldName;
}
/**
* Write {@code IgniteUuid} instance.
*
* @param out Writer.
* @param val Value.
*/
public static void writeIgniteUuid(BinaryRawWriter out, @Nullable IgniteUuid val) {
if (val != null) {
out.writeBoolean(true);
out.writeLong(val.globalId().getMostSignificantBits());
out.writeLong(val.globalId().getLeastSignificantBits());
out.writeLong(val.localId());
}
else
out.writeBoolean(false);
}
/**
* Read {@code IgniteUuid} instance.
*
* @param in Reader.
* @return Value.
*/
@Nullable public static IgniteUuid readIgniteUuid(BinaryRawReader in) {
if (in.readBoolean()) {
UUID globalId = new UUID(in.readLong(), in.readLong());
return new IgniteUuid(globalId, in.readLong());
}
else
return null;
}
/**
* Reconstructs string from UTF-8 bytes.
*
* @param arr array Byte array.
* @param off offset Offset in the array.
* @param len length Byte array lenght.
* @return string Resulting string.
*/
public static String utf8BytesToStr(byte[] arr, int off, int len) {
int c, charArrCnt = 0, total = off + len;
int c2, c3;
char[] res = new char[len];
// try reading ascii
while (off < total) {
c = (int)arr[off] & 0xff;
if (c > 127)
break;
off++;
res[charArrCnt++] = (char)c;
}
// read other
while (off < total) {
c = (int)arr[off] & 0xff;
switch (c >> 4) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
/* 0xxxxxxx*/
off++;
res[charArrCnt++] = (char)c;
break;
case 12:
case 13:
/* 110x xxxx 10xx xxxx*/
off += 2;
if (off > total)
throw new BinaryObjectException("Malformed input: partial character at end");
c2 = (int)arr[off - 1];
if ((c2 & 0xC0) != 0x80)
throw new BinaryObjectException("Malformed input around byte: " + off);
res[charArrCnt++] = (char)(((c & 0x1F) << 6) | (c2 & 0x3F));
break;
case 14:
/* 1110 xxxx 10xx xxxx 10xx xxxx */
off += 3;
if (off > total)
throw new BinaryObjectException("Malformed input: partial character at end");
c2 = (int)arr[off - 2];
c3 = (int)arr[off - 1];
if (((c2 & 0xC0) != 0x80) || ((c3 & 0xC0) != 0x80))
throw new BinaryObjectException("Malformed input around byte: " + (off - 1));
res[charArrCnt++] = (char)(((c & 0x0F) << 12) |
((c2 & 0x3F) << 6) | (c3 & 0x3F));
break;
default:
/* 10xx xxxx, 1111 xxxx */
throw new BinaryObjectException("Malformed input around byte: " + off);
}
}
return len == charArrCnt ? new String(res) : new String(res, 0, charArrCnt);
}
/**
* Converts the string into UTF-8 byte array considering special symbols like the surrogates.
*
* @param val String to convert.
* @return Resulting byte array.
*/
public static byte[] strToUtf8Bytes(String val) {
int strLen = val.length();
int utfLen = 0;
int c, cnt;
// Determine length of resulting byte array.
for (cnt = 0; cnt < strLen; cnt++) {
c = val.charAt(cnt);
if (c >= 0x0001 && c <= 0x007F)
utfLen++;
else if (c > 0x07FF)
utfLen += 3;
else
utfLen += 2;
}
byte[] arr = new byte[utfLen];
int position = 0;
for (cnt = 0; cnt < strLen; cnt++) {
c = val.charAt(cnt);
if (c >= 0x0001 && c <= 0x007F)
arr[position++] = (byte)c;
else if (c > 0x07FF) {
arr[position++] = (byte)(0xE0 | (c >> 12) & 0x0F);
arr[position++] = (byte)(0x80 | (c >> 6) & 0x3F);
arr[position++] = (byte)(0x80 | (c & 0x3F));
}
else {
arr[position++] = (byte)(0xC0 | ((c >> 6) & 0x1F));
arr[position++] = (byte)(0x80 | (c & 0x3F));
}
}
return arr;
}
/**
* Create binary type proxy.
*
* @param ctx Context.
* @param obj Binary object.
* @return Binary type proxy.
*/
public static BinaryType typeProxy(BinaryContext ctx, BinaryObjectEx obj) {
if (ctx == null)
throw new BinaryObjectException("BinaryContext is not set for the object.");
String clsName = obj instanceof BinaryEnumObjectImpl ? ((BinaryEnumObjectImpl)obj).className() : null;
return new BinaryTypeProxy(ctx, obj.typeId(), clsName);
}
/**
* Create binary type which is used by users.
*
* @param ctx Context.
* @param obj Binary object.
* @return Binary type.
*/
public static BinaryType type(BinaryContext ctx, BinaryObjectEx obj) {
if (ctx == null)
throw new BinaryObjectException("BinaryContext is not set for the object.");
return ctx.metadata(obj.typeId());
}
/**
* Get predefined write-replacer associated with class.
*
* @param cls Class.
* @return Write replacer.
*/
public static BinaryWriteReplacer writeReplacer(Class cls) {
return cls != null ? CLS_TO_WRITE_REPLACER.get(cls) : null;
}
/**
* Checks enum values mapping.
*
* @param typeName Name of the type.
* @param enumValues Enum name to ordinal mapping.
* @throws BinaryObjectException
*/
public static void validateEnumValues(String typeName, @Nullable Map<String, Integer> enumValues)
throws BinaryObjectException {
if (enumValues == null)
return;
Map<Integer, String> tmpMap = new LinkedHashMap<>(enumValues.size());
for (Map.Entry<String, Integer> e: enumValues.entrySet()) {
String prevName = tmpMap.put(e.getValue(), e.getKey());
if (prevName != null)
throw new BinaryObjectException("Conflicting enum values. Name '" + e.getKey() +
"' uses ordinal value (" + e.getValue() +
") that is also used for name '" + prevName +
"' [typeName='" + typeName + "']");
}
}
/**
* Merges enum value mappings and checks for conflicts.
*
* Possible conflicts:
* - same name is used for different ordinal values.
* - ordinal value is used more than once.
*
* @param typeName Name of the type.
* @param oldValues Old enum value mapping.
* @param newValues New enum value mapping.
* @throws BinaryObjectException in case of name or value conflict.
*/
public static Map<String, Integer> mergeEnumValues(String typeName,
@Nullable Map<String, Integer> oldValues,
Map<String, Integer> newValues)
throws BinaryObjectException {
assert newValues != null;
int size = (oldValues != null) ? oldValues.size() + newValues.size() : newValues.size();
Map<Integer, String> revMap = new LinkedHashMap<>(size);
Map<String, Integer> mergedMap = new LinkedHashMap<>(size);
if (oldValues != null) {
//assuming that old values were validated earlier once.
for (Map.Entry<String, Integer> e : oldValues.entrySet()) {
revMap.put(e.getValue(), e.getKey());
mergedMap.put(e.getKey(), e.getValue());
}
}
for (Map.Entry<String, Integer> e: newValues.entrySet()) {
String prevName = revMap.put(e.getValue(), e.getKey());
if (prevName != null && !prevName.equals(e.getKey()))
throw new BinaryObjectException("Conflicting enum values. Name '" + e.getKey() +
"' uses ordinal value (" + e.getValue() +
") that is also used for name '" + prevName +
"' [typeName='" + typeName + "']");
Integer prevVal = mergedMap.put(e.getKey(), e.getValue());
if (prevVal != null && !prevVal.equals(e.getValue()))
throw new BinaryObjectException("Conflicting enum values. Value (" + e.getValue() +
") has name '" + e.getKey() +
"' that is also used for value '" + prevVal +
"' [typeName='" + typeName + "']");
}
return mergedMap;
}
/**
* Enum type.
*/
private static class EnumType {
/** Type ID. */
private final int typeId;
/** Class name. */
private final String clsName;
/**
* Constructor.
*
* @param typeId Type ID.
* @param clsName Class name.
*/
public EnumType(int typeId, @Nullable String clsName) {
assert typeId != GridBinaryMarshaller.UNREGISTERED_TYPE_ID && clsName == null ||
typeId == GridBinaryMarshaller.UNREGISTERED_TYPE_ID && clsName != null;
this.typeId = typeId;
this.clsName = clsName;
}
}
}