http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/1f2be19d/modules/core/src/main/java/org/apache/ignite/internal/portable/GridPortableBuilderImpl.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/GridPortableBuilderImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/GridPortableBuilderImpl.java new file mode 100644 index 0000000..95f8fd2 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/GridPortableBuilderImpl.java @@ -0,0 +1,519 @@ +/* + * 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.portable; + +import org.apache.ignite.internal.processors.cache.portable.*; +import org.apache.ignite.internal.util.*; +import org.apache.ignite.internal.util.typedef.internal.*; +import org.apache.ignite.portable.*; + +import org.jetbrains.annotations.*; + +import java.util.*; + +import static org.apache.ignite.internal.portable.GridPortableMarshaller.*; + +/** + * + */ +public class GridPortableBuilderImpl implements PortableBuilder { + /** */ + private static final Object REMOVED_FIELD_MARKER = new Object(); + + /** */ + private final GridPortableContext ctx; + + /** */ + private final int typeId; + + /** May be null. */ + private String typeName; + + /** May be null. */ + private String clsNameToWrite; + + /** */ + private boolean registeredType = true; + + /** */ + private Map<String, Object> assignedVals; + + /** */ + private Map<Integer, Object> readCache; + + /** Position of object in source array, or -1 if object is not created from PortableObject. */ + private final int start; + + /** Total header length */ + private final int hdrLen; + + /** + * Context of PortableObject reading process. Or {@code null} if object is not created from PortableObject. + */ + private final GridPortableBuilderReader reader; + + /** */ + private int hashCode; + + /** + * @param clsName Class name. + * @param ctx Portable context. + */ + public GridPortableBuilderImpl(GridPortableContext ctx, String clsName) { + this(ctx, ctx.typeId(clsName), GridPortableContext.typeName(clsName)); + } + + /** + * @param typeId Type ID. + * @param ctx Portable context. + */ + public GridPortableBuilderImpl(GridPortableContext ctx, int typeId) { + this(ctx, typeId, null); + } + + /** + * @param typeName Type name. + * @param ctx Context. + * @param typeId Type id. + */ + public GridPortableBuilderImpl(GridPortableContext ctx, int typeId, String typeName) { + this.typeId = typeId; + this.typeName = typeName; + this.ctx = ctx; + + start = -1; + reader = null; + hdrLen = DFLT_HDR_LEN; + + readCache = Collections.emptyMap(); + } + + /** + * @param obj Object to wrap. + */ + public GridPortableBuilderImpl(GridPortableObjectImpl obj) { + this(new GridPortableBuilderReader(obj), obj.start()); + + reader.registerObject(this); + } + + /** + * @param reader ctx + * @param start Start. + */ + GridPortableBuilderImpl(GridPortableBuilderReader reader, int start) { + this.reader = reader; + this.start = start; + + int typeId = reader.readIntAbsolute(start + TYPE_ID_POS); + ctx = reader.portableContext(); + hashCode = reader.readIntAbsolute(start + HASH_CODE_POS); + + if (typeId == UNREGISTERED_TYPE_ID) { + int mark = reader.position(); + + reader.position(start + CLS_NAME_POS); + + clsNameToWrite = reader.readString(); + + Class cls; + + try { + // TODO: GG-10396 - Is class loader needed here? + cls = U.forName(clsNameToWrite, null); + } + catch (ClassNotFoundException e) { + throw new PortableInvalidClassException("Failed to load the class: " + clsNameToWrite, e); + } + + this.typeId = ctx.descriptorForClass(cls).typeId(); + + registeredType = false; + + hdrLen = reader.position() - mark; + + reader.position(mark); + } + else { + this.typeId = typeId; + hdrLen = DFLT_HDR_LEN; + } + } + + /** {@inheritDoc} */ + @Override public PortableObject build() { + try (GridPortableWriterImpl writer = new GridPortableWriterImpl(ctx, 0, typeId, false)) { + + GridPortableBuilderSerializer serializationCtx = new GridPortableBuilderSerializer(); + + serializationCtx.registerObjectWriting(this, 0); + + serializeTo(writer, serializationCtx); + + byte[] arr = writer.array(); + + return new GridPortableObjectImpl(ctx, arr, 0); + } + } + + /** + * @param writer Writer. + * @param serializer Serializer. + */ + void serializeTo(GridPortableWriterImpl writer, GridPortableBuilderSerializer serializer) { + writer.doWriteByte(GridPortableMarshaller.OBJ); + writer.doWriteBoolean(true); + writer.doWriteInt(registeredType ? typeId : UNREGISTERED_TYPE_ID); + writer.doWriteInt(hashCode); + + // Length and raw offset. + writer.reserve(8); + + if (!registeredType) + writer.writeString(clsNameToWrite); + + + Set<Integer> remainsFlds = null; + + if (reader != null) { + Map<Integer, Object> assignedFldsById; + + if (assignedVals != null) { + assignedFldsById = U.newHashMap(assignedVals.size()); + + for (Map.Entry<String, Object> entry : assignedVals.entrySet()) { + int fldId = ctx.fieldId(typeId, entry.getKey()); + + assignedFldsById.put(fldId, entry.getValue()); + } + + remainsFlds = assignedFldsById.keySet(); + } + else + assignedFldsById = Collections.emptyMap(); + + int rawOff = start + reader.readIntAbsolute(start + RAW_DATA_OFF_POS); + + reader.position(start + hdrLen); + + int cpStart = -1; + + while (reader.position() < rawOff) { + int fldId = reader.readInt(); + + int len = reader.readInt(); + + if (assignedFldsById.containsKey(fldId)) { + if (cpStart >= 0) { + writer.write(reader.array(), cpStart, reader.position() - 4 - 4 - cpStart); + + cpStart = -1; + } + + Object assignedVal = assignedFldsById.remove(fldId); + + reader.skip(len); + + if (assignedVal != REMOVED_FIELD_MARKER) { + writer.writeInt(fldId); + + int lenPos = writer.reserveAndMark(4); + + serializer.writeValue(writer, assignedVal); + + writer.writeDelta(lenPos); + } + } + else { + if (len != 0 && GridPortableUtils.isPlainType(reader.readByte(0))) { + if (cpStart < 0) + cpStart = reader.position() - 4 - 4; + + reader.skip(len); + } + else { + if (cpStart >= 0) { + writer.write(reader.array(), cpStart, reader.position() - 4 - cpStart); + + cpStart = -1; + } + else + writer.writeInt(fldId); + + Object val; + + if (len == 0) + val = null; + else if (readCache == null) { + int savedPos = reader.position(); + + val = reader.parseValue(); + + assert reader.position() == savedPos + len; + } + else { + val = readCache.get(fldId); + + reader.skip(len); + } + + int lenPos = writer.reserveAndMark(4); + + serializer.writeValue(writer, val); + + writer.writeDelta(lenPos); + } + } + } + + if (cpStart >= 0) + writer.write(reader.array(), cpStart, reader.position() - cpStart); + } + + if (assignedVals != null && (remainsFlds == null || !remainsFlds.isEmpty())) { + boolean metadataEnabled = ctx.isMetaDataEnabled(typeId); + + PortableMetadata metadata = null; + + if (metadataEnabled) + metadata = ctx.metaData(typeId); + + Map<String, String> newFldsMetadata = null; + + for (Map.Entry<String, Object> entry : assignedVals.entrySet()) { + Object val = entry.getValue(); + + if (val == REMOVED_FIELD_MARKER) + continue; + + String name = entry.getKey(); + + int fldId = ctx.fieldId(typeId, name); + + if (remainsFlds != null && !remainsFlds.contains(fldId)) + continue; + + writer.writeInt(fldId); + + int lenPos = writer.reserveAndMark(4); + + serializer.writeValue(writer, val); + + writer.writeDelta(lenPos); + + if (metadataEnabled) { + String oldFldTypeName = metadata == null ? null : metadata.fieldTypeName(name); + + String newFldTypeName; + + if (val instanceof GridPortableValueWithType) + newFldTypeName = ((GridPortableValueWithType)val).typeName(); + else { + byte type = GridPortableUtils.typeByClass(val.getClass()); + + newFldTypeName = CacheObjectPortableProcessorImpl.fieldTypeName(type); + } + + if (oldFldTypeName == null) { + // It's a new field, we have to add it to metadata. + + if (newFldsMetadata == null) + newFldsMetadata = new HashMap<>(); + + newFldsMetadata.put(name, newFldTypeName); + } + else { + if (!"Object".equals(oldFldTypeName) && !oldFldTypeName.equals(newFldTypeName)) { + throw new PortableException( + "Wrong value has been set [" + + "typeName=" + (typeName == null ? metadata.typeName() : typeName) + + ", fieldName=" + name + + ", fieldType=" + oldFldTypeName + + ", assignedValueType=" + newFldTypeName + + ", assignedValue=" + (((GridPortableValueWithType)val).value()) + ']' + ); + } + } + } + } + + if (newFldsMetadata != null) { + String typeName = this.typeName; + + if (typeName == null) + typeName = metadata.typeName(); + + ctx.updateMetaData(typeId, typeName, newFldsMetadata); + } + } + + writer.writeRawOffsetIfNeeded(); + + if (reader != null) { + int rawOff = reader.readIntAbsolute(start + RAW_DATA_OFF_POS); + int len = reader.readIntAbsolute(start + TOTAL_LEN_POS); + + if (rawOff < len) + writer.write(reader.array(), rawOff, len - rawOff); + } + + writer.writeLength(); + } + + /** {@inheritDoc} */ + @Override public GridPortableBuilderImpl hashCode(int hashCode) { + this.hashCode = hashCode; + + return this; + } + + /** + * + */ + private void ensureReadCacheInit() { + if (readCache == null) { + Map<Integer, Object> readCache = new HashMap<>(); + + int pos = start + hdrLen; + int end = start + reader.readIntAbsolute(start + RAW_DATA_OFF_POS); + + while (pos < end) { + int fieldId = reader.readIntAbsolute(pos); + + pos += 4; + + int len = reader.readIntAbsolute(pos); + + pos += 4; + + Object val = reader.getValueQuickly(pos, len); + + readCache.put(fieldId, val); + + pos += len; + } + + this.readCache = readCache; + } + } + + /** {@inheritDoc} */ + @Override public <F> F getField(String name) { + Object val; + + if (assignedVals != null && assignedVals.containsKey(name)) { + val = assignedVals.get(name); + + if (val == REMOVED_FIELD_MARKER) + return null; + } + else { + ensureReadCacheInit(); + + int fldId = ctx.fieldId(typeId, name); + + val = readCache.get(fldId); + } + + return (F)GridPortableUtils.unwrapLazy(val); + } + + /** {@inheritDoc} */ + @Override public PortableBuilder setField(String name, Object val) { + GridArgumentCheck.notNull(val, "val"); + + if (assignedVals == null) + assignedVals = new LinkedHashMap<>(); + + Object oldVal = assignedVals.put(name, val); + + if (oldVal instanceof GridPortableValueWithType) { + ((GridPortableValueWithType)oldVal).value(val); + + assignedVals.put(name, oldVal); + } + + return this; + } + + /** {@inheritDoc} */ + @Override public <T> PortableBuilder setField(String name, @Nullable T val, Class<? super T> type) { + if (assignedVals == null) + assignedVals = new LinkedHashMap<>(); + + //int fldId = ctx.fieldId(typeId, fldName); + + assignedVals.put(name, new GridPortableValueWithType(GridPortableUtils.typeByClass(type), val)); + + return this; + } + + /** {@inheritDoc} */ + @Override public PortableBuilder setField(String name, @Nullable PortableBuilder builder) { + if (builder == null) + return setField(name, null, Object.class); + else + return setField(name, (Object)builder); + } + + /** + * Removes field from portable object. + * + * @param name Field name. + * @return {@code this} instance for chaining. + */ + @Override public GridPortableBuilderImpl removeField(String name) { + if (assignedVals == null) + assignedVals = new LinkedHashMap<>(); + + assignedVals.put(name, REMOVED_FIELD_MARKER); + + return this; + } + + /** + * Creates builder initialized by specified portable object. + * + * @param obj Portable object to initialize builder. + * @return New builder. + */ + public static GridPortableBuilderImpl wrap(PortableObject obj) { + GridPortableObjectImpl heapObj; + + if (obj instanceof GridPortableObjectOffheapImpl) + heapObj = (GridPortableObjectImpl)((GridPortableObjectOffheapImpl)obj).heapCopy(); + else + heapObj = (GridPortableObjectImpl)obj; + + return new GridPortableBuilderImpl(heapObj); + } + + /** + * @return Object start position in source array. + */ + int start() { + return start; + } + + /** + * @return Object type id. + */ + int typeId() { + return typeId; + } +}
http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/1f2be19d/modules/core/src/main/java/org/apache/ignite/internal/portable/GridPortableBuilderReader.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/GridPortableBuilderReader.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/GridPortableBuilderReader.java new file mode 100644 index 0000000..362c7db --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/GridPortableBuilderReader.java @@ -0,0 +1,775 @@ +/* + * 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.portable; + +import org.apache.ignite.portable.*; + +import java.sql.*; +import java.util.Date; +import java.util.*; + +import static java.nio.charset.StandardCharsets.*; +import static org.apache.ignite.internal.portable.GridPortableMarshaller.*; + +/** + * + */ +class GridPortableBuilderReader { + /** */ + private static final GridPortablePrimitives PRIM = GridPortablePrimitives.get(); + + /** */ + private final Map<Integer, GridPortableBuilderImpl> objMap = new HashMap<>(); + + /** */ + private final GridPortableContext ctx; + + /** */ + private final GridGridPortableReaderImpl reader; + + /** */ + private byte[] arr; + + /** */ + private int pos; + + /** + * @param objImpl Portable object + */ + GridPortableBuilderReader(GridPortableObjectImpl objImpl) { + ctx = objImpl.context(); + arr = objImpl.array(); + pos = objImpl.start(); + + // TODO: GG-10396 - Is class loader needed here? + reader = new GridGridPortableReaderImpl(portableContext(), arr, pos, null); + } + + /** + * @return Portable context. + */ + public GridPortableContext portableContext() { + return ctx; + } + + /** + * @param obj Mutable portable object. + */ + public void registerObject(GridPortableBuilderImpl obj) { + objMap.put(obj.start(), obj); + } + + /** + * @return Read int value. + */ + public int readInt() { + int res = readInt(0); + + pos += 4; + + return res; + } + + /** + * @return Read int value. + */ + public byte readByte() { + return arr[pos++]; + } + + /** + * @return Read boolean value. + */ + public boolean readBoolean() { + return readByte() == 1; + } + + /** + * @return Read int value. + */ + public byte readByte(int off) { + return arr[pos + off]; + } + + /** + * @param off Offset related to {@link #pos} + * @return Read int value. + */ + public int readInt(int off) { + return PRIM.readInt(arr, pos + off); + } + + /** + * @param pos Position in the source array. + * @return Read int value. + */ + public int readIntAbsolute(int pos) { + return PRIM.readInt(arr, pos); + } + + /** + * @return Read length of array. + */ + public int readLength() { + return PRIM.readInt(arr, pos); + } + + /** + * Read string length. + * + * @return String length. + */ + public int readStringLength() { + boolean utf = PRIM.readBoolean(arr, pos); + + int arrLen = PRIM.readInt(arr, pos + 1); + + return 1 + (utf ? arrLen : arrLen << 1); + } + + /** + * Reads string. + * + * @return String. + */ + public String readString() { + byte flag = readByte(); + + if (flag == NULL) + return null; + + if (flag != STRING) + throw new PortableException("Failed to deserialize String."); + + boolean convert = readBoolean(); + int len = readInt(); + + String str; + + if (convert) { + str = new String(arr, pos, len, UTF_8); + + pos += len; + } + else { + str = String.valueOf(PRIM.readCharArray(arr, pos, len)); + + pos += len << 1; + } + + return str; + } + + /** + * + */ + public void skipValue() { + byte type = arr[pos++]; + + int len; + + switch (type) { + case GridPortableMarshaller.NULL: + return; + + case GridPortableMarshaller.OBJ: + pos += readInt(GridPortableMarshaller.TOTAL_LEN_POS - 1) - 1; + + return; + + case GridPortableMarshaller.BOOLEAN: + case GridPortableMarshaller.BYTE: + len = 1; + break; + + case GridPortableMarshaller.CHAR: + case GridPortableMarshaller.SHORT: + len = 2; + + break; + + case GridPortableMarshaller.HANDLE: + case GridPortableMarshaller.FLOAT: + case GridPortableMarshaller.INT: + len = 4; + + break; + + case GridPortableMarshaller.ENUM: + //skipping type id and ordinal value + len = 8; + + break; + + case GridPortableMarshaller.LONG: + case GridPortableMarshaller.DOUBLE: + len = 8; + + break; + + case GridPortableMarshaller.BYTE_ARR: + case GridPortableMarshaller.BOOLEAN_ARR: + len = 4 + readLength(); + + break; + + case GridPortableMarshaller.STRING: + len = 4 + readStringLength(); + + break; + + case GridPortableMarshaller.DECIMAL: + len = /** scale */ 4 + /** mag len */ 4 + /** mag bytes count */ readInt(4); + + break; + + case GridPortableMarshaller.UUID: + len = 8 + 8; + + break; + + case GridPortableMarshaller.DATE: + len = 8 + 4; + + break; + + case GridPortableMarshaller.CHAR_ARR: + case GridPortableMarshaller.SHORT_ARR: + len = 4 + readLength() * 2; + + break; + + case GridPortableMarshaller.INT_ARR: + case GridPortableMarshaller.FLOAT_ARR: + len = 4 + readLength() * 4; + + break; + + case GridPortableMarshaller.LONG_ARR: + case GridPortableMarshaller.DOUBLE_ARR: + len = 4 + readLength() * 8; + + break; + + case GridPortableMarshaller.DECIMAL_ARR: + case GridPortableMarshaller.DATE_ARR: + case GridPortableMarshaller.OBJ_ARR: + case GridPortableMarshaller.ENUM_ARR: + case GridPortableMarshaller.UUID_ARR: + case GridPortableMarshaller.STRING_ARR: { + int size = readInt(); + + for (int i = 0; i < size; i++) + skipValue(); + + return; + } + + case GridPortableMarshaller.COL: { + int size = readInt(); + + pos++; // skip collection type + + for (int i = 0; i < size; i++) + skipValue(); + + return; + } + + case GridPortableMarshaller.MAP: { + int size = readInt(); + + pos++; // skip collection type + + for (int i = 0; i < size; i++) { + skipValue(); // skip key. + skipValue(); // skip value. + } + + return; + } + + case GridPortableMarshaller.MAP_ENTRY: + skipValue(); + skipValue(); + + return; + + case GridPortableMarshaller.PORTABLE_OBJ: + len = readInt() + 4; + + break; + + default: + throw new PortableException("Invalid flag value: " + type); + } + + pos += len; + } + + /** + * @param pos Position. + * @param len Length. + * @return Object. + */ + public Object getValueQuickly(int pos, int len) { + byte type = arr[pos]; + + switch (type) { + case GridPortableMarshaller.NULL: + return null; + + case GridPortableMarshaller.HANDLE: { + int objStart = pos - readIntAbsolute(pos + 1); + + GridPortableBuilderImpl res = objMap.get(objStart); + + if (res == null) { + res = new GridPortableBuilderImpl(this, objStart); + + objMap.put(objStart, res); + } + + return res; + } + + case GridPortableMarshaller.OBJ: { + GridPortableBuilderImpl res = objMap.get(pos); + + if (res == null) { + res = new GridPortableBuilderImpl(this, pos); + + objMap.put(pos, res); + } + + return res; + } + + case GridPortableMarshaller.BYTE: + return arr[pos + 1]; + + case GridPortableMarshaller.SHORT: + return PRIM.readShort(arr, pos + 1); + + case GridPortableMarshaller.INT: + return PRIM.readInt(arr, pos + 1); + + case GridPortableMarshaller.LONG: + return PRIM.readLong(arr, pos + 1); + + case GridPortableMarshaller.FLOAT: + return PRIM.readFloat(arr, pos + 1); + + case GridPortableMarshaller.DOUBLE: + return PRIM.readDouble(arr, pos + 1); + + case GridPortableMarshaller.CHAR: + return PRIM.readChar(arr, pos + 1); + + case GridPortableMarshaller.BOOLEAN: + return arr[pos + 1] != 0; + + case GridPortableMarshaller.DECIMAL: + case GridPortableMarshaller.STRING: + case GridPortableMarshaller.UUID: + case GridPortableMarshaller.DATE: + case GridPortableMarshaller.BYTE_ARR: + case GridPortableMarshaller.SHORT_ARR: + case GridPortableMarshaller.INT_ARR: + case GridPortableMarshaller.LONG_ARR: + case GridPortableMarshaller.FLOAT_ARR: + case GridPortableMarshaller.DOUBLE_ARR: + case GridPortableMarshaller.CHAR_ARR: + case GridPortableMarshaller.BOOLEAN_ARR: + case GridPortableMarshaller.DECIMAL_ARR: + case GridPortableMarshaller.DATE_ARR: + case GridPortableMarshaller.UUID_ARR: + case GridPortableMarshaller.STRING_ARR: + return new GridPortablePlainLazyValue(this, pos, len); + + case GridPortableMarshaller.COL: + case GridPortableMarshaller.OBJ_ARR: + case GridPortableMarshaller.MAP: + case GridPortableMarshaller.ENUM_ARR: + case GridPortableMarshaller.MAP_ENTRY: + return new LazyCollection(pos); + + case GridPortableMarshaller.ENUM: { + if (len == 1) { + assert readByte(pos) == GridPortableMarshaller.NULL; + + return null; + } + + int mark = position(); + position(pos + 1); + + GridPortableBuilderEnum builderEnum = new GridPortableBuilderEnum(this); + + position(mark); + + return builderEnum; + } + + case GridPortableMarshaller.PORTABLE_OBJ: { + int size = readIntAbsolute(pos + 1); + + int start = readIntAbsolute(pos + 4 + size); + + GridPortableObjectImpl portableObj = new GridPortableObjectImpl(ctx, arr, pos + 4 + start); + + return new GridPortablePlainPortableObject(portableObj); + } + + default: + throw new PortableException("Invalid flag value: " + type); + } + } + + /** + * @return Parsed value. + */ + public Object parseValue() { + int valPos = pos; + + byte type = arr[pos++]; + + int plainLazyValLen; + + switch (type) { + case GridPortableMarshaller.NULL: + return null; + + case GridPortableMarshaller.HANDLE: { + int objStart = pos - 1 - readInt(); + + GridPortableBuilderImpl res = objMap.get(objStart); + + if (res == null) { + res = new GridPortableBuilderImpl(this, objStart); + + objMap.put(objStart, res); + } + + return res; + } + + case GridPortableMarshaller.OBJ: { + pos--; + + GridPortableBuilderImpl res = objMap.get(pos); + + if (res == null) { + res = new GridPortableBuilderImpl(this, pos); + + objMap.put(pos, res); + } + + pos += readInt(GridPortableMarshaller.TOTAL_LEN_POS); + + return res; + } + + case GridPortableMarshaller.BYTE: + return arr[pos++]; + + case GridPortableMarshaller.SHORT: { + Object res = PRIM.readShort(arr, pos); + pos += 2; + return res; + } + + case GridPortableMarshaller.INT: + return readInt(); + + case GridPortableMarshaller.LONG: + plainLazyValLen = 8; + + break; + + case GridPortableMarshaller.FLOAT: + plainLazyValLen = 4; + + break; + + case GridPortableMarshaller.DOUBLE: + plainLazyValLen = 8; + + break; + + case GridPortableMarshaller.CHAR: + plainLazyValLen = 2; + + break; + + case GridPortableMarshaller.BOOLEAN: + return arr[pos++] != 0; + + case GridPortableMarshaller.DECIMAL: + plainLazyValLen = /** scale */ 4 + /** mag len */ 4 + /** mag bytes count */ readInt(4); + + break; + + case GridPortableMarshaller.STRING: + plainLazyValLen = 4 + readStringLength(); + + break; + + case GridPortableMarshaller.UUID: + plainLazyValLen = 8 + 8; + + break; + + case GridPortableMarshaller.DATE: + plainLazyValLen = 8 + 4; + + break; + + case GridPortableMarshaller.BYTE_ARR: + plainLazyValLen = 4 + readLength(); + + break; + + case GridPortableMarshaller.SHORT_ARR: + plainLazyValLen = 4 + readLength() * 2; + + break; + + case GridPortableMarshaller.INT_ARR: + plainLazyValLen = 4 + readLength() * 4; + + break; + + case GridPortableMarshaller.LONG_ARR: + plainLazyValLen = 4 + readLength() * 8; + + break; + + case GridPortableMarshaller.FLOAT_ARR: + plainLazyValLen = 4 + readLength() * 4; + + break; + + case GridPortableMarshaller.DOUBLE_ARR: + plainLazyValLen = 4 + readLength() * 8; + + break; + + case GridPortableMarshaller.CHAR_ARR: + plainLazyValLen = 4 + readLength() * 2; + + break; + + case GridPortableMarshaller.BOOLEAN_ARR: + plainLazyValLen = 4 + readLength(); + + break; + + case GridPortableMarshaller.OBJ_ARR: + return new GridPortableObjectArrayLazyValue(this); + + case GridPortableMarshaller.DATE_ARR: { + int size = readInt(); + + Date[] res = new Date[size]; + + for (int i = 0; i < res.length; i++) { + byte flag = arr[pos++]; + + if (flag == GridPortableMarshaller.NULL) continue; + + if (flag != GridPortableMarshaller.DATE) + throw new PortableException("Invalid flag value: " + flag); + + long time = PRIM.readLong(arr, pos); + + pos += 8; + + if (ctx.isUseTimestamp()) { + Timestamp ts = new Timestamp(time); + + ts.setNanos(ts.getNanos() + readInt()); + + res[i] = ts; + } + else { + res[i] = new Date(time); + + pos += 4; + } + } + + return res; + } + + case GridPortableMarshaller.UUID_ARR: + case GridPortableMarshaller.STRING_ARR: + case GridPortableMarshaller.DECIMAL_ARR: { + int size = readInt(); + + for (int i = 0; i < size; i++) { + byte flag = arr[pos++]; + + if (flag == GridPortableMarshaller.UUID) + pos += 8 + 8; + else if (flag == GridPortableMarshaller.STRING) + pos += 4 + readStringLength(); + else if (flag == GridPortableMarshaller.DECIMAL) + pos += 4 + readLength(); + else + assert flag == GridPortableMarshaller.NULL; + } + + return new GridPortablePlainLazyValue(this, valPos, pos - valPos); + } + + case GridPortableMarshaller.COL: { + int size = readInt(); + byte colType = arr[pos++]; + + switch (colType) { + case GridPortableMarshaller.USER_COL: + case GridPortableMarshaller.ARR_LIST: + return new GridPortableLazyArrayList(this, size); + + case GridPortableMarshaller.LINKED_LIST: + return new GridPortableLazyLinkedList(this, size); + + case GridPortableMarshaller.HASH_SET: + case GridPortableMarshaller.LINKED_HASH_SET: + case GridPortableMarshaller.TREE_SET: + case GridPortableMarshaller.CONC_SKIP_LIST_SET: + return new GridPortableLazySet(this, size); + } + + throw new PortableException("Unknown collection type: " + colType); + } + + case GridPortableMarshaller.MAP: + return GridPortableLazyMap.parseMap(this); + + case GridPortableMarshaller.ENUM: + return new GridPortableBuilderEnum(this); + + case GridPortableMarshaller.ENUM_ARR: + return new GridPortableEnumArrayLazyValue(this); + + case GridPortableMarshaller.MAP_ENTRY: + return new GridPortableLazyMapEntry(this); + + case GridPortableMarshaller.PORTABLE_OBJ: { + int size = readInt(); + + pos += size; + + int start = readInt(); + + GridPortableObjectImpl portableObj = new GridPortableObjectImpl(ctx, arr, + pos - 4 - size + start); + + return new GridPortablePlainPortableObject(portableObj); + } + + + default: + throw new PortableException("Invalid flag value: " + type); + } + + GridPortablePlainLazyValue res = new GridPortablePlainLazyValue(this, valPos, 1 + plainLazyValLen); + + pos += plainLazyValLen; + + return res; + } + + /** + * @return Array. + */ + public byte[] array() { + return arr; + } + + /** + * @return Position of reader. + */ + public int position() { + return pos; + } + + /** + * @param pos New pos. + */ + public void position(int pos) { + this.pos = pos; + } + + /** + * @param n Number of bytes to skip. + */ + public void skip(int n) { + pos += n; + } + + /** + * @return Reader. + */ + GridGridPortableReaderImpl reader() { + return reader; + } + + /** + * + */ + private class LazyCollection implements GridPortableLazyValue { + /** */ + private final int valOff; + + /** */ + private Object col; + + /** + * @param valOff Value. + */ + protected LazyCollection(int valOff) { + this.valOff = valOff; + } + + /** + * @return Object. + */ + private Object wrappedCollection() { + if (col == null) { + position(valOff); + + col = parseValue(); + } + + return col; + } + + /** {@inheritDoc} */ + @Override public void writeTo(GridPortableWriterImpl writer, GridPortableBuilderSerializer ctx) { + ctx.writeValue(writer, wrappedCollection()); + } + + /** {@inheritDoc} */ + @Override public Object value() { + return GridPortableUtils.unwrapLazy(wrappedCollection()); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/1f2be19d/modules/core/src/main/java/org/apache/ignite/internal/portable/GridPortableBuilderSerializationAware.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/GridPortableBuilderSerializationAware.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/GridPortableBuilderSerializationAware.java new file mode 100644 index 0000000..8db2a68 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/GridPortableBuilderSerializationAware.java @@ -0,0 +1,29 @@ +/* + * 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.portable; + +/** + * + */ +interface GridPortableBuilderSerializationAware { + /** + * @param writer Writer. + * @param ctx Context. + */ + public void writeTo(GridPortableWriterImpl writer, GridPortableBuilderSerializer ctx); +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/1f2be19d/modules/core/src/main/java/org/apache/ignite/internal/portable/GridPortableBuilderSerializer.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/GridPortableBuilderSerializer.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/GridPortableBuilderSerializer.java new file mode 100644 index 0000000..ba7b556 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/GridPortableBuilderSerializer.java @@ -0,0 +1,210 @@ +/* + * 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.portable; + +import org.apache.ignite.internal.util.*; +import org.apache.ignite.portable.*; + +import java.util.*; + +/** + * + */ +class GridPortableBuilderSerializer { + /** */ + private final Map<GridPortableBuilderImpl, Integer> objToPos = new IdentityHashMap<>(); + + /** */ + private Map<PortableObject, GridPortableBuilderImpl> portableObjToWrapper; + + /** + * @param obj Mutable object. + * @param posInResArr Object position in the array. + */ + public void registerObjectWriting(GridPortableBuilderImpl obj, int posInResArr) { + objToPos.put(obj, posInResArr); + } + + /** + * @param writer Writer. + * @param val Value. + */ + public void writeValue(GridPortableWriterImpl writer, Object val) { + if (val == null) { + writer.writeByte(GridPortableMarshaller.NULL); + + return; + } + + if (val instanceof GridPortableBuilderSerializationAware) { + ((GridPortableBuilderSerializationAware)val).writeTo(writer, this); + + return; + } + + if (val instanceof GridPortableObjectEx) { + if (portableObjToWrapper == null) + portableObjToWrapper = new IdentityHashMap<>(); + + GridPortableBuilderImpl wrapper = portableObjToWrapper.get(val); + + if (wrapper == null) { + wrapper = GridPortableBuilderImpl.wrap((PortableObject)val); + + portableObjToWrapper.put((PortableObject)val, wrapper); + } + + val = wrapper; + } + + if (val instanceof GridPortableBuilderImpl) { + GridPortableBuilderImpl obj = (GridPortableBuilderImpl)val; + + Integer posInResArr = objToPos.get(obj); + + if (posInResArr == null) { + objToPos.put(obj, writer.outputStream().position()); + + obj.serializeTo(writer.newWriter(obj.typeId()), this); + } + else { + int handle = writer.outputStream().position() - posInResArr; + + writer.writeByte(GridPortableMarshaller.HANDLE); + writer.writeInt(handle); + } + + return; + } + + if (val.getClass().isEnum()) { + writer.writeByte(GridPortableMarshaller.ENUM); + writer.writeInt(writer.context().typeId(val.getClass().getName())); + writer.writeInt(((Enum)val).ordinal()); + + return; + } + + if (val instanceof Collection) { + Collection<?> c = (Collection<?>)val; + + writer.writeByte(GridPortableMarshaller.COL); + writer.writeInt(c.size()); + + byte colType; + + if (c instanceof GridConcurrentSkipListSet) + colType = GridPortableMarshaller.CONC_SKIP_LIST_SET; + else + colType = writer.context().collectionType(c.getClass()); + + + writer.writeByte(colType); + + for (Object obj : c) + writeValue(writer, obj); + + return; + } + + if (val instanceof Map) { + Map<?, ?> map = (Map<?, ?>)val; + + writer.writeByte(GridPortableMarshaller.MAP); + writer.writeInt(map.size()); + + writer.writeByte(writer.context().mapType(map.getClass())); + + for (Map.Entry<?, ?> entry : map.entrySet()) { + writeValue(writer, entry.getKey()); + writeValue(writer, entry.getValue()); + } + + return; + } + + Byte flag = GridPortableUtils.PLAIN_CLASS_TO_FLAG.get(val.getClass()); + + if (flag != null) { + GridPortableUtils.writePlainObject(writer, val); + + return; + } + + if (val instanceof Object[]) { + int compTypeId = writer.context().typeId(((Object[])val).getClass().getComponentType().getName()); + + if (val instanceof GridPortableBuilderEnum[]) { + writeArray(writer, GridPortableMarshaller.ENUM_ARR, (Object[])val, compTypeId); + + return; + } + + if (((Object[])val).getClass().getComponentType().isEnum()) { + Enum[] enumArr = (Enum[])val; + + writer.writeByte(GridPortableMarshaller.ENUM_ARR); + writer.writeInt(compTypeId); + writer.writeInt(enumArr.length); + + for (Enum anEnum : enumArr) + writeValue(writer, anEnum); + + return; + } + + writeArray(writer, GridPortableMarshaller.OBJ_ARR, (Object[])val, compTypeId); + + return; + } + + writer.doWriteObject(val, false); + } + + /** + * @param writer Writer. + * @param elementType Element type. + * @param arr The array. + * @param compTypeId Component type ID. + */ + public void writeArray(GridPortableWriterImpl writer, byte elementType, Object[] arr, int compTypeId) { + writer.writeByte(elementType); + writer.writeInt(compTypeId); + writer.writeInt(arr.length); + + for (Object obj : arr) + writeValue(writer, obj); + } + + /** + * @param writer Writer. + * @param elementType Element type. + * @param arr The array. + * @param clsName Component class name. + */ + public void writeArray(GridPortableWriterImpl writer, byte elementType, Object[] arr, String clsName) { + writer.writeByte(elementType); + writer.writeInt(GridPortableMarshaller.UNREGISTERED_TYPE_ID); + writer.writeString(clsName); + writer.writeInt(arr.length); + + for (Object obj : arr) + writeValue(writer, obj); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/1f2be19d/modules/core/src/main/java/org/apache/ignite/internal/portable/GridPortableClassDescriptor.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/GridPortableClassDescriptor.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/GridPortableClassDescriptor.java new file mode 100644 index 0000000..6dec53c --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/GridPortableClassDescriptor.java @@ -0,0 +1,1344 @@ +/* + * 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.portable; + +import org.apache.ignite.*; +import org.apache.ignite.internal.processors.cache.*; +import org.apache.ignite.internal.util.typedef.internal.*; +import org.apache.ignite.marshaller.*; +import org.apache.ignite.portable.*; + +import org.jetbrains.annotations.*; + +import java.io.*; +import java.lang.reflect.*; +import java.math.*; +import java.sql.*; +import java.util.*; +import java.util.Date; + +import static java.lang.reflect.Modifier.*; + +/** + * Portable class descriptor. + */ +class GridPortableClassDescriptor { + /** */ + private final GridPortableContext ctx; + + /** */ + private final Class<?> cls; + + /** */ + private final PortableSerializer serializer; + + /** */ + private final Mode mode; + + /** */ + private final boolean userType; + + /** */ + private final int typeId; + + /** */ + private final String typeName; + + /** */ + private final Constructor<?> ctor; + + /** */ + private final Collection<FieldInfo> fields; + + /** */ + private final Method writeReplaceMtd; + + /** */ + private final Method readResolveMtd; + + /** */ + private final boolean useTs; + + /** */ + private final Map<String, String> fieldsMeta; + + /** */ + private final boolean keepDeserialized; + + /** */ + private final boolean registered; + + /** */ + private final boolean excluded; + + /** + * @param ctx Context. + * @param cls Class. + * @param userType User type flag. + * @param typeId Type ID. + * @param typeName Type name. + * @param idMapper ID mapper. + * @param serializer Serializer. + * @param useTs Use timestamp flag. + * @param metaDataEnabled Metadata enabled flag. + * @param keepDeserialized Keep deserialized flag. + * @throws PortableException In case of error. + */ + GridPortableClassDescriptor( + GridPortableContext ctx, + Class<?> cls, + boolean userType, + int typeId, + String typeName, + @Nullable PortableIdMapper idMapper, + @Nullable PortableSerializer serializer, + boolean useTs, + boolean metaDataEnabled, + boolean keepDeserialized + ) throws PortableException { + this(ctx, cls, userType, typeId, typeName, idMapper, serializer, useTs, metaDataEnabled, keepDeserialized, + true); + } + + /** + * @param ctx Context. + * @param cls Class. + * @param userType User type flag. + * @param typeId Type ID. + * @param typeName Type name. + * @param idMapper ID mapper. + * @param serializer Serializer. + * @param useTs Use timestamp flag. + * @param metaDataEnabled Metadata enabled flag. + * @param keepDeserialized Keep deserialized flag. + * @param registered Whether typeId has been successfully registered by MarshallerContext or not. + * @throws PortableException In case of error. + */ + GridPortableClassDescriptor( + GridPortableContext ctx, + Class<?> cls, + boolean userType, + int typeId, + String typeName, + @Nullable PortableIdMapper idMapper, + @Nullable PortableSerializer serializer, + boolean useTs, + boolean metaDataEnabled, + boolean keepDeserialized, + boolean registered + ) throws PortableException { + assert ctx != null; + assert cls != null; + + this.ctx = ctx; + this.cls = cls; + this.userType = userType; + this.typeId = typeId; + this.typeName = typeName; + this.serializer = serializer; + this.useTs = useTs; + this.keepDeserialized = keepDeserialized; + this.registered = registered; + + excluded = MarshallerExclusions.isExcluded(cls); + + if (excluded) + mode = Mode.EXCLUSION; + else + mode = serializer != null ? Mode.PORTABLE : mode(cls); + + switch (mode) { + case BYTE: + case SHORT: + case INT: + case LONG: + case FLOAT: + case DOUBLE: + case CHAR: + case BOOLEAN: + case DECIMAL: + case STRING: + case UUID: + case DATE: + case BYTE_ARR: + case SHORT_ARR: + case INT_ARR: + case LONG_ARR: + case FLOAT_ARR: + case DOUBLE_ARR: + case CHAR_ARR: + case BOOLEAN_ARR: + case DECIMAL_ARR: + case STRING_ARR: + case UUID_ARR: + case DATE_ARR: + case OBJ_ARR: + case COL: + case MAP: + case MAP_ENTRY: + case PORTABLE_OBJ: + case ENUM: + case ENUM_ARR: + case CLASS: + case EXCLUSION: + ctor = null; + fields = null; + fieldsMeta = null; + + break; + + case PORTABLE: + case EXTERNALIZABLE: + ctor = constructor(cls); + fields = null; + fieldsMeta = null; + + break; + + case OBJECT: + assert idMapper != null; + + ctor = constructor(cls); + fields = new ArrayList<>(); + fieldsMeta = metaDataEnabled ? new HashMap<String, String>() : null; + + Collection<String> names = new HashSet<>(); + Collection<Integer> ids = new HashSet<>(); + + for (Class<?> c = cls; c != null && !c.equals(Object.class); c = c.getSuperclass()) { + for (Field f : c.getDeclaredFields()) { + int mod = f.getModifiers(); + + if (!isStatic(mod) && !isTransient(mod)) { + f.setAccessible(true); + + String name = f.getName(); + + if (!names.add(name)) + throw new PortableException("Duplicate field name: " + name); + + int fieldId = idMapper.fieldId(typeId, name); + + if (!ids.add(fieldId)) + throw new PortableException("Duplicate field ID: " + name); + + FieldInfo fieldInfo = new FieldInfo(f, fieldId); + + fields.add(fieldInfo); + + if (metaDataEnabled) + fieldsMeta.put(name, fieldInfo.fieldMode().typeName()); + } + } + } + + break; + + default: + // Should never happen. + throw new PortableException("Invalid mode: " + mode); + } + + if (mode == Mode.PORTABLE || mode == Mode.EXTERNALIZABLE || mode == Mode.OBJECT) { + readResolveMtd = U.findNonPublicMethod(cls, "readResolve"); + writeReplaceMtd = U.findNonPublicMethod(cls, "writeReplace"); + } + else { + readResolveMtd = null; + writeReplaceMtd = null; + } + } + + /** + * @return Described class. + */ + Class<?> describedClass() { + return cls; + } + + /** + * @return Type ID. + */ + int typeId() { + return typeId; + } + + /** + * @return Fields meta data. + */ + Map<String, String> fieldsMeta() { + return fieldsMeta; + } + + /** + * @return Use timestamp flag. + */ + boolean isUseTimestamp() { + return useTs; + } + + /** + * @return Keep deserialized flag. + */ + boolean keepDeserialized() { + return keepDeserialized; + } + + /** + * @return Whether typeId has been successfully registered by MarshallerContext or not. + */ + public boolean isRegistered() { + return registered; + } + + /** + * Checks whether the class values are explicitly excluded from marshalling. + * + * @return {@code true} if excluded, {@code false} otherwise. + */ + public boolean excluded() { + return excluded; + } + + /** + * @return portableWriteReplace() method + */ + @Nullable Method getWriteReplaceMethod() { + return writeReplaceMtd; + } + + /** + * @return portableReadResolve() method + */ + @Nullable Method getReadResolveMethod() { + return readResolveMtd; + } + + /** + * @param obj Object. + * @param writer Writer. + * @throws PortableException In case of error. + */ + void write(Object obj, GridPortableWriterImpl writer) throws PortableException { + assert obj != null; + assert writer != null; + + switch (mode) { + case BYTE: + writer.doWriteByte(GridPortableMarshaller.BYTE); + writer.doWriteByte((byte)obj); + + break; + + case SHORT: + writer.doWriteByte(GridPortableMarshaller.SHORT); + writer.doWriteShort((short)obj); + + break; + + case INT: + writer.doWriteByte(GridPortableMarshaller.INT); + writer.doWriteInt((int)obj); + + break; + + case LONG: + writer.doWriteByte(GridPortableMarshaller.LONG); + writer.doWriteLong((long)obj); + + break; + + case FLOAT: + writer.doWriteByte(GridPortableMarshaller.FLOAT); + writer.doWriteFloat((float)obj); + + break; + + case DOUBLE: + writer.doWriteByte(GridPortableMarshaller.DOUBLE); + writer.doWriteDouble((double)obj); + + break; + + case CHAR: + writer.doWriteByte(GridPortableMarshaller.CHAR); + writer.doWriteChar((char)obj); + + break; + + case BOOLEAN: + writer.doWriteByte(GridPortableMarshaller.BOOLEAN); + writer.doWriteBoolean((boolean)obj); + + break; + + case DECIMAL: + writer.doWriteDecimal((BigDecimal) obj); + + break; + + case STRING: + writer.doWriteString((String)obj); + + break; + + case UUID: + writer.doWriteUuid((UUID)obj); + + break; + + case DATE: + if (obj instanceof Timestamp) + writer.doWriteTimestamp((Timestamp)obj); + else + writer.doWriteDate((Date)obj); + + break; + + case BYTE_ARR: + writer.doWriteByteArray((byte[])obj); + + break; + + case SHORT_ARR: + writer.doWriteShortArray((short[])obj); + + break; + + case INT_ARR: + writer.doWriteIntArray((int[])obj); + + break; + + case LONG_ARR: + writer.doWriteLongArray((long[])obj); + + break; + + case FLOAT_ARR: + writer.doWriteFloatArray((float[])obj); + + break; + + case DOUBLE_ARR: + writer.doWriteDoubleArray((double[])obj); + + break; + + case CHAR_ARR: + writer.doWriteCharArray((char[])obj); + + break; + + case BOOLEAN_ARR: + writer.doWriteBooleanArray((boolean[])obj); + + break; + + case DECIMAL_ARR: + writer.doWriteDecimalArray((BigDecimal[])obj); + + break; + + case STRING_ARR: + writer.doWriteStringArray((String[])obj); + + break; + + case UUID_ARR: + writer.doWriteUuidArray((UUID[])obj); + + break; + + case DATE_ARR: + writer.doWriteDateArray((Date[])obj); + + break; + + case OBJ_ARR: + writer.doWriteObjectArray((Object[])obj); + + break; + + case COL: + writer.doWriteCollection((Collection<?>)obj); + + break; + + case MAP: + writer.doWriteMap((Map<?, ?>)obj); + + break; + + case MAP_ENTRY: + writer.doWriteMapEntry((Map.Entry<?, ?>)obj); + + break; + + case ENUM: + writer.doWriteEnum((Enum<?>)obj); + + break; + + case ENUM_ARR: + writer.doWriteEnumArray((Object[])obj); + + break; + + case CLASS: + writer.doWriteClass((Class)obj); + + break; + + case PORTABLE_OBJ: + writer.doWritePortableObject((GridPortableObjectImpl)obj); + + break; + + case PORTABLE: + if (writeHeader(obj, writer)) { + if (serializer != null) + serializer.writePortable(obj, writer); + else + ((PortableMarshalAware)obj).writePortable(writer); + + writer.writeRawOffsetIfNeeded(); + writer.writeLength(); + + if (obj.getClass() != GridPortableMetaDataImpl.class + && ctx.isMetaDataChanged(typeId, writer.metaDataHashSum())) { + GridPortableMetaDataCollector metaCollector = new GridPortableMetaDataCollector(typeName); + + if (serializer != null) + serializer.writePortable(obj, metaCollector); + else + ((PortableMarshalAware)obj).writePortable(metaCollector); + + ctx.updateMetaData(typeId, typeName, metaCollector.meta()); + } + } + + break; + + case EXTERNALIZABLE: + if (writeHeader(obj, writer)) { + try { + ((Externalizable)obj).writeExternal(writer); + } + catch (IOException e) { + throw new PortableException("Failed to write Externalizable object: " + obj, e); + } + + writer.writeLength(); + } + + break; + + case OBJECT: + if (writeHeader(obj, writer)) { + for (FieldInfo info : fields) + info.write(obj, writer); + + writer.writeRawOffsetIfNeeded(); + writer.writeLength(); + } + + break; + + default: + assert false : "Invalid mode: " + mode; + } + } + + /** + * @param reader Reader. + * @return Object. + * @throws PortableException If failed. + */ + Object read(GridGridPortableReaderImpl reader) throws PortableException { + assert reader != null; + + Object res; + + switch (mode) { + case PORTABLE: + res = newInstance(); + + reader.setHandler(res); + + if (serializer != null) + serializer.readPortable(res, reader); + else + ((PortableMarshalAware)res).readPortable(reader); + + break; + + case EXTERNALIZABLE: + res = newInstance(); + + reader.setHandler(res); + + try { + ((Externalizable)res).readExternal(reader); + } + catch (IOException | ClassNotFoundException e) { + throw new PortableException("Failed to read Externalizable object: " + + res.getClass().getName(), e); + } + + break; + + case OBJECT: + res = newInstance(); + + reader.setHandler(res); + + for (FieldInfo info : fields) + info.read(res, reader); + + break; + + default: + assert false : "Invalid mode: " + mode; + + return null; + } + + if (readResolveMtd != null) { + try { + res = readResolveMtd.invoke(res); + + reader.setHandler(res); + } + catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + catch (InvocationTargetException e) { + if (e.getTargetException() instanceof PortableException) + throw (PortableException)e.getTargetException(); + + throw new PortableException("Failed to execute readResolve() method on " + res, e); + } + } + + return res; + } + + /** + * @param obj Object. + * @param writer Writer. + * @return Whether further write is needed. + */ + private boolean writeHeader(Object obj, GridPortableWriterImpl writer) { + int handle = writer.handle(obj); + + if (handle >= 0) { + writer.doWriteByte(GridPortableMarshaller.HANDLE); + writer.doWriteInt(handle); + + return false; + } + else { + int pos = writer.position(); + + writer.doWriteByte(GridPortableMarshaller.OBJ); + writer.doWriteBoolean(userType); + writer.doWriteInt(registered ? typeId : GridPortableMarshaller.UNREGISTERED_TYPE_ID); + writer.doWriteInt(obj instanceof CacheObjectImpl ? 0 : obj.hashCode()); + + // For length and raw offset. + int reserved = writer.reserve(8); + + // Class name in case if typeId registration is failed. + if (!registered) + writer.doWriteString(cls.getName()); + + int current = writer.position(); + int len = current - pos; + + // Default raw offset (equal to header length). + writer.position(reserved + 4); + writer.doWriteInt(len); + writer.position(current); + + return true; + } + } + + /** + * @return Instance. + * @throws PortableException In case of error. + */ + private Object newInstance() throws PortableException { + assert ctor != null; + + try { + return ctor.newInstance(); + } + catch (InstantiationException | InvocationTargetException | IllegalAccessException e) { + throw new PortableException("Failed to instantiate instance: " + cls, e); + } + } + + /** + * @param cls Class. + * @return Constructor. + * @throws PortableException If constructor doesn't exist. + */ + @Nullable private static Constructor<?> constructor(Class<?> cls) throws PortableException { + assert cls != null; + + try { + Constructor<?> ctor = U.forceEmptyConstructor(cls); + + ctor.setAccessible(true); + + return ctor; + } + catch (IgniteCheckedException e) { + throw new PortableException("Failed to get constructor for class: " + cls.getName(), e); + } + } + + /** + * @param cls Class. + * @return Mode. + */ + @SuppressWarnings("IfMayBeConditional") + private static Mode mode(Class<?> cls) { + assert cls != null; + + if (cls == byte.class || cls == Byte.class) + return Mode.BYTE; + else if (cls == short.class || cls == Short.class) + return Mode.SHORT; + else if (cls == int.class || cls == Integer.class) + return Mode.INT; + else if (cls == long.class || cls == Long.class) + return Mode.LONG; + else if (cls == float.class || cls == Float.class) + return Mode.FLOAT; + else if (cls == double.class || cls == Double.class) + return Mode.DOUBLE; + else if (cls == char.class || cls == Character.class) + return Mode.CHAR; + else if (cls == boolean.class || cls == Boolean.class) + return Mode.BOOLEAN; + else if (cls == BigDecimal.class) + return Mode.DECIMAL; + else if (cls == String.class) + return Mode.STRING; + else if (cls == UUID.class) + return Mode.UUID; + else if (cls == Timestamp.class || cls == Date.class) + return Mode.DATE; + else if (cls == byte[].class) + return Mode.BYTE_ARR; + else if (cls == short[].class) + return Mode.SHORT_ARR; + else if (cls == int[].class) + return Mode.INT_ARR; + else if (cls == long[].class) + return Mode.LONG_ARR; + else if (cls == float[].class) + return Mode.FLOAT_ARR; + else if (cls == double[].class) + return Mode.DOUBLE_ARR; + else if (cls == char[].class) + return Mode.CHAR_ARR; + else if (cls == boolean[].class) + return Mode.BOOLEAN_ARR; + else if (cls == BigDecimal[].class) + return Mode.DECIMAL_ARR; + else if (cls == String[].class) + return Mode.STRING_ARR; + else if (cls == UUID[].class) + return Mode.UUID_ARR; + else if (cls == Date[].class) + return Mode.DATE_ARR; + else if (cls.isArray()) + return cls.getComponentType().isEnum() ? Mode.ENUM_ARR : Mode.OBJ_ARR; + else if (cls == GridPortableObjectImpl.class) + return Mode.PORTABLE_OBJ; + else if (PortableMarshalAware.class.isAssignableFrom(cls)) + return Mode.PORTABLE; + else if (Externalizable.class.isAssignableFrom(cls)) + return Mode.EXTERNALIZABLE; + else if (Map.Entry.class.isAssignableFrom(cls)) + return Mode.MAP_ENTRY; + else if (Collection.class.isAssignableFrom(cls)) + return Mode.COL; + else if (Map.class.isAssignableFrom(cls)) + return Mode.MAP; + else if (cls == GridPortableObjectImpl.class) + return Mode.PORTABLE_OBJ; + else if (cls.isEnum()) + return Mode.ENUM; + else if (cls == Class.class) + return Mode.CLASS; + else + return Mode.OBJECT; + } + + /** */ + private static class FieldInfo { + /** */ + private final Field field; + + /** */ + private final int id; + + /** */ + private final Mode mode; + + /** + * @param field Field. + * @param id Field ID. + */ + private FieldInfo(Field field, int id) { + assert field != null; + + this.field = field; + this.id = id; + + Class<?> type = field.getType(); + + mode = mode(type); + } + + /** + * @return Field mode. + */ + public Mode fieldMode() { + return mode; + } + + /** + * @param obj Object. + * @param writer Writer. + * @throws PortableException In case of error. + */ + public void write(Object obj, GridPortableWriterImpl writer) throws PortableException { + assert obj != null; + assert writer != null; + + writer.doWriteInt(id); + + Object val; + + try { + val = field.get(obj); + } + catch (IllegalAccessException e) { + throw new PortableException("Failed to get value for field: " + field, e); + } + + switch (mode) { + case BYTE: + writer.writeByteField((Byte)val); + + break; + + case SHORT: + writer.writeShortField((Short)val); + + break; + + case INT: + writer.writeIntField((Integer)val); + + break; + + case LONG: + writer.writeLongField((Long)val); + + break; + + case FLOAT: + writer.writeFloatField((Float)val); + + break; + + case DOUBLE: + writer.writeDoubleField((Double)val); + + break; + + case CHAR: + writer.writeCharField((Character)val); + + break; + + case BOOLEAN: + writer.writeBooleanField((Boolean)val); + + break; + + case DECIMAL: + writer.writeDecimalField((BigDecimal)val); + + break; + + case STRING: + writer.writeStringField((String)val); + + break; + + case UUID: + writer.writeUuidField((UUID)val); + + break; + + case DATE: + if (val instanceof Timestamp) + writer.writeTimestampField((Timestamp)val); + else + writer.writeDateField((Date)val); + + break; + + case BYTE_ARR: + writer.writeByteArrayField((byte[])val); + + break; + + case SHORT_ARR: + writer.writeShortArrayField((short[])val); + + break; + + case INT_ARR: + writer.writeIntArrayField((int[])val); + + break; + + case LONG_ARR: + writer.writeLongArrayField((long[])val); + + break; + + case FLOAT_ARR: + writer.writeFloatArrayField((float[])val); + + break; + + case DOUBLE_ARR: + writer.writeDoubleArrayField((double[])val); + + break; + + case CHAR_ARR: + writer.writeCharArrayField((char[])val); + + break; + + case BOOLEAN_ARR: + writer.writeBooleanArrayField((boolean[])val); + + break; + + case DECIMAL_ARR: + writer.writeDecimalArrayField((BigDecimal[])val); + + break; + + case STRING_ARR: + writer.writeStringArrayField((String[])val); + + break; + + case UUID_ARR: + writer.writeUuidArrayField((UUID[])val); + + break; + + case DATE_ARR: + writer.writeDateArrayField((Date[])val); + + break; + + case OBJ_ARR: + writer.writeObjectArrayField((Object[])val); + + break; + + case COL: + writer.writeCollectionField((Collection<?>)val); + + break; + + case MAP: + writer.writeMapField((Map<?, ?>)val); + + break; + + case MAP_ENTRY: + writer.writeMapEntryField((Map.Entry<?, ?>)val); + + break; + + case PORTABLE_OBJ: + writer.writePortableObjectField((GridPortableObjectImpl)val); + + break; + + case ENUM: + writer.writeEnumField((Enum<?>)val); + + break; + + case ENUM_ARR: + writer.writeEnumArrayField((Object[])val); + + break; + + case PORTABLE: + case EXTERNALIZABLE: + case OBJECT: + writer.writeObjectField(val); + + break; + + case CLASS: + writer.writeClassField((Class)val); + + break; + + default: + assert false : "Invalid mode: " + mode; + } + } + + /** + * @param obj Object. + * @param reader Reader. + * @throws PortableException In case of error. + */ + public void read(Object obj, GridGridPortableReaderImpl reader) throws PortableException { + Object val = null; + + switch (mode) { + case BYTE: + val = reader.readByte(id); + + break; + + case SHORT: + val = reader.readShort(id); + + break; + + case INT: + val = reader.readInt(id); + + break; + + case LONG: + val = reader.readLong(id); + + break; + + case FLOAT: + val = reader.readFloat(id); + + break; + + case DOUBLE: + val = reader.readDouble(id); + + break; + + case CHAR: + val = reader.readChar(id); + + break; + + case BOOLEAN: + val = reader.readBoolean(id); + + break; + + case DECIMAL: + val = reader.readDecimal(id); + + break; + + case STRING: + val = reader.readString(id); + + break; + + case UUID: + val = reader.readUuid(id); + + break; + + case DATE: + val = field.getType() == Timestamp.class ? reader.readTimestamp(id) : reader.readDate(id); + + break; + + case BYTE_ARR: + val = reader.readByteArray(id); + + break; + + case SHORT_ARR: + val = reader.readShortArray(id); + + break; + + case INT_ARR: + val = reader.readIntArray(id); + + break; + + case LONG_ARR: + val = reader.readLongArray(id); + + break; + + case FLOAT_ARR: + val = reader.readFloatArray(id); + + break; + + case DOUBLE_ARR: + val = reader.readDoubleArray(id); + + break; + + case CHAR_ARR: + val = reader.readCharArray(id); + + break; + + case BOOLEAN_ARR: + val = reader.readBooleanArray(id); + + break; + + case DECIMAL_ARR: + val = reader.readDecimalArray(id); + + break; + + case STRING_ARR: + val = reader.readStringArray(id); + + break; + + case UUID_ARR: + val = reader.readUuidArray(id); + + break; + + case DATE_ARR: + val = reader.readDateArray(id); + + break; + + case OBJ_ARR: + val = reader.readObjectArray(id); + + break; + + case COL: + val = reader.readCollection(id, null); + + break; + + case MAP: + val = reader.readMap(id, null); + + break; + + case MAP_ENTRY: + val = reader.readMapEntry(id); + + break; + + case PORTABLE_OBJ: + val = reader.readPortableObject(id); + + break; + + case ENUM: + val = reader.readEnum(id, field.getType()); + + break; + + case ENUM_ARR: + val = reader.readEnumArray(id, field.getType().getComponentType()); + + break; + + case PORTABLE: + case EXTERNALIZABLE: + case OBJECT: + val = reader.readObject(id); + + break; + + case CLASS: + val = reader.readClass(id); + + break; + + default: + assert false : "Invalid mode: " + mode; + } + + try { + if (val != null || !field.getType().isPrimitive()) + field.set(obj, val); + } + catch (IllegalAccessException e) { + throw new PortableException("Failed to set value for field: " + field, e); + } + } + } + + /** */ + enum Mode { + /** */ + BYTE("byte"), + + /** */ + SHORT("short"), + + /** */ + INT("int"), + + /** */ + LONG("long"), + + /** */ + FLOAT("float"), + + /** */ + DOUBLE("double"), + + /** */ + CHAR("char"), + + /** */ + BOOLEAN("boolean"), + + /** */ + DECIMAL("decimal"), + + /** */ + STRING("String"), + + /** */ + UUID("UUID"), + + /** */ + DATE("Date"), + + /** */ + BYTE_ARR("byte[]"), + + /** */ + SHORT_ARR("short[]"), + + /** */ + INT_ARR("int[]"), + + /** */ + LONG_ARR("long[]"), + + /** */ + FLOAT_ARR("float[]"), + + /** */ + DOUBLE_ARR("double[]"), + + /** */ + CHAR_ARR("char[]"), + + /** */ + BOOLEAN_ARR("boolean[]"), + + /** */ + DECIMAL_ARR("decimal[]"), + + /** */ + STRING_ARR("String[]"), + + /** */ + UUID_ARR("UUID[]"), + + /** */ + DATE_ARR("Date[]"), + + /** */ + OBJ_ARR("Object[]"), + + /** */ + COL("Collection"), + + /** */ + MAP("Map"), + + /** */ + MAP_ENTRY("Entry"), + + /** */ + PORTABLE_OBJ("Object"), + + /** */ + ENUM("Enum"), + + /** */ + ENUM_ARR("Enum[]"), + + /** */ + CLASS("Class"), + + /** */ + PORTABLE("Object"), + + /** */ + EXTERNALIZABLE("Object"), + + /** */ + OBJECT("Object"), + + /** */ + EXCLUSION("Exclusion"); + + /** */ + private final String typeName; + + /** + * @param typeName Type name. + */ + Mode(String typeName) { + this.typeName = typeName; + } + + /** + * @return Type name. + */ + String typeName() { + return typeName; + } + } +}