| /* |
| * Copyright 2000-2014 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.builders.java.dependencyView; |
| |
| import com.intellij.util.io.DataExternalizer; |
| import com.intellij.util.io.DataInputOutputUtil; |
| import gnu.trove.TIntHashSet; |
| import gnu.trove.TIntProcedure; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.jps.builders.storage.BuildDataCorruptedException; |
| import org.jetbrains.org.objectweb.asm.Type; |
| |
| import java.io.DataInput; |
| import java.io.DataOutput; |
| import java.io.IOException; |
| import java.io.PrintStream; |
| import java.util.*; |
| |
| /** |
| * @author: db |
| * Date: 14.02.11 |
| */ |
| class UsageRepr { |
| private static final byte FIELD_USAGE = 0x0; |
| private static final byte FIELD_ASSIGN_USAGE = 0x1; |
| private static final byte METHOD_USAGE = 0x2; |
| private static final byte CLASS_USAGE = 0x3; |
| private static final byte CLASS_EXTENDS_USAGE = 0x4; |
| private static final byte CLASS_NEW_USAGE = 0x5; |
| private static final byte ANNOTATION_USAGE = 0x6; |
| private static final byte METAMETHOD_USAGE = 0x7; |
| private static final byte CLASS_AS_GENERIC_BOUND_USAGE = 0x8; |
| |
| private static final int DEFAULT_SET_CAPACITY = 32; |
| private static final float DEFAULT_SET_LOAD_FACTOR = 0.98f; |
| |
| private UsageRepr() { |
| |
| } |
| |
| public static abstract class Usage implements RW.Savable, Streamable { |
| public abstract int getOwner(); |
| } |
| |
| public static abstract class FMUsage extends Usage { |
| public final int myName; |
| public final int myOwner; |
| |
| abstract void kindToStream (PrintStream stream); |
| |
| @Override |
| public void toStream(final DependencyContext context, final PrintStream stream) { |
| kindToStream(stream); |
| stream.println(" Name : " + context.getValue(myName)); |
| stream.println(" Owner: " + context.getValue(myOwner)); |
| } |
| |
| @Override |
| public int getOwner() { |
| return myOwner; |
| } |
| |
| private FMUsage(final int name, final int owner) { |
| this.myName = name; |
| this.myOwner = owner; |
| } |
| |
| private FMUsage(final DataInput in) { |
| try { |
| myName = DataInputOutputUtil.readINT(in); |
| myOwner = DataInputOutputUtil.readINT(in); |
| } |
| catch (IOException e) { |
| throw new BuildDataCorruptedException(e); |
| } |
| } |
| |
| protected final void save(final byte tag, final DataOutput out) { |
| try { |
| out.writeByte(tag); |
| DataInputOutputUtil.writeINT(out, myName); |
| DataInputOutputUtil.writeINT(out, myOwner); |
| } |
| catch (IOException e) { |
| throw new BuildDataCorruptedException(e); |
| } |
| } |
| |
| @Override |
| public boolean equals(final Object o) { |
| if (this == o) return true; |
| if (o == null || getClass() != o.getClass()) return false; |
| |
| FMUsage fmUsage = (FMUsage)o; |
| |
| if (myName != fmUsage.myName) return false; |
| if (myOwner != fmUsage.myOwner) return false; |
| |
| return true; |
| } |
| |
| @Override |
| public int hashCode() { |
| return 31 * myName + myOwner; |
| } |
| } |
| |
| public static class FieldUsage extends FMUsage { |
| public final TypeRepr.AbstractType myType; |
| |
| private FieldUsage(final DependencyContext context, final int name, final int owner, final int descriptor) { |
| super(name, owner); |
| myType = TypeRepr.getType(context, descriptor); |
| } |
| |
| private FieldUsage(final DependencyContext context, final DataInput in) { |
| super(in); |
| try { |
| myType = TypeRepr.externalizer(context).read(in); |
| } |
| catch (IOException e) { |
| throw new BuildDataCorruptedException(e); |
| } |
| } |
| |
| protected void kindToStream(final PrintStream stream) { |
| stream.println("FieldUsage:"); |
| } |
| |
| @Override |
| public void toStream(final DependencyContext context, final PrintStream stream) { |
| super.toStream(context, stream); |
| stream.println(" Type: " + myType.getDescr(context)); |
| } |
| |
| @Override |
| public void save(final DataOutput out) { |
| save(FIELD_USAGE, out); |
| myType.save(out); |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) return true; |
| if (o == null || getClass() != o.getClass()) return false; |
| |
| final FieldUsage that = (FieldUsage)o; |
| |
| return myType.equals(that.myType) && myName == that.myName && myOwner == that.myOwner; |
| } |
| |
| @Override |
| public int hashCode() { |
| return 31 * (31 * myType.hashCode() + myName) + myOwner; |
| } |
| } |
| |
| public static class FieldAssignUsage extends FieldUsage { |
| private FieldAssignUsage(final DependencyContext context, final int n, final int o, final int d) { |
| super(context, n, o, d); |
| } |
| |
| private FieldAssignUsage(final DependencyContext context, final DataInput in) { |
| super(context, in); |
| } |
| |
| @Override |
| protected void kindToStream(final PrintStream stream) { |
| stream.println("FieldAssignUsage:"); |
| } |
| |
| @Override |
| public void save(final DataOutput out) { |
| save(FIELD_ASSIGN_USAGE, out); |
| myType.save(out); |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) return true; |
| if (o == null || getClass() != o.getClass()) return false; |
| |
| final FieldAssignUsage that = (FieldAssignUsage)o; |
| |
| return myType.equals(that.myType) && myName == that.myName && myOwner == that.myOwner; |
| } |
| |
| @Override |
| public int hashCode() { |
| return super.hashCode() + 1; |
| } |
| } |
| |
| public static class MethodUsage extends FMUsage { |
| public final TypeRepr.AbstractType[] myArgumentTypes; |
| public final TypeRepr.AbstractType myReturnType; |
| |
| private MethodUsage(final DependencyContext context, final int name, final int owner, final String descriptor) { |
| super(name, owner); |
| myArgumentTypes = TypeRepr.getType(context, Type.getArgumentTypes(descriptor)); |
| myReturnType = TypeRepr.getType(context, Type.getReturnType(descriptor)); |
| } |
| |
| private MethodUsage(final DependencyContext context, final DataInput in) { |
| super(in); |
| try { |
| final DataExternalizer<TypeRepr.AbstractType> externalizer = TypeRepr.externalizer(context); |
| int argumentTypes = DataInputOutputUtil.readINT(in); |
| myArgumentTypes = RW.read(externalizer, in, argumentTypes != 0 ? new TypeRepr.AbstractType[argumentTypes]: TypeRepr.AbstractType.EMPTY_TYPE_ARRAY); |
| myReturnType = externalizer.read(in); |
| } |
| catch (IOException e) { |
| throw new BuildDataCorruptedException(e); |
| } |
| } |
| |
| @Override |
| public void save(final DataOutput out) { |
| save(METHOD_USAGE, out); |
| RW.save(myArgumentTypes, out); |
| myReturnType.save(out); |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) return true; |
| if (o == null || getClass() != o.getClass()) return false; |
| |
| final MethodUsage that = (MethodUsage)o; |
| |
| if (!Arrays.equals(myArgumentTypes, that.myArgumentTypes)) return false; |
| if (myReturnType != null ? !myReturnType.equals(that.myReturnType) : that.myReturnType != null) return false; |
| if (myName != that.myName) return false; |
| if (myOwner != that.myOwner) return false; |
| |
| return Arrays.equals(myArgumentTypes, that.myArgumentTypes) && |
| myReturnType.equals(that.myReturnType) && |
| myName == that.myName && |
| myOwner == that.myOwner; |
| } |
| |
| @Override |
| public int hashCode() { |
| return ((31 * Arrays.hashCode(myArgumentTypes) + (myReturnType.hashCode())) * 31 + (myName)) * 31 + (myOwner); |
| } |
| |
| @Override |
| void kindToStream(final PrintStream stream) { |
| stream.println("MethodUsage:"); |
| } |
| |
| @Override |
| public void toStream(DependencyContext context, PrintStream stream) { |
| super.toStream(context, stream); |
| |
| stream.println(" Arguments:"); |
| |
| for (final TypeRepr.AbstractType at : myArgumentTypes) { |
| stream.println(" " + at.getDescr(context)); |
| } |
| |
| stream.println(" Return type:"); |
| stream.println(" " + myReturnType.getDescr(context)); |
| } |
| } |
| |
| public static class MetaMethodUsage extends FMUsage { |
| private int myArity; |
| |
| public MetaMethodUsage(final DependencyContext context, final int n, final int o, final String descr) { |
| super(n, o); |
| myArity = TypeRepr.getType(context, Type.getArgumentTypes(descr)).length; |
| } |
| |
| public MetaMethodUsage(final DataInput in) { |
| super(in); |
| try { |
| myArity = DataInputOutputUtil.readINT(in); |
| } |
| catch (IOException e) { |
| throw new BuildDataCorruptedException(e); |
| } |
| } |
| |
| @Override |
| public void save(final DataOutput out) { |
| save(METAMETHOD_USAGE, out); |
| try { |
| DataInputOutputUtil.writeINT(out, myArity); |
| } |
| catch (IOException e) { |
| throw new BuildDataCorruptedException(e); |
| } |
| } |
| |
| @Override |
| public boolean equals(final Object o) { |
| if (this == o) return true; |
| if (o == null || getClass() != o.getClass()) return false; |
| if (!super.equals(o)) return false; |
| |
| MetaMethodUsage that = (MetaMethodUsage)o; |
| |
| if (myArity != that.myArity) return false; |
| |
| return super.equals(o); |
| } |
| |
| @Override |
| public int hashCode() { |
| int result = super.hashCode(); |
| result = 31 * result + myArity; |
| return result; |
| } |
| |
| @Override |
| void kindToStream(final PrintStream stream) { |
| stream.println("MetaMethodUsage:"); |
| } |
| |
| @Override |
| public void toStream(DependencyContext context, PrintStream stream) { |
| super.toStream(context, stream); |
| stream.println(" Arity: " + Integer.toString(myArity)); |
| } |
| } |
| |
| public static class ClassUsage extends Usage { |
| final int myClassName; |
| |
| @Override |
| public int getOwner() { |
| return myClassName; |
| } |
| |
| private ClassUsage(final int className) { |
| this.myClassName = className; |
| } |
| |
| private ClassUsage(final DataInput in) { |
| try { |
| myClassName = DataInputOutputUtil.readINT(in); |
| } |
| catch (IOException e) { |
| throw new BuildDataCorruptedException(e); |
| } |
| } |
| |
| @Override |
| public void save(final DataOutput out) { |
| try { |
| out.writeByte(CLASS_USAGE); |
| DataInputOutputUtil.writeINT(out, myClassName); |
| } |
| catch (IOException e) { |
| throw new BuildDataCorruptedException(e); |
| } |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) return true; |
| if (o == null || getClass() != o.getClass()) return false; |
| |
| final ClassUsage that = (ClassUsage)o; |
| |
| return myClassName == that.myClassName; |
| } |
| |
| @Override |
| public int hashCode() { |
| return myClassName; |
| } |
| |
| @Override |
| public void toStream(final DependencyContext context, final PrintStream stream) { |
| stream.println("ClassUsage: " + context.getValue(myClassName)); |
| } |
| } |
| |
| public static class ClassAsGenericBoundUsage extends ClassUsage { |
| public ClassAsGenericBoundUsage(int className) { |
| super(className); |
| } |
| |
| public ClassAsGenericBoundUsage(DataInput in) { |
| super(in); |
| } |
| |
| @Override |
| public int hashCode() { |
| return super.hashCode() + 3; |
| } |
| |
| @Override |
| public void save(final DataOutput out) { |
| try { |
| out.writeByte(CLASS_AS_GENERIC_BOUND_USAGE); |
| DataInputOutputUtil.writeINT(out, myClassName); |
| } |
| catch (IOException e) { |
| throw new BuildDataCorruptedException(e); |
| } |
| } |
| |
| @Override |
| public void toStream(final DependencyContext context, final PrintStream stream) { |
| stream.println("ClassAsGenericBoundUsage: " + context.getValue(myClassName)); |
| } |
| } |
| |
| public static class ClassExtendsUsage extends Usage { |
| protected final int myClassName; |
| |
| @Override |
| public int getOwner() { |
| return myClassName; |
| } |
| |
| private ClassExtendsUsage(final int className) { |
| this.myClassName = className; |
| } |
| |
| private ClassExtendsUsage(final DataInput in) { |
| try { |
| myClassName = DataInputOutputUtil.readINT(in); |
| } |
| catch (IOException e) { |
| throw new BuildDataCorruptedException(e); |
| } |
| } |
| |
| @Override |
| public void save(final DataOutput out) { |
| try { |
| out.writeByte(CLASS_EXTENDS_USAGE); |
| DataInputOutputUtil.writeINT(out, myClassName); |
| } |
| catch (IOException e) { |
| throw new BuildDataCorruptedException(e); |
| } |
| } |
| |
| @Override |
| public int hashCode() { |
| return myClassName + 1; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) return true; |
| if (o == null || getClass() != o.getClass()) return false; |
| |
| ClassExtendsUsage that = (ClassExtendsUsage)o; |
| |
| if (myClassName != that.myClassName) return false; |
| |
| return true; |
| } |
| |
| @Override |
| public void toStream(final DependencyContext context, final PrintStream stream) { |
| stream.println("ClassExtendsUsage: " + context.getValue(myClassName)); |
| } |
| } |
| |
| public static class ClassNewUsage extends ClassExtendsUsage { |
| public ClassNewUsage(int className) { |
| super(className); |
| } |
| |
| private ClassNewUsage(final DataInput in) { |
| super(in); |
| } |
| |
| @Override |
| public void save(final DataOutput out) { |
| try { |
| out.writeByte(CLASS_NEW_USAGE); |
| DataInputOutputUtil.writeINT(out, myClassName); |
| } |
| catch (IOException e) { |
| throw new BuildDataCorruptedException(e); |
| } |
| } |
| |
| @Override |
| public int hashCode() { |
| return myClassName + 2; |
| } |
| |
| @Override |
| public void toStream(final DependencyContext context, final PrintStream stream) { |
| stream.println("ClassNewUsage: " + context.getValue(myClassName)); |
| } |
| } |
| |
| public static class AnnotationUsage extends Usage { |
| public static final DataExternalizer<ElemType> elementTypeExternalizer = new DataExternalizer<ElemType>() { |
| @Override |
| public void save(@NotNull final DataOutput out, final ElemType value) throws IOException { |
| DataInputOutputUtil.writeINT(out, value.ordinal()); |
| } |
| |
| @Override |
| public ElemType read(@NotNull final DataInput in) throws IOException { |
| final int ordinal = DataInputOutputUtil.readINT(in); |
| for (ElemType value : ElemType.values()) { |
| if (value.ordinal() == ordinal) { |
| return value; |
| } |
| } |
| throw new IOException("Error reading ElementType enum value; unknown ordinal: " + ordinal); |
| } |
| }; |
| |
| final TypeRepr.ClassType myType; |
| final TIntHashSet myUsedArguments; |
| final Set<ElemType> myUsedTargets; |
| |
| public boolean satisfies(final Usage usage) { |
| if (usage instanceof AnnotationUsage) { |
| final AnnotationUsage annotationUsage = (AnnotationUsage)usage; |
| |
| if (!myType.equals(annotationUsage.myType)) { |
| return false; |
| } |
| |
| boolean argumentsSatisfy = false; |
| |
| if (myUsedArguments != null) { |
| final TIntHashSet arguments = new TIntHashSet(myUsedArguments.toArray()); |
| |
| arguments.removeAll(annotationUsage.myUsedArguments.toArray()); |
| |
| argumentsSatisfy = !arguments.isEmpty(); |
| } |
| |
| boolean targetsSatisfy = false; |
| |
| if (myUsedTargets != null) { |
| final Collection<ElemType> targets = EnumSet.copyOf(myUsedTargets); |
| |
| targets.retainAll(annotationUsage.myUsedTargets); |
| |
| targetsSatisfy = !targets.isEmpty(); |
| } |
| |
| return argumentsSatisfy || targetsSatisfy; |
| } |
| |
| return false; |
| } |
| |
| private AnnotationUsage(final TypeRepr.ClassType type, final TIntHashSet usedArguments, final Set<ElemType> targets) { |
| this.myType = type; |
| this.myUsedArguments = usedArguments; |
| this.myUsedTargets = targets; |
| } |
| |
| private AnnotationUsage(final DependencyContext context, final DataInput in) { |
| final DataExternalizer<TypeRepr.AbstractType> externalizer = TypeRepr.externalizer(context); |
| |
| try { |
| myType = (TypeRepr.ClassType)externalizer.read(in); |
| myUsedArguments = RW.read(new TIntHashSet(DEFAULT_SET_CAPACITY, DEFAULT_SET_LOAD_FACTOR), in); |
| myUsedTargets = (EnumSet<ElemType>)RW.read(elementTypeExternalizer, EnumSet.noneOf(ElemType.class), in); |
| } |
| catch (IOException e) { |
| throw new BuildDataCorruptedException(e); |
| } |
| } |
| |
| @Override |
| public void save(final DataOutput out) { |
| try { |
| out.writeByte(ANNOTATION_USAGE); |
| myType.save(out); |
| RW.save(myUsedArguments, out); |
| RW.save(myUsedTargets, elementTypeExternalizer, out); |
| } |
| catch (IOException e) { |
| throw new BuildDataCorruptedException(e); |
| } |
| } |
| |
| @Override |
| public int getOwner() { |
| return myType.className; |
| } |
| |
| @Override |
| public boolean equals(final Object o) { |
| if (this == o) return true; |
| if (o == null || getClass() != o.getClass()) return false; |
| |
| AnnotationUsage that = (AnnotationUsage)o; |
| |
| if (myUsedArguments != null ? !myUsedArguments.equals(that.myUsedArguments) : that.myUsedArguments != null) return false; |
| if (myUsedTargets != null ? !myUsedTargets.equals(that.myUsedTargets) : that.myUsedTargets != null) return false; |
| if (myType != null ? !myType.equals(that.myType) : that.myType != null) return false; |
| |
| return true; |
| } |
| |
| @Override |
| public int hashCode() { |
| int result = myType != null ? myType.hashCode() : 0; |
| result = 31 * result + (myUsedArguments != null ? myUsedArguments.hashCode() : 0); |
| result = 31 * result + (myUsedTargets != null ? myUsedTargets.hashCode() : 0); |
| return result; |
| } |
| |
| @Override |
| public void toStream(final DependencyContext context, final PrintStream stream) { |
| stream.println(" AnnotationUsage:"); |
| stream.println(" Type : " + myType.getDescr(context)); |
| |
| final List<String> arguments = new LinkedList<String>(); |
| |
| if (myUsedArguments != null) { |
| myUsedArguments.forEach(new TIntProcedure() { |
| @Override |
| public boolean execute(final int value) { |
| arguments.add(context.getValue(value)); |
| return true; |
| } |
| }); |
| } |
| |
| Collections.sort(arguments); |
| |
| final List<String> targets = new LinkedList<String>(); |
| |
| if (myUsedTargets != null) { |
| for (final ElemType e : myUsedTargets) { |
| targets.add(e.toString()); |
| } |
| } |
| |
| Collections.sort(targets); |
| |
| stream.println(" Arguments:"); |
| |
| for (final String s : arguments) { |
| stream.println(" " + s); |
| } |
| |
| stream.println(" Targets :"); |
| |
| for (final String s : targets) { |
| stream.println(" " + s); |
| } |
| } |
| } |
| |
| public static Usage createFieldUsage(final DependencyContext context, final int name, final int owner, final int descr) { |
| return context.getUsage(new FieldUsage(context, name, owner, descr)); |
| } |
| |
| public static Usage createFieldAssignUsage(final DependencyContext context, final int name, final int owner, final int descr) { |
| return context.getUsage(new FieldAssignUsage(context, name, owner, descr)); |
| } |
| |
| public static Usage createMethodUsage(final DependencyContext context, final int name, final int owner, final String descr) { |
| return context.getUsage(new MethodUsage(context, name, owner, descr)); |
| } |
| |
| public static Usage createMetaMethodUsage(final DependencyContext context, final int name, final int owner, final String descr) { |
| return context.getUsage(new MetaMethodUsage(context, name, owner, descr)); |
| } |
| |
| public static Usage createClassUsage(final DependencyContext context, final int name) { |
| return context.getUsage(new ClassUsage(name)); |
| } |
| |
| public static Usage createClassAsGenericBoundUsage(final DependencyContext context, final int name) { |
| return context.getUsage(new ClassAsGenericBoundUsage(name)); |
| } |
| |
| public static Usage createClassNewUsage(final DependencyContext context, final int name) { |
| return context.getUsage(new ClassNewUsage(name)); |
| } |
| |
| public static Usage createAnnotationUsage(final DependencyContext context, |
| final TypeRepr.ClassType type, |
| final TIntHashSet usedArguments, |
| final Set<ElemType> targets) { |
| return context.getUsage(new AnnotationUsage(type, usedArguments, targets)); |
| } |
| |
| public static DataExternalizer<Usage> externalizer(final DependencyContext context) { |
| return new DataExternalizer<Usage>() { |
| @Override |
| public void save(@NotNull final DataOutput out, final Usage value) throws IOException { |
| value.save(out); |
| } |
| |
| @Override |
| public Usage read(@NotNull DataInput in) throws IOException { |
| final byte tag = in.readByte(); |
| switch (tag) { |
| case CLASS_USAGE: |
| return context.getUsage(new ClassUsage(in)); |
| |
| case CLASS_AS_GENERIC_BOUND_USAGE: |
| return context.getUsage(new ClassAsGenericBoundUsage(in)); |
| |
| case CLASS_EXTENDS_USAGE: |
| return context.getUsage(new ClassExtendsUsage(in)); |
| |
| case CLASS_NEW_USAGE: |
| return context.getUsage(new ClassNewUsage(in)); |
| |
| case FIELD_USAGE: |
| return context.getUsage(new FieldUsage(context, in)); |
| |
| case FIELD_ASSIGN_USAGE: |
| return context.getUsage(new FieldAssignUsage(context, in)); |
| |
| case METHOD_USAGE: |
| return context.getUsage(new MethodUsage(context, in)); |
| |
| case ANNOTATION_USAGE: |
| return context.getUsage(new AnnotationUsage(context, in)); |
| |
| case METAMETHOD_USAGE: |
| return context.getUsage(new MetaMethodUsage(in)); |
| } |
| |
| assert (false); |
| |
| return null; |
| } |
| }; |
| } |
| } |