| /* |
| * 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.codegen; |
| |
| import java.io.BufferedReader; |
| import java.io.BufferedWriter; |
| import java.io.File; |
| import java.io.FileNotFoundException; |
| import java.io.FileReader; |
| import java.io.FileWriter; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.Modifier; |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.BitSet; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.HashMap; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.TreeSet; |
| import java.util.UUID; |
| import org.apache.ignite.internal.GridCodegenConverter; |
| import org.apache.ignite.internal.GridDirectCollection; |
| import org.apache.ignite.internal.GridDirectMap; |
| import org.apache.ignite.internal.GridDirectTransient; |
| import org.apache.ignite.internal.IgniteCodeGeneratingFail; |
| import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; |
| import org.apache.ignite.internal.processors.cache.mvcc.DeadlockProbe; |
| import org.apache.ignite.internal.processors.cache.mvcc.ProbedTx; |
| import org.apache.ignite.internal.util.IgniteUtils; |
| import org.apache.ignite.internal.util.typedef.internal.SB; |
| import org.apache.ignite.internal.util.typedef.internal.U; |
| import org.apache.ignite.lang.IgniteUuid; |
| import org.apache.ignite.plugin.extensions.communication.Message; |
| import org.apache.ignite.plugin.extensions.communication.MessageCollectionItemType; |
| import org.jetbrains.annotations.Nullable; |
| |
| import static java.lang.reflect.Modifier.isStatic; |
| import static java.lang.reflect.Modifier.isTransient; |
| |
| /** |
| * Direct marshallable code generator. |
| */ |
| public class MessageCodeGenerator { |
| /** */ |
| private static final Comparator<Field> FIELD_CMP = new Comparator<Field>() { |
| @Override public int compare(Field f1, Field f2) { |
| return f1.getName().compareTo(f2.getName()); |
| } |
| }; |
| |
| /** */ |
| public static final String DFLT_SRC_DIR = U.getIgniteHome() + "/modules/core/src/main/java"; |
| |
| /** */ |
| public static final String INDEXING_SRC_DIR = U.getIgniteHome() + "/modules/indexing/src/main/java"; |
| |
| /** */ |
| private static final Class<?> BASE_CLS = Message.class; |
| |
| /** */ |
| private static final String EMPTY = ""; |
| |
| /** */ |
| public static final String TAB = " "; |
| |
| /** */ |
| private static final String BUF_VAR = "buf"; |
| |
| /** */ |
| private static final Map<Class<?>, MessageCollectionItemType> TYPES = U.newHashMap(30); |
| |
| static { |
| TYPES.put(byte.class, MessageCollectionItemType.BYTE); |
| TYPES.put(Byte.class, MessageCollectionItemType.BYTE); |
| TYPES.put(short.class, MessageCollectionItemType.SHORT); |
| TYPES.put(Short.class, MessageCollectionItemType.SHORT); |
| TYPES.put(int.class, MessageCollectionItemType.INT); |
| TYPES.put(Integer.class, MessageCollectionItemType.INT); |
| TYPES.put(long.class, MessageCollectionItemType.LONG); |
| TYPES.put(Long.class, MessageCollectionItemType.LONG); |
| TYPES.put(float.class, MessageCollectionItemType.FLOAT); |
| TYPES.put(Float.class, MessageCollectionItemType.FLOAT); |
| TYPES.put(double.class, MessageCollectionItemType.DOUBLE); |
| TYPES.put(Double.class, MessageCollectionItemType.DOUBLE); |
| TYPES.put(char.class, MessageCollectionItemType.CHAR); |
| TYPES.put(Character.class, MessageCollectionItemType.CHAR); |
| TYPES.put(boolean.class, MessageCollectionItemType.BOOLEAN); |
| TYPES.put(Boolean.class, MessageCollectionItemType.BOOLEAN); |
| TYPES.put(byte[].class, MessageCollectionItemType.BYTE_ARR); |
| TYPES.put(short[].class, MessageCollectionItemType.SHORT_ARR); |
| TYPES.put(int[].class, MessageCollectionItemType.INT_ARR); |
| TYPES.put(long[].class, MessageCollectionItemType.LONG_ARR); |
| TYPES.put(float[].class, MessageCollectionItemType.FLOAT_ARR); |
| TYPES.put(double[].class, MessageCollectionItemType.DOUBLE_ARR); |
| TYPES.put(char[].class, MessageCollectionItemType.CHAR_ARR); |
| TYPES.put(boolean[].class, MessageCollectionItemType.BOOLEAN_ARR); |
| TYPES.put(String.class, MessageCollectionItemType.STRING); |
| TYPES.put(BitSet.class, MessageCollectionItemType.BIT_SET); |
| TYPES.put(UUID.class, MessageCollectionItemType.UUID); |
| TYPES.put(IgniteUuid.class, MessageCollectionItemType.IGNITE_UUID); |
| TYPES.put(AffinityTopologyVersion.class, MessageCollectionItemType.AFFINITY_TOPOLOGY_VERSION); |
| } |
| |
| /** |
| * @param cls Class. |
| * @return Type enum value. |
| */ |
| private static MessageCollectionItemType typeEnum(Class<?> cls) { |
| MessageCollectionItemType type = TYPES.get(cls); |
| |
| if (type == null) { |
| assert Message.class.isAssignableFrom(cls) : cls; |
| |
| type = MessageCollectionItemType.MSG; |
| } |
| |
| return type; |
| } |
| |
| /** */ |
| private final Collection<String> write = new ArrayList<>(); |
| |
| /** */ |
| private final Collection<String> read = new ArrayList<>(); |
| |
| /** */ |
| private final Map<Class<?>, Integer> fieldCnt = new HashMap<>(); |
| |
| /** */ |
| private final String srcDir; |
| |
| /** */ |
| private int totalFieldCnt; |
| |
| /** */ |
| private List<Field> fields; |
| |
| /** */ |
| private int indent; |
| |
| /** |
| * @param args Arguments. |
| * @throws Exception In case of error. |
| */ |
| public static void main(String[] args) throws Exception { |
| String srcDir = DFLT_SRC_DIR; |
| |
| if (args != null && args.length > 0) |
| srcDir = args[0]; |
| |
| MessageCodeGenerator gen = new MessageCodeGenerator(srcDir); |
| |
| gen.generateAndWrite(ProbedTx.class); |
| gen.generateAndWrite(DeadlockProbe.class); |
| |
| // gen.generateAll(true); |
| |
| // gen.generateAndWrite(GridCacheMessage.class); |
| |
| // gen.generateAndWrite(GridMessageCollection.class); |
| // gen.generateAndWrite(DataStreamerEntry.class); |
| |
| // gen.generateAndWrite(GridDistributedLockRequest.class); |
| // gen.generateAndWrite(GridDistributedLockResponse.class); |
| // gen.generateAndWrite(GridNearLockRequest.class); |
| // gen.generateAndWrite(GridNearLockResponse.class); |
| // gen.generateAndWrite(GridDhtLockRequest.class); |
| // gen.generateAndWrite(GridDhtLockResponse.class); |
| // |
| // gen.generateAndWrite(GridDistributedTxPrepareRequest.class); |
| // gen.generateAndWrite(GridDistributedTxPrepareResponse.class); |
| // gen.generateAndWrite(GridNearTxPrepareRequest.class); |
| // gen.generateAndWrite(GridNearTxPrepareResponse.class); |
| // gen.generateAndWrite(GridDhtTxPrepareRequest.class); |
| // gen.generateAndWrite(GridDhtTxPrepareResponse.class); |
| // |
| // gen.generateAndWrite(GridDistributedTxFinishRequest.class); |
| // gen.generateAndWrite(GridDistributedTxFinishResponse.class); |
| // gen.generateAndWrite(GridNearTxFinishRequest.class); |
| // gen.generateAndWrite(GridNearTxFinishResponse.class); |
| // gen.generateAndWrite(GridDhtTxFinishRequest.class); |
| // gen.generateAndWrite(GridDhtTxFinishResponse.class); |
| // |
| // gen.generateAndWrite(GridCacheTxRecoveryRequest.class); |
| // gen.generateAndWrite(GridCacheTxRecoveryResponse.class); |
| |
| // gen.generateAndWrite(GridQueryCancelRequest.class); |
| // gen.generateAndWrite(GridQueryFailResponse.class); |
| // gen.generateAndWrite(GridQueryNextPageRequest.class); |
| // gen.generateAndWrite(GridQueryNextPageResponse.class); |
| // gen.generateAndWrite(GridQueryRequest.class); |
| // gen.generateAndWrite(GridCacheSqlQuery.class); |
| |
| // gen.generateAndWrite(GridH2Null.class); |
| // gen.generateAndWrite(GridH2Boolean.class); |
| // gen.generateAndWrite(GridH2Byte.class); |
| // gen.generateAndWrite(GridH2Short.class); |
| // gen.generateAndWrite(GridH2Integer.class); |
| // gen.generateAndWrite(GridH2Long.class); |
| // gen.generateAndWrite(GridH2Decimal.class); |
| // gen.generateAndWrite(GridH2Double.class); |
| // gen.generateAndWrite(GridH2Float.class); |
| // gen.generateAndWrite(GridH2Time.class); |
| // gen.generateAndWrite(GridH2Date.class); |
| // gen.generateAndWrite(GridH2Timestamp.class); |
| // gen.generateAndWrite(GridH2Bytes.class); |
| // gen.generateAndWrite(GridH2String.class); |
| // gen.generateAndWrite(GridH2Array.class); |
| // gen.generateAndWrite(GridH2JavaObject.class); |
| // gen.generateAndWrite(GridH2Uuid.class); |
| // gen.generateAndWrite(GridH2Geometry.class); |
| // gen.generateAndWrite(GridH2CacheObject.class); |
| // gen.generateAndWrite(GridH2IndexRangeRequest.class); |
| // gen.generateAndWrite(GridH2IndexRangeResponse.class); |
| // gen.generateAndWrite(GridH2RowRange.class); |
| // gen.generateAndWrite(GridH2RowRangeBounds.class); |
| // gen.generateAndWrite(GridH2QueryRequest.class); |
| // gen.generateAndWrite(GridH2RowMessage.class); |
| // gen.generateAndWrite(GridCacheVersion.class); |
| // gen.generateAndWrite(GridCacheVersionEx.class); |
| // gen.generateAndWrite(GridH2DmlRequest.class); |
| // gen.generateAndWrite(GridH2DmlResponse.class); |
| // gen.generateAndWrite(GridNearTxEnlistRequest.class); |
| // gen.generateAndWrite(GridNearTxEnlistResponse.class); |
| // gen.generateAndWrite(GenerateEncryptionKeyRequest.class); |
| // gen.generateAndWrite(GenerateEncryptionKeyResponse.class); |
| } |
| |
| /** |
| * @param srcDir Source directory. |
| */ |
| public MessageCodeGenerator(String srcDir) { |
| this.srcDir = srcDir; |
| } |
| |
| /** |
| * Generates code for all classes. |
| * |
| * @param write Whether to write to file. |
| * @throws Exception In case of error. |
| */ |
| public void generateAll(boolean write) throws Exception { |
| Collection<Class<? extends Message>> classes = classes(); |
| |
| for (Class<? extends Message> cls : classes) { |
| try { |
| boolean isAbstract = Modifier.isAbstract(cls.getModifiers()); |
| |
| System.out.println("Processing class: " + cls.getName() + (isAbstract ? " (abstract)" : "")); |
| |
| if (write) |
| generateAndWrite(cls); |
| else |
| generate(cls); |
| } |
| catch (IllegalStateException e) { |
| System.out.println("Will skip class generation [cls=" + cls + ", err=" + e.getMessage() + ']'); |
| } |
| } |
| } |
| |
| /** |
| * Generates code for provided class and writes it to source file. |
| * Note: this method must be called only from {@code generateAll(boolean)} |
| * and only with updating {@code CLASSES_ORDER_FILE} and other auto generated files. |
| * |
| * @param cls Class. |
| * @throws Exception In case of error. |
| */ |
| @SuppressWarnings("ConstantConditions") |
| public void generateAndWrite(Class<? extends Message> cls) throws Exception { |
| assert cls != null; |
| |
| generate(cls); |
| |
| File file = new File(srcDir, cls.getName().replace('.', File.separatorChar) + ".java"); |
| |
| if (!file.exists() || !file.isFile()) { |
| System.out.println("Source file not found: " + file.getPath()); |
| |
| return; |
| } |
| |
| Collection<String> src = new ArrayList<>(); |
| |
| BufferedReader rdr = null; |
| |
| try { |
| rdr = new BufferedReader(new FileReader(file)); |
| |
| String line; |
| boolean skip = false; |
| |
| boolean writeFound = false; |
| boolean readFound = false; |
| boolean fieldCntFound = false; |
| |
| while ((line = rdr.readLine()) != null) { |
| if (!skip) { |
| src.add(line); |
| |
| if (line.contains("public boolean writeTo(ByteBuffer buf, MessageWriter writer)")) { |
| src.addAll(write); |
| |
| skip = true; |
| |
| writeFound = true; |
| } |
| else if (line.contains("public boolean readFrom(ByteBuffer buf, MessageReader reader)")) { |
| src.addAll(read); |
| |
| skip = true; |
| |
| readFound = true; |
| } |
| else if (line.contains("public byte fieldsCount()")) { |
| src.add(TAB + TAB + "return " + totalFieldCnt + ";"); |
| |
| skip = true; |
| |
| fieldCntFound = true; |
| } |
| } |
| else if (line.startsWith(TAB + "}")) { |
| src.add(line); |
| |
| skip = false; |
| } |
| } |
| |
| if (!writeFound) |
| System.out.println(" writeTo method doesn't exist."); |
| |
| if (!readFound) |
| System.out.println(" readFrom method doesn't exist."); |
| |
| if (!fieldCntFound) |
| System.out.println(" fieldCount method doesn't exist."); |
| } |
| finally { |
| if (rdr != null) |
| rdr.close(); |
| } |
| |
| BufferedWriter wr = null; |
| |
| try { |
| wr = new BufferedWriter(new FileWriter(file)); |
| |
| for (String line : src) |
| wr.write(line + '\n'); |
| } |
| finally { |
| if (wr != null) |
| wr.close(); |
| } |
| } |
| |
| /** |
| * Generates code for provided class. |
| * |
| * @param cls Class. |
| * @throws Exception In case of error. |
| */ |
| private void generate(Class<? extends Message> cls) throws Exception { |
| assert cls != null; |
| |
| if (cls.isInterface()) |
| return; |
| |
| if (cls.isAnnotationPresent(IgniteCodeGeneratingFail.class)) |
| throw new IllegalStateException("@IgniteCodeGeneratingFail is provided for class: " + cls.getName()); |
| |
| write.clear(); |
| read.clear(); |
| |
| fields = new ArrayList<>(); |
| |
| Field[] declaredFields = cls.getDeclaredFields(); |
| |
| for (Field field : declaredFields) { |
| int mod = field.getModifiers(); |
| |
| if (!isStatic(mod) && !isTransient(mod) && !field.isAnnotationPresent(GridDirectTransient.class)) |
| fields.add(field); |
| } |
| |
| Collections.sort(fields, FIELD_CMP); |
| |
| int state = startState(cls); |
| |
| totalFieldCnt = state + fields.size(); |
| |
| indent = 2; |
| |
| boolean hasSuper = cls.getSuperclass() != Object.class; |
| |
| start(write, hasSuper ? "writeTo" : null, true); |
| start(read, hasSuper ? "readFrom" : null, false); |
| |
| indent++; |
| |
| for (Field field : fields) |
| processField(field, state++); |
| |
| indent--; |
| |
| finish(write, null); |
| finish(read, cls.getSimpleName()); |
| } |
| |
| /** |
| * @param cls Message class. |
| * @return Start state. |
| */ |
| private int startState(Class<?> cls) { |
| assert cls != null; |
| |
| Class<?> superCls = cls.getSuperclass(); |
| |
| Integer state = fieldCnt.get(superCls); |
| |
| if (state != null) |
| return state; |
| |
| state = 0; |
| |
| while (cls.getSuperclass() != Object.class) { |
| cls = cls.getSuperclass(); |
| |
| for (Field field : cls.getDeclaredFields()) { |
| int mod = field.getModifiers(); |
| |
| if (!isStatic(mod) && !isTransient(mod) && !field.isAnnotationPresent(GridDirectTransient.class)) |
| state++; |
| } |
| } |
| |
| fieldCnt.put(superCls, state); |
| |
| return state; |
| } |
| |
| /** |
| * @param code Code lines. |
| * @param superMtd Super class method name. |
| * @param write Whether write code is generated. |
| */ |
| private void start(Collection<String> code, @Nullable String superMtd, boolean write) { |
| assert code != null; |
| |
| code.add(builder().a(write ? "writer" : "reader").a(".setBuffer(").a(BUF_VAR).a(");").toString()); |
| code.add(EMPTY); |
| |
| if (!write) { |
| code.add(builder().a("if (!reader.beforeMessageRead())").toString()); |
| |
| indent++; |
| |
| code.add(builder().a("return false;").toString()); |
| code.add(EMPTY); |
| |
| indent--; |
| } |
| |
| if (superMtd != null) { |
| if (write) |
| returnFalseIfFailed(code, "super." + superMtd, BUF_VAR, "writer"); |
| else |
| returnFalseIfFailed(code, "super." + superMtd, BUF_VAR, "reader"); |
| |
| code.add(EMPTY); |
| } |
| |
| if (write) { |
| code.add(builder().a("if (!writer.isHeaderWritten()) {").toString()); |
| |
| indent++; |
| |
| returnFalseIfFailed(code, "writer.writeHeader", "directType()", "fieldsCount()"); |
| |
| code.add(EMPTY); |
| code.add(builder().a("writer.onHeaderWritten();").toString()); |
| |
| indent--; |
| |
| code.add(builder().a("}").toString()); |
| code.add(EMPTY); |
| } |
| |
| if (!fields.isEmpty()) |
| code.add(builder().a("switch (").a(write ? "writer.state()" : "reader.state()").a(") {").toString()); |
| } |
| |
| /** |
| * @param code Code lines. |
| */ |
| private void finish(Collection<String> code, String readClsName) { |
| assert code != null; |
| |
| if (!fields.isEmpty()) { |
| code.add(builder().a("}").toString()); |
| code.add(EMPTY); |
| } |
| |
| if (readClsName == null) |
| code.add(builder().a("return true;").toString()); |
| else |
| code.add(builder().a("return reader.afterMessageRead(").a(readClsName).a(".class);").toString()); |
| } |
| |
| /** |
| * @param field Field. |
| * @param opt Case option. |
| */ |
| private void processField(Field field, int opt) { |
| assert field != null; |
| assert opt >= 0; |
| |
| GridDirectCollection colAnn = field.getAnnotation(GridDirectCollection.class); |
| GridDirectMap mapAnn = field.getAnnotation(GridDirectMap.class); |
| |
| if (colAnn == null && Collection.class.isAssignableFrom(field.getType())) |
| throw new IllegalStateException("@GridDirectCollection annotation is not provided for field: " + |
| field.getName()); |
| |
| if (mapAnn == null && Map.class.isAssignableFrom(field.getType())) |
| throw new IllegalStateException("@GridDirectMap annotation is not provided for field: " + field.getName()); |
| |
| writeField(field, opt, colAnn, mapAnn); |
| readField(field, opt, colAnn, mapAnn); |
| } |
| |
| /** |
| * @param field Field. |
| * @param opt Case option. |
| * @param colAnn Collection annotation. |
| * @param mapAnn Map annotation. |
| */ |
| private void writeField(Field field, int opt, @Nullable GridDirectCollection colAnn, |
| @Nullable GridDirectMap mapAnn) { |
| assert field != null; |
| assert opt >= 0; |
| |
| write.add(builder().a("case ").a(opt).a(":").toString()); |
| |
| indent++; |
| |
| GridCodegenConverter fldPreproc = field.getAnnotation(GridCodegenConverter.class); |
| |
| String getExp = (fldPreproc != null && !fldPreproc.get().isEmpty()) ? fldPreproc.get() : field.getName(); |
| Class<?> writeType = (fldPreproc != null && !fldPreproc.type().equals(GridCodegenConverter.Default.class)) ? |
| fldPreproc.type() : field.getType(); |
| |
| returnFalseIfWriteFailed(writeType, field.getName(), colAnn != null ? colAnn.value() : null, |
| mapAnn != null ? mapAnn.keyType() : null, mapAnn != null ? mapAnn.valueType() : null, false, getExp); |
| |
| write.add(EMPTY); |
| write.add(builder().a("writer.incrementState();").toString()); |
| write.add(EMPTY); |
| |
| indent--; |
| } |
| |
| /** |
| * @param field Field. |
| * @param opt Case option. |
| * @param colAnn Collection annotation. |
| * @param mapAnn Map annotation. |
| */ |
| private void readField(Field field, int opt, @Nullable GridDirectCollection colAnn, |
| @Nullable GridDirectMap mapAnn) { |
| assert field != null; |
| assert opt >= 0; |
| |
| read.add(builder().a("case ").a(opt).a(":").toString()); |
| |
| indent++; |
| |
| GridCodegenConverter fldPreproc = field.getAnnotation(GridCodegenConverter.class); |
| String setExp = (fldPreproc != null && !fldPreproc.get().isEmpty()) ? fldPreproc.set() : ""; |
| Class<?> writeType = (fldPreproc != null && !fldPreproc.type().equals(GridCodegenConverter.Default.class)) ? |
| fldPreproc.type() : field.getType(); |
| |
| returnFalseIfReadFailed(writeType, field.getName(), colAnn != null ? colAnn.value() : null, |
| mapAnn != null ? mapAnn.keyType() : null, mapAnn != null ? mapAnn.valueType() : null, setExp); |
| |
| read.add(EMPTY); |
| read.add(builder().a("reader.incrementState();").toString()); |
| read.add(EMPTY); |
| |
| indent--; |
| } |
| |
| /** |
| * @param type Field type. |
| * @param name Field name. |
| * @param colItemType Collection item type. |
| * @param mapKeyType Map key type. |
| * @param mapValType Map key value. |
| * @param raw Raw write flag. |
| */ |
| private void returnFalseIfWriteFailed(Class<?> type, String name, @Nullable Class<?> colItemType, |
| @Nullable Class<?> mapKeyType, @Nullable Class<?> mapValType, boolean raw, String getExpr) { |
| assert type != null; |
| assert name != null; |
| |
| String field = raw ? "null" : '"' + name + '"'; |
| |
| if (type == byte.class) |
| returnFalseIfFailed(write, "writer.writeByte", field, getExpr); |
| else if (type == short.class) |
| returnFalseIfFailed(write, "writer.writeShort", field, getExpr); |
| else if (type == int.class) |
| returnFalseIfFailed(write, "writer.writeInt", field, getExpr); |
| else if (type == long.class) |
| returnFalseIfFailed(write, "writer.writeLong", field, getExpr); |
| else if (type == float.class) |
| returnFalseIfFailed(write, "writer.writeFloat", field, getExpr); |
| else if (type == double.class) |
| returnFalseIfFailed(write, "writer.writeDouble", field, getExpr); |
| else if (type == char.class) |
| returnFalseIfFailed(write, "writer.writeChar", field, getExpr); |
| else if (type == boolean.class) |
| returnFalseIfFailed(write, "writer.writeBoolean", field, getExpr); |
| else if (type == byte[].class) |
| returnFalseIfFailed(write, "writer.writeByteArray", field, getExpr); |
| else if (type == short[].class) |
| returnFalseIfFailed(write, "writer.writeShortArray", field, getExpr); |
| else if (type == int[].class) |
| returnFalseIfFailed(write, "writer.writeIntArray", field, getExpr); |
| else if (type == long[].class) |
| returnFalseIfFailed(write, "writer.writeLongArray", field, getExpr); |
| else if (type == float[].class) |
| returnFalseIfFailed(write, "writer.writeFloatArray", field, getExpr); |
| else if (type == double[].class) |
| returnFalseIfFailed(write, "writer.writeDoubleArray", field, getExpr); |
| else if (type == char[].class) |
| returnFalseIfFailed(write, "writer.writeCharArray", field, getExpr); |
| else if (type == boolean[].class) |
| returnFalseIfFailed(write, "writer.writeBooleanArray", field, getExpr); |
| else if (type == String.class) |
| returnFalseIfFailed(write, "writer.writeString", field, getExpr); |
| else if (type == BitSet.class) |
| returnFalseIfFailed(write, "writer.writeBitSet", field, getExpr); |
| else if (type == UUID.class) |
| returnFalseIfFailed(write, "writer.writeUuid", field, getExpr); |
| else if (type == IgniteUuid.class) |
| returnFalseIfFailed(write, "writer.writeIgniteUuid", field, getExpr); |
| else if (type == AffinityTopologyVersion.class) |
| returnFalseIfFailed(write, "writer.writeAffinityTopologyVersion", field, getExpr); |
| else if (type.isEnum()) { |
| String arg = getExpr + " != null ? (byte)" + getExpr + ".ordinal() : -1"; |
| |
| returnFalseIfFailed(write, "writer.writeByte", field, arg); |
| } |
| else if (BASE_CLS.isAssignableFrom(type)) |
| returnFalseIfFailed(write, "writer.writeMessage", field, getExpr); |
| else if (type.isArray()) { |
| returnFalseIfFailed(write, "writer.writeObjectArray", field, getExpr, |
| "MessageCollectionItemType." + typeEnum(type.getComponentType())); |
| } |
| else if (Collection.class.isAssignableFrom(type) && !Set.class.isAssignableFrom(type)) { |
| assert colItemType != null; |
| |
| returnFalseIfFailed(write, "writer.writeCollection", field, getExpr, |
| "MessageCollectionItemType." + typeEnum(colItemType)); |
| } |
| else if (Map.class.isAssignableFrom(type)) { |
| assert mapKeyType != null; |
| assert mapValType != null; |
| |
| returnFalseIfFailed(write, "writer.writeMap", field, getExpr, |
| "MessageCollectionItemType." + typeEnum(mapKeyType), |
| "MessageCollectionItemType." + typeEnum(mapValType)); |
| } |
| else |
| throw new IllegalStateException("Unsupported type: " + type); |
| } |
| |
| /** |
| * @param type Field type. |
| * @param name Field name. |
| * @param colItemType Collection item type. |
| * @param mapKeyType Map key type. |
| * @param mapValType Map value type. |
| */ |
| private void returnFalseIfReadFailed(Class<?> type, @Nullable String name, @Nullable Class<?> colItemType, |
| @Nullable Class<?> mapKeyType, @Nullable Class<?> mapValType, String setExpr) { |
| assert type != null; |
| |
| String field = '"' + name + '"'; |
| |
| if (type == byte.class) |
| returnFalseIfReadFailed(name, "reader.readByte", setExpr, field); |
| else if (type == short.class) |
| returnFalseIfReadFailed(name, "reader.readShort", setExpr, field); |
| else if (type == int.class) |
| returnFalseIfReadFailed(name, "reader.readInt", setExpr, field); |
| else if (type == long.class) |
| returnFalseIfReadFailed(name, "reader.readLong", setExpr, field); |
| else if (type == float.class) |
| returnFalseIfReadFailed(name, "reader.readFloat", setExpr, field); |
| else if (type == double.class) |
| returnFalseIfReadFailed(name, "reader.readDouble", setExpr, field); |
| else if (type == char.class) |
| returnFalseIfReadFailed(name, "reader.readChar", setExpr, field); |
| else if (type == boolean.class) |
| returnFalseIfReadFailed(name, "reader.readBoolean", setExpr, field); |
| else if (type == byte[].class) |
| returnFalseIfReadFailed(name, "reader.readByteArray", setExpr, field); |
| else if (type == short[].class) |
| returnFalseIfReadFailed(name, "reader.readShortArray", setExpr, field); |
| else if (type == int[].class) |
| returnFalseIfReadFailed(name, "reader.readIntArray", setExpr, field); |
| else if (type == long[].class) |
| returnFalseIfReadFailed(name, "reader.readLongArray", setExpr, field); |
| else if (type == float[].class) |
| returnFalseIfReadFailed(name, "reader.readFloatArray", setExpr, field); |
| else if (type == double[].class) |
| returnFalseIfReadFailed(name, "reader.readDoubleArray", setExpr, field); |
| else if (type == char[].class) |
| returnFalseIfReadFailed(name, "reader.readCharArray", setExpr, field); |
| else if (type == boolean[].class) |
| returnFalseIfReadFailed(name, "reader.readBooleanArray", setExpr, field); |
| else if (type == String.class) |
| returnFalseIfReadFailed(name, "reader.readString", setExpr, field); |
| else if (type == BitSet.class) |
| returnFalseIfReadFailed(name, "reader.readBitSet", setExpr, field); |
| else if (type == UUID.class) |
| returnFalseIfReadFailed(name, "reader.readUuid", setExpr, field); |
| else if (type == IgniteUuid.class) |
| returnFalseIfReadFailed(name, "reader.readIgniteUuid", setExpr, field); |
| else if (type == AffinityTopologyVersion.class) |
| returnFalseIfReadFailed(name, "reader.readAffinityTopologyVersion", setExpr, field); |
| else if (type.isEnum()) { |
| String loc = name + "Ord"; |
| |
| read.add(builder().a("byte ").a(loc).a(";").toString()); |
| read.add(EMPTY); |
| |
| returnFalseIfReadFailed(loc, "reader.readByte", setExpr, field); |
| |
| read.add(EMPTY); |
| read.add(builder().a(name).a(" = ").a(type.getSimpleName()).a(".fromOrdinal(").a(loc).a(");").toString()); |
| } |
| else if (BASE_CLS.isAssignableFrom(type)) |
| returnFalseIfReadFailed(name, "reader.readMessage", setExpr, field); |
| else if (type.isArray()) { |
| Class<?> compType = type.getComponentType(); |
| |
| returnFalseIfReadFailed(name, "reader.readObjectArray", setExpr, field, |
| "MessageCollectionItemType." + typeEnum(compType), |
| compType.getSimpleName() + ".class"); |
| } |
| else if (Collection.class.isAssignableFrom(type) && !Set.class.isAssignableFrom(type)) { |
| assert colItemType != null; |
| |
| returnFalseIfReadFailed(name, "reader.readCollection", setExpr, field, |
| "MessageCollectionItemType." + typeEnum(colItemType)); |
| } |
| else if (Map.class.isAssignableFrom(type)) { |
| assert mapKeyType != null; |
| assert mapValType != null; |
| |
| boolean linked = type.equals(LinkedHashMap.class); |
| |
| returnFalseIfReadFailed(name, "reader.readMap", setExpr, field, |
| "MessageCollectionItemType." + typeEnum(mapKeyType), |
| "MessageCollectionItemType." + typeEnum(mapValType), |
| linked ? "true" : "false"); |
| } |
| else |
| throw new IllegalStateException("Unsupported type: " + type); |
| } |
| |
| /** |
| * @param var Variable name. |
| * @param mtd Method name. |
| * @param args Method arguments. |
| */ |
| private void returnFalseIfReadFailed(String var, String mtd, String setConverter, @Nullable String... args) { |
| assert mtd != null; |
| |
| String argsStr = ""; |
| |
| if (args != null && args.length > 0) { |
| for (String arg : args) |
| argsStr += arg + ", "; |
| |
| argsStr = argsStr.substring(0, argsStr.length() - 2); |
| } |
| |
| if (setConverter.isEmpty()) |
| read.add(builder().a(var).a(" = ").a(mtd).a("(").a(argsStr).a(");").toString()); |
| else { |
| read.add(builder().a(var).a(" = ").a(setConverter |
| .replace("$val$", new SB().a(mtd).a("(").a(argsStr).a(")").toString())).a(";").toString()); |
| } |
| read.add(EMPTY); |
| |
| read.add(builder().a("if (!reader.isLastRead())").toString()); |
| |
| indent++; |
| |
| read.add(builder().a("return false;").toString()); |
| |
| indent--; |
| } |
| |
| /** |
| * @param code Code lines. |
| * @param accessor Field or method name. |
| * @param args Method arguments. |
| */ |
| private void returnFalseIfFailed(Collection<String> code, String accessor, @Nullable String... args) { |
| assert code != null; |
| assert accessor != null; |
| |
| String argsStr = ""; |
| |
| if (args != null && args.length > 0) { |
| for (String arg : args) |
| argsStr += arg + ", "; |
| |
| argsStr = argsStr.substring(0, argsStr.length() - 2); |
| } |
| |
| code.add(builder().a("if (!").a(accessor).a("(").a(argsStr).a("))").toString()); |
| |
| indent++; |
| |
| code.add(builder().a("return false;").toString()); |
| |
| indent--; |
| } |
| |
| /** |
| * Creates new builder with correct indent. |
| * |
| * @return Builder. |
| */ |
| private SB builder() { |
| assert indent > 0; |
| |
| SB sb = new SB(); |
| |
| for (int i = 0; i < indent; i++) |
| sb.a(TAB); |
| |
| return sb; |
| } |
| |
| /** |
| * Gets all direct marshallable classes. |
| * First classes will be classes from {@code classesOrder} with same order |
| * as ordered values. Other classes will be at the end and ordered by name |
| * (with package prefix). |
| * That orders need for saving {@code directType} value. |
| * |
| * @return Classes. |
| * @throws Exception In case of error. |
| */ |
| private Collection<Class<? extends Message>> classes() throws Exception { |
| Collection<Class<? extends Message>> col = new TreeSet<>( |
| new Comparator<Class<? extends Message>>() { |
| @Override public int compare(Class<? extends Message> c1, |
| Class<? extends Message> c2) { |
| return c1.getName().compareTo(c2.getName()); |
| } |
| }); |
| |
| ClassLoader ldr = getClass().getClassLoader(); |
| |
| for (URL url : IgniteUtils.classLoaderUrls(ldr)) { |
| File file = new File(url.toURI()); |
| |
| int prefixLen = file.getPath().length() + 1; |
| |
| processFile(file, ldr, prefixLen, col); |
| } |
| |
| return col; |
| } |
| |
| /** |
| * Recursively process provided file or directory. |
| * |
| * @param file File. |
| * @param ldr Class loader. |
| * @param prefixLen Path prefix length. |
| * @param col Classes. |
| * @throws Exception In case of error. |
| */ |
| @SuppressWarnings("unchecked") |
| private void processFile(File file, ClassLoader ldr, int prefixLen, |
| Collection<Class<? extends Message>> col) throws Exception { |
| assert file != null; |
| assert ldr != null; |
| assert prefixLen > 0; |
| assert col != null; |
| |
| if (!file.exists()) |
| throw new FileNotFoundException("File doesn't exist: " + file); |
| |
| if (file.isDirectory()) { |
| for (File f : file.listFiles()) |
| processFile(f, ldr, prefixLen, col); |
| } |
| else { |
| assert file.isFile(); |
| |
| String path = file.getPath(); |
| |
| if (path.endsWith(".class")) { |
| String clsName = path.substring(prefixLen, path.length() - 6).replace(File.separatorChar, '.'); |
| |
| Class<?> cls = Class.forName(clsName, false, ldr); |
| |
| if (cls.getDeclaringClass() == null && cls.getEnclosingClass() == null && |
| !BASE_CLS.equals(cls) && BASE_CLS.isAssignableFrom(cls)) |
| col.add((Class<? extends Message>)cls); |
| } |
| } |
| } |
| } |