/* * Copyright 2000-2016 JetBrains s.r.o. * * Licensed 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.jetbrains.jps.javac.ast.api; import com.intellij.openapi.util.ThrowableComputable; import com.intellij.openapi.util.io.DataInputOutputUtilRt; import com.intellij.util.ThrowableConsumer; import gnu.trove.TObjectIntHashMap; import gnu.trove.TObjectIntProcedure; import org.jetbrains.annotations.NotNull; import javax.lang.model.element.Modifier; import java.io.*; import java.util.*; public class JavacFileData { private static final byte CLASS_MARKER = 0; private static final byte METHOD_MARKER = 1; private static final byte FIELD_MARKER = 2; private static final byte FUN_EXPR_MARKER = 3; private final String myFilePath; private final TObjectIntHashMap<JavacRef> myRefs; private final TObjectIntHashMap<JavacRef> myImportRefs; private final List<JavacDef> myDefs; public JavacFileData(@NotNull String path, @NotNull TObjectIntHashMap<JavacRef> refs, @NotNull TObjectIntHashMap<JavacRef> importRefs, @NotNull List<JavacDef> defs) { myFilePath = path; myRefs = refs; myImportRefs = importRefs; myDefs = defs; } @NotNull public String getFilePath() { return myFilePath; } @NotNull public TObjectIntHashMap<JavacRef> getRefs() { return myRefs; } @NotNull public TObjectIntHashMap<JavacRef> getImportRefs() { return myImportRefs; } @NotNull public List<JavacDef> getDefs() { return myDefs; } @NotNull public byte[] asBytes() { final ByteArrayOutputStream os = new ByteArrayOutputStream(); DataOutputStream stream = new DataOutputStream(os); try { stream.writeUTF(getFilePath()); saveRefs(stream, getRefs()); saveRefs(stream, getImportRefs()); saveDefs(stream, getDefs()); } catch (IOException e) { throw new RuntimeException(e); } return os.toByteArray(); } @NotNull public static JavacFileData fromBytes(byte[] bytes) { @SuppressWarnings("IOResourceOpenedButNotSafelyClosed") final DataInputStream in = new DataInputStream(new ByteArrayInputStream(bytes)); try { return new JavacFileData(in.readUTF(), readRefs(in), readRefs(in), readDefs(in)); } catch (IOException e) { throw new RuntimeException(e); } } private static void saveRefs(final DataOutput out, TObjectIntHashMap<JavacRef> refs) throws IOException { final IOException[] exception = new IOException[]{null}; DataInputOutputUtilRt.writeINT(out, refs.size()); if (!refs.forEachEntry(new TObjectIntProcedure<JavacRef>() { @Override public boolean execute(JavacRef ref, int count) { try { writeJavacRef(out, ref); DataInputOutputUtilRt.writeINT(out, count); } catch (IOException e) { exception[0] = e; return false; } return true; } })) { assert exception[0] != null; throw exception[0]; } } private static TObjectIntHashMap<JavacRef> readRefs(final DataInput in) throws IOException { final int size = DataInputOutputUtilRt.readINT(in); TObjectIntHashMap<JavacRef> deserialized = new TObjectIntHashMap<JavacRef>(size); for (int i = 0; i < size; i++) { deserialized.put(readJavacRef(in), DataInputOutputUtilRt.readINT(in)); } return deserialized; } private static void saveDefs(final DataOutput out, List<JavacDef> defs) throws IOException { DataInputOutputUtilRt.writeSeq(out, defs, new ThrowableConsumer<JavacDef, IOException>() { @Override public void consume(JavacDef def) throws IOException { writeJavacDef(out, def); } }); } private static List<JavacDef> readDefs(final DataInput in) throws IOException { return DataInputOutputUtilRt.readSeq(in, new ThrowableComputable<JavacDef, IOException>() { @Override public JavacDef compute() throws IOException { return readJavacDef(in); } }); } private static JavacDef readJavacDef(@NotNull DataInput in) throws IOException { final byte marker = in.readByte(); switch (marker) { case CLASS_MARKER: final int supersSize = in.readInt(); JavacRef[] superClasses = new JavacRef[supersSize]; for (int i = 0; i < supersSize; i++) { superClasses[i] = readJavacRef(in); } return new JavacDef.JavacClassDef(readJavacRef(in), superClasses); case FUN_EXPR_MARKER: return new JavacDef.JavacFunExprDef(readJavacRef(in)); case METHOD_MARKER: JavacRef retType = readJavacRef(in); byte dimension = in.readByte(); boolean isStatic = in.readBoolean(); return new JavacDef.JavacMemberDef(readJavacRef(in), retType, dimension, isStatic); default: throw new IllegalStateException("unknown marker " + marker); } } private static void writeJavacDef(@NotNull DataOutput out, JavacDef def) throws IOException { if (def instanceof JavacDef.JavacClassDef) { out.writeByte(CLASS_MARKER); final JavacRef[] superClasses = ((JavacDef.JavacClassDef)def).getSuperClasses(); out.writeInt(superClasses.length); for (JavacRef aClass : superClasses) { writeJavacRef(out, aClass); } } else if (def instanceof JavacDef.JavacFunExprDef) { out.writeByte(FUN_EXPR_MARKER); } else if (def instanceof JavacDef.JavacMemberDef) { out.writeByte(METHOD_MARKER); writeJavacRef(out, ((JavacDef.JavacMemberDef)def).getReturnType()); out.writeByte(((JavacDef.JavacMemberDef)def).getIteratorKind()); out.writeBoolean(((JavacDef.JavacMemberDef)def).isStatic()); } else { throw new IllegalStateException("unknown type: " + def.getClass()); } writeJavacRef(out, def.getDefinedElement()); } private static void writeJavacRef(@NotNull DataOutput out, JavacRef ref) throws IOException { if (ref instanceof JavacRef.JavacClass) { out.writeByte(CLASS_MARKER); out.writeBoolean(((JavacRef.JavacClass)ref).isAnonymous()); } else if (ref instanceof JavacRef.JavacField) { out.writeByte(FIELD_MARKER); out.writeUTF(ref.getOwnerName()); } else if (ref instanceof JavacRef.JavacMethod) { out.writeByte(METHOD_MARKER); out.writeUTF(ref.getOwnerName()); out.write(((JavacRef.JavacMethod)ref).getParamCount()); } else { throw new IllegalStateException("unknown type: " + ref.getClass()); } writeModifiers(out, ref.getModifiers()); out.writeUTF(ref.getName()); } private static JavacRef readJavacRef(@NotNull DataInput in) throws IOException { final byte marker = in.readByte(); switch (marker) { case CLASS_MARKER: return new JavacRef.JavacClassImpl(in.readBoolean(), readModifiers(in), in.readUTF()); case METHOD_MARKER: return new JavacRef.JavacMethodImpl(in.readUTF(), in.readByte(), readModifiers(in), in.readUTF()); case FIELD_MARKER: return new JavacRef.JavacFieldImpl(in.readUTF(), readModifiers(in), in.readUTF()); default: throw new IllegalStateException("unknown marker " + marker); } } private static void writeModifiers(final DataOutput output, Set<Modifier> modifiers) throws IOException { DataInputOutputUtilRt.writeSeq(output, modifiers, new ThrowableConsumer<Modifier, IOException>() { @Override public void consume(Modifier modifier) throws IOException { output.writeUTF(modifier.name()); } }); } private static Set<Modifier> readModifiers(final DataInput input) throws IOException { final List<Modifier> modifierList = DataInputOutputUtilRt.readSeq(input, new ThrowableComputable<Modifier, IOException>() { @Override public Modifier compute() throws IOException { return Modifier.valueOf(input.readUTF()); } }); return modifierList.isEmpty() ? Collections.<Modifier>emptySet() : EnumSet.copyOf(modifierList); } }