blob: 2cbfe0592f3a9b93e240fe6b232b465fb85d8744 [file] [log] [blame]
/*
* 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.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.builders.storage.BuildDataCorruptedException;
import org.jetbrains.org.objectweb.asm.Opcodes;
import java.io.*;
import java.lang.annotation.RetentionPolicy;
import java.util.*;
/**
* @author: db
* Date: 01.02.11
*/
public class ClassRepr extends Proto {
private final DependencyContext myContext;
private final int myFileName;
private final TypeRepr.ClassType mySuperClass;
private final Set<TypeRepr.AbstractType> myInterfaces;
private final Set<ElemType> myAnnotationTargets;
private final RetentionPolicy myRetentionPolicy;
private final Set<FieldRepr> myFields;
private final Set<MethodRepr> myMethods;
private final Set<UsageRepr.Usage> myUsages;
private final int myOuterClassName;
private final boolean myIsLocal;
private final boolean myIsAnonymous;
public Set<MethodRepr> getMethods() {
return myMethods;
}
public Set<FieldRepr> getFields() {
return myFields;
}
public int getOuterClassName() {
return myOuterClassName;
}
public boolean isLocal() {
return myIsLocal;
}
public boolean isAnonymous() {
return myIsAnonymous;
}
public TypeRepr.ClassType getSuperClass() {
return mySuperClass;
}
public RetentionPolicy getRetentionPolicy() {
return myRetentionPolicy;
}
public Set<UsageRepr.Usage> getUsages() {
return myUsages;
}
public boolean addUsage(final UsageRepr.Usage usage) {
return myUsages.add(usage);
}
public boolean isInterface() {
return (access & Opcodes.ACC_INTERFACE) != 0;
}
public abstract static class Diff extends Difference {
public abstract Specifier<TypeRepr.AbstractType> interfaces();
public abstract Specifier<FieldRepr> fields();
public abstract Specifier<MethodRepr> methods();
public abstract Specifier<ElemType> targets();
public abstract boolean retentionChanged();
public abstract boolean extendsAdded();
public boolean no() {
return base() == NONE &&
interfaces().unchanged() &&
fields().unchanged() &&
methods().unchanged() &&
targets().unchanged() &&
!retentionChanged();
}
}
public Diff difference(final Proto past) {
final ClassRepr pastClass = (ClassRepr)past;
final Difference diff = super.difference(past);
int base = diff.base();
if (!mySuperClass.equals(pastClass.mySuperClass)) {
base |= Difference.SUPERCLASS;
}
if (!myUsages.equals(pastClass.myUsages)) {
base |= Difference.USAGES;
}
final int d = base;
return new Diff() {
@Override
public boolean extendsAdded() {
if ((d & Difference.SUPERCLASS) <= 0) {
return false;
}
final String pastSuperName = myContext.getValue(((ClassRepr)past).mySuperClass.className);
return "java/lang/Object".equals(pastSuperName);
}
@Override
public boolean packageLocalOn() {
return diff.packageLocalOn();
}
@Override
public int addedModifiers() {
return diff.addedModifiers();
}
@Override
public int removedModifiers() {
return diff.removedModifiers();
}
@Override
public Difference.Specifier<TypeRepr.AbstractType> interfaces() {
return Difference.make(pastClass.myInterfaces, myInterfaces);
}
@Override
public Difference.Specifier<FieldRepr> fields() {
return Difference.make(pastClass.myFields, myFields);
}
@Override
public Difference.Specifier<MethodRepr> methods() {
return Difference.make(pastClass.myMethods, myMethods);
}
@Override
public Specifier<ElemType> targets() {
return Difference.make(pastClass.myAnnotationTargets, myAnnotationTargets);
}
@Override
public boolean retentionChanged() {
return !((myRetentionPolicy == null && pastClass.myRetentionPolicy == RetentionPolicy.CLASS) ||
(myRetentionPolicy == RetentionPolicy.CLASS && pastClass.myRetentionPolicy == null) ||
(myRetentionPolicy == pastClass.myRetentionPolicy));
}
@Override
public int base() {
return d;
}
@Override
public boolean hadValue() {
return false;
}
@Override
public boolean weakedAccess() {
return diff.weakedAccess();
}
};
}
@NotNull
public int[] getSupers() {
final int[] result = new int[myInterfaces.size() + 1];
result[0] = mySuperClass.className;
int i = 1;
for (TypeRepr.AbstractType t : myInterfaces) {
result[i++] = ((TypeRepr.ClassType)t).className;
}
return result;
}
public void updateClassUsages(final DependencyContext context, final Set<UsageRepr.Usage> s) {
mySuperClass.updateClassUsages(context, name, s);
for (TypeRepr.AbstractType t : myInterfaces) {
t.updateClassUsages(context, name, s);
}
for (MethodRepr m : myMethods) {
m.updateClassUsages(context, name, s);
}
for (FieldRepr f : myFields) {
f.updateClassUsages(context, name, s);
}
}
public ClassRepr(final DependencyContext context, final int a, final int fn, final int n, final int sig,
final int sup,
final String[] i,
final Set<FieldRepr> f,
final Set<MethodRepr> m,
final Set<ElemType> targets,
final RetentionPolicy policy,
final int outerClassName,
final boolean localClassFlag,
final boolean anonymousClassFlag,
final Set<UsageRepr.Usage> usages) {
super(a, sig, n);
this.myContext = context;
myFileName = fn;
mySuperClass = TypeRepr.createClassType(context, sup);
myInterfaces = (Set<TypeRepr.AbstractType>)TypeRepr.createClassType(context, i, new THashSet<TypeRepr.AbstractType>(1));
myFields = f;
myMethods = m;
this.myAnnotationTargets = targets;
this.myRetentionPolicy = policy;
this.myOuterClassName = outerClassName;
this.myIsLocal = localClassFlag;
this.myIsAnonymous = anonymousClassFlag;
this.myUsages = usages;
}
public ClassRepr(final DependencyContext context, final DataInput in) {
super(in);
try {
this.myContext = context;
myFileName = DataInputOutputUtil.readINT(in);
mySuperClass = (TypeRepr.ClassType)TypeRepr.externalizer(context).read(in);
myInterfaces = (Set<TypeRepr.AbstractType>)RW.read(TypeRepr.externalizer(context), new THashSet<TypeRepr.AbstractType>(1), in);
myFields = (Set<FieldRepr>)RW.read(FieldRepr.externalizer(context), new THashSet<FieldRepr>(), in);
myMethods = (Set<MethodRepr>)RW.read(MethodRepr.externalizer(context), new THashSet<MethodRepr>(), in);
myAnnotationTargets = (Set<ElemType>)RW.read(UsageRepr.AnnotationUsage.elementTypeExternalizer, EnumSet.noneOf(ElemType.class), in);
final String s = RW.readUTF(in);
myRetentionPolicy = s.length() == 0 ? null : RetentionPolicy.valueOf(s);
myOuterClassName = DataInputOutputUtil.readINT(in);
int flags = DataInputOutputUtil.readINT(in);
myIsLocal = (flags & LOCAL_MASK) != 0;
myIsAnonymous = (flags & ANONYMOUS_MASK) != 0;
myUsages =(Set<UsageRepr.Usage>)RW.read(UsageRepr.externalizer(context), new THashSet<UsageRepr.Usage>(), in);
}
catch (IOException e) {
throw new BuildDataCorruptedException(e);
}
}
private static final int LOCAL_MASK = 1;
private static final int ANONYMOUS_MASK = 2;
@Override
public void save(final DataOutput out) {
try {
super.save(out);
DataInputOutputUtil.writeINT(out, myFileName);
mySuperClass.save(out);
RW.save(myInterfaces, out);
RW.save(myFields, out);
RW.save(myMethods, out);
RW.save(myAnnotationTargets, UsageRepr.AnnotationUsage.elementTypeExternalizer, out);
RW.writeUTF(out, myRetentionPolicy == null ? "" : myRetentionPolicy.toString());
DataInputOutputUtil.writeINT(out, myOuterClassName);
DataInputOutputUtil.writeINT(out, (myIsLocal ? LOCAL_MASK:0) | (myIsAnonymous ? ANONYMOUS_MASK : 0));
RW.save(myUsages, UsageRepr.externalizer(myContext), out);
}
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;
ClassRepr classRepr = (ClassRepr)o;
if (myFileName != classRepr.myFileName) return false;
if (name != classRepr.name) return false;
return true;
}
@Override
public int hashCode() {
return 31 * myFileName + name;
}
public UsageRepr.Usage createUsage() {
return UsageRepr.createClassUsage(myContext, name);
}
public String getPackageName() {
final String strValue = myContext.getValue(name);
return strValue != null? getPackageName(strValue) : null;
}
public String getShortName() {
final String strValue = myContext.getValue(name);
return strValue != null? getShortName(strValue) : null;
}
@NotNull
public static String getPackageName(@NotNull final String raw) {
final int index = raw.lastIndexOf('/');
if (index == -1) {
return "";
}
return raw.substring(0, index);
}
@NotNull
public static String getShortName(@NotNull final String fqName) {
final int index = fqName.lastIndexOf('/');
if (index == -1) {
return fqName;
}
return fqName.substring(index + 1);
}
@Nullable
public FieldRepr findField(final int name) {
for (FieldRepr f : myFields) {
if (f.name == name) {
return f;
}
}
return null;
}
@NotNull
public Collection<MethodRepr> findMethods(final MethodRepr.Predicate p) {
final Collection<MethodRepr> result = new LinkedList<MethodRepr>();
for (MethodRepr mm : myMethods) {
if (p.satisfy(mm)) {
result.add(mm);
}
}
return result;
}
public static DataExternalizer<ClassRepr> externalizer(final DependencyContext context) {
return new DataExternalizer<ClassRepr>() {
@Override
public void save(@NotNull final DataOutput out, final ClassRepr value) throws IOException {
value.save(out);
}
@Override
public ClassRepr read(@NotNull final DataInput in) throws IOException {
return new ClassRepr(context, in);
}
};
}
@Override
public void toStream(final DependencyContext context, final PrintStream stream) {
super.toStream(context, stream);
stream.print(" Filename : ");
stream.println(context.getValue(myFileName));
stream.print(" Superclass : ");
stream.println(mySuperClass == null ? "<null>" : mySuperClass.getDescr(context));
stream.print(" Interfaces : ");
final TypeRepr.AbstractType[] is = myInterfaces.toArray(new TypeRepr.AbstractType[myInterfaces.size()]);
Arrays.sort(is, new Comparator<TypeRepr.AbstractType>() {
@Override
public int compare(final TypeRepr.AbstractType o1, final TypeRepr.AbstractType o2) {
return o1.getDescr(context).compareTo(o2.getDescr(context));
}
});
for (final TypeRepr.AbstractType t : is) {
stream.print(t.getDescr(context));
stream.print(" ");
}
stream.println();
stream.print(" Targets : ");
final ElemType[] es = myAnnotationTargets.toArray(new ElemType[myAnnotationTargets.size()]);
Arrays.sort(es);
for (final ElemType e : es) {
stream.print(e);
stream.print("; ");
}
stream.println();
stream.print(" Policy : ");
stream.println(myRetentionPolicy);
stream.print(" Outer class: ");
stream.println(context.getValue(myOuterClassName));
stream.print(" Local class: ");
stream.println(myIsLocal);
stream.print(" Anonymous class: ");
stream.println(myIsAnonymous);
stream.println(" Fields:");
final FieldRepr[] fs = myFields.toArray(new FieldRepr[myFields.size()]);
Arrays.sort(fs, new Comparator<FieldRepr>() {
@Override
public int compare(final FieldRepr o1, final FieldRepr o2) {
if (o1.name == o2.name) {
return o1.myType.getDescr(context).compareTo(o2.myType.getDescr(context));
}
return context.getValue(o1.name).compareTo(context.getValue(o2.name));
}
});
for (final FieldRepr f : fs) {
f.toStream(context, stream);
}
stream.println(" End Of Fields");
stream.println(" Methods:");
final MethodRepr[] ms = myMethods.toArray(new MethodRepr[myMethods.size()]);
Arrays.sort(ms, new Comparator<MethodRepr>() {
@Override
public int compare(final MethodRepr o1, final MethodRepr o2) {
if (o1.name == o2.name) {
final String d1 = o1.myType.getDescr(context);
final String d2 = o2.myType.getDescr(context);
final int c = d1.compareTo(d2);
if (c == 0) {
final int l1 = o1.myArgumentTypes.length;
final int l2 = o2.myArgumentTypes.length;
if (l1 == l2) {
for (int i = 0; i<l1; i++) {
final String d11 = o1.myArgumentTypes[i].getDescr(context);
final String d22 = o2.myArgumentTypes[i].getDescr(context);
final int cc = d11.compareTo(d22);
if (cc != 0) {
return cc;
}
}
return 0;
}
return l1 -l2;
}
return c;
}
return context.getValue(o1.name).compareTo(context.getValue(o2.name));
}
});
for (final MethodRepr m : ms) {
m.toStream(context, stream);
}
stream.println(" End Of Methods");
stream.println(" Usages:");
final List<String> usages = new LinkedList<String>();
for (final UsageRepr.Usage u : myUsages) {
final ByteArrayOutputStream bas = new ByteArrayOutputStream();
u.toStream(myContext, new PrintStream(bas));
try {
bas.close();
}
catch (final IOException e) {
throw new BuildDataCorruptedException(e);
}
usages.add(bas.toString());
}
Collections.sort(usages);
for (final String s : usages) {
stream.println(s);
}
stream.println(" End Of Usages");
}
}