package org.jetbrains.ether.dependencyView;
import org.jetbrains.ether.RW;
import org.objectweb.asm.Type;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.lang.annotation.ElementType;
import java.util.*;
/**
* Created by IntelliJ IDEA.
* User: db
* Date: 14.02.11
* Time: 5:11
* To change this template use File | Settings | File Templates.
*/
public class UsageRepr {
private final static TypeRepr.AbstractType[] dummyAbstractType = new TypeRepr.AbstractType[0];
private final static Map<Usage, Usage> map = new HashMap<Usage, Usage>();
private static Usage getUsage(final Usage u) {
final Usage r = map.get(u);
if (r == null) {
map.put(u, u);
return u;
}
return r;
}
public static class Cluster implements RW.Writable {
final Set<Usage> usages = new HashSet<Usage>();
final Map<Usage, Set<StringCache.S>> residentialMap = new HashMap<Usage, Set<StringCache.S>>();
public Cluster() {
}
public Cluster(final BufferedReader r) {
final int size = RW.readInt(r);
for (int i = 0; i < size; i++) {
final Usage u = reader.read(r);
final Set<StringCache.S> s = (Set<StringCache.S>) RW.readMany(r, StringCache.reader, new HashSet<StringCache.S>());
usages.add(u);
residentialMap.put(u, s);
}
}
public void write(final BufferedWriter w) {
RW.writeln(w, Integer.toString(usages.size()));
for (Usage u : usages) {
u.write(w);
RW.writeln(w, residentialMap.get(u));
}
}
public void addUsage(final String residence, final Usage usage) {
final StringCache.S r = StringCache.get(residence);
Set<StringCache.S> s = residentialMap.get(usage);
if (s == null) {
s = new HashSet<StringCache.S>();
residentialMap.put(usage, s);
}
s.add(r);
usages.add(usage);
}
public Set<Usage> getUsages() {
return usages;
}
public Set<StringCache.S> getResidence(final Usage usage) {
return residentialMap.get(usage);
}
public void updateCluster(final Cluster c) {
usages.addAll(c.getUsages());
for (Map.Entry<Usage, Set<StringCache.S>> e : c.residentialMap.entrySet()) {
final Usage u = e.getKey();
final Set<StringCache.S> v = e.getValue();
final Set<StringCache.S> s = residentialMap.get(u);
if (s == null) {
residentialMap.put(u, v);
} else {
s.addAll(v);
}
}
}
public boolean isEmpty() {
return usages.isEmpty();
}
}
public static abstract class Usage implements RW.Writable {
public abstract StringCache.S getOwner();
}
public static abstract class FMUsage extends Usage {
public final StringCache.S name;
public final StringCache.S owner;
@Override
public StringCache.S getOwner() {
return owner;
}
protected FMUsage(final String n, final String o) {
name = StringCache.get(n);
owner = StringCache.get(o);
}
protected FMUsage(final BufferedReader r) {
name = StringCache.get(RW.readString(r));
owner = StringCache.get(RW.readString(r));
}
}
public static class FieldUsage extends FMUsage {
public final TypeRepr.AbstractType type;
private FieldUsage(final String n, final String o, final String d) {
super(n, o);
type = TypeRepr.getType(d);
}
private FieldUsage(final BufferedReader r) {
super(r);
type = TypeRepr.reader.read(r);
}
public void write(final BufferedWriter w) {
RW.writeln(w, "fieldUsage");
RW.writeln(w, name.value);
RW.writeln(w, owner.value);
type.write(w);
}
@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 type.equals(that.type) && name.equals(that.name) && owner.equals(that.owner);
}
@Override
public int hashCode() {
return 31 * (31 * type.hashCode() + (name.hashCode())) + owner.hashCode();
}
}
public static class FieldAssignUsage extends FieldUsage {
private FieldAssignUsage(final String n, final String o, final String d) {
super(n, o, d);
}
private FieldAssignUsage(final BufferedReader r) {
super(r);
}
public void write(final BufferedWriter w) {
RW.writeln(w, "fieldAssignUsage");
RW.writeln(w, name.value);
RW.writeln(w, owner.value);
type.write(w);
}
@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 type.equals(that.type) && name.equals(that.name) && owner.equals(that.owner);
}
@Override
public int hashCode() {
return super.hashCode() + 1;
}
}
public static class MethodUsage extends FMUsage {
public final TypeRepr.AbstractType[] argumentTypes;
public final TypeRepr.AbstractType returnType;
private MethodUsage(final String n, final String o, final String d) {
super(n, o);
argumentTypes = TypeRepr.getType(Type.getArgumentTypes(d));
returnType = TypeRepr.getType(Type.getReturnType(d));
}
private MethodUsage(final BufferedReader r) {
super(r);
argumentTypes = RW.readMany(r, TypeRepr.reader, new ArrayList<TypeRepr.AbstractType>()).toArray(dummyAbstractType);
returnType = TypeRepr.reader.read(r);
}
public void write(final BufferedWriter w) {
RW.writeln(w, "methodUsage");
RW.writeln(w, name.value);
RW.writeln(w, owner.value);
RW.writeln(w, argumentTypes, TypeRepr.fromAbstractType);
returnType.write(w);
}
@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(argumentTypes, that.argumentTypes)) return false;
if (returnType != null ? !returnType.equals(that.returnType) : that.returnType != null) return false;
if (name != null ? !name.equals(that.name) : that.name != null) return false;
if (owner != null ? !owner.equals(that.owner) : that.owner != null) return false;
return Arrays.equals(argumentTypes, that.argumentTypes) &&
returnType.equals(that.returnType) &&
name.equals(that.name) &&
owner.equals(that.owner);
}
@Override
public int hashCode() {
return ((31 * Arrays.hashCode(argumentTypes) + (returnType.hashCode())) * 31 + (name.hashCode())) * 31 + (owner.hashCode());
}
}
public static class ClassUsage extends Usage {
final StringCache.S className;
@Override
public StringCache.S getOwner() {
return className;
}
private ClassUsage(final String n) {
className = StringCache.get(n);
}
private ClassUsage(final StringCache.S n) {
className = n;
}
private ClassUsage(final BufferedReader r) {
className = StringCache.get(RW.readString(r));
}
private ClassUsage(final BufferedReader r, boolean b) {
super();
className = StringCache.get(RW.readString(r));
}
public void write(final BufferedWriter w) {
RW.writeln(w, "classUsage");
RW.writeln(w, className.value);
}
@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 className.equals(that.className);
}
@Override
public int hashCode() {
return className.hashCode();
}
}
public static class ClassExtendsUsage extends Usage {
protected final StringCache.S className;
@Override
public StringCache.S getOwner() {
return className;
}
public ClassExtendsUsage(final StringCache.S n) {
className = n;
}
public ClassExtendsUsage(final BufferedReader r) {
className = StringCache.get(RW.readString(r));
}
public void write(final BufferedWriter w) {
RW.writeln(w, "classExtendsUsage");
RW.writeln(w, className.value);
}
@Override
public int hashCode() {
return className.hashCode() + 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 (!className.equals(that.className)) return false;
return true;
}
}
public static class ClassNewUsage extends ClassExtendsUsage {
public ClassNewUsage(StringCache.S n) {
super(n);
}
public ClassNewUsage(BufferedReader r) {
super(r);
}
public void write(final BufferedWriter w) {
RW.writeln(w, "classNewUsage");
RW.writeln(w, className.value);
}
@Override
public int hashCode() {
return className.hashCode() + 2;
}
}
public static class AnnotationUsage extends Usage {
public static final RW.Reader<ElementType> elementTypeReader = new RW.Reader<ElementType>() {
public ElementType read(final BufferedReader r) {
return ElementType.valueOf(RW.readString(r));
}
};
public static final RW.ToWritable<ElementType> elementTypeToWritable = new RW.ToWritable<ElementType>() {
public RW.Writable convert(final ElementType x) {
return new RW.Writable() {
public void write(final BufferedWriter w) {
RW.writeln(w, x.toString());
}
};
}
};
final TypeRepr.ClassType type;
final Collection<StringCache.S> usedArguments;
final Collection<ElementType> usedTargets;
public boolean satisfies(final Usage usage) {
if (usage instanceof AnnotationUsage) {
final AnnotationUsage annotationUsage = (AnnotationUsage) usage;
if (!type.equals(annotationUsage.type)) {
return false;
}
boolean argumentsSatisfy = false;
if (usedArguments != null) {
final Collection<StringCache.S> arguments = new HashSet<StringCache.S>(usedArguments);
arguments.removeAll(annotationUsage.usedArguments);
argumentsSatisfy = !arguments.isEmpty();
}
boolean targetsSatisfy = false;
if (usedTargets != null) {
final Collection<ElementType> targets = new HashSet<ElementType>(usedTargets);
targets.retainAll(annotationUsage.usedTargets);
targetsSatisfy = !targets.isEmpty();
}
return argumentsSatisfy || targetsSatisfy;
}
return false;
}
private AnnotationUsage(final TypeRepr.ClassType type, final Collection<StringCache.S> usedArguments, final Collection<ElementType> targets) {
this.type = type;
this.usedArguments = usedArguments;
this.usedTargets = targets;
}
private AnnotationUsage(final BufferedReader r) {
type = (TypeRepr.ClassType) TypeRepr.reader.read(r);
usedArguments = RW.readMany(r, StringCache.reader, new HashSet<StringCache.S>());
usedTargets = RW.readMany(r, elementTypeReader, new HashSet<ElementType>());
}
@Override
public StringCache.S getOwner() {
return type.className;
}
public void write(final BufferedWriter w) {
RW.writeln(w, "annotationUsage");
type.write(w);
RW.writeln(w, usedArguments);
RW.writeln(w, usedTargets, elementTypeToWritable);
}
@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 (usedArguments != null ? !usedArguments.equals(that.usedArguments) : that.usedArguments != null)
return false;
if (usedTargets != null ? !usedTargets.equals(that.usedTargets) : that.usedTargets != null) return false;
if (type != null ? !type.equals(that.type) : that.type != null) return false;
return true;
}
@Override
public int hashCode() {
int result = type != null ? type.hashCode() : 0;
result = 31 * result + (usedArguments != null ? usedArguments.hashCode() : 0);
result = 31 * result + (usedTargets != null ? usedTargets.hashCode() : 0);
return result;
}
}
public static Usage createFieldUsage(final String name, final String owner, final String descr) {
return getUsage(new FieldUsage(name, owner, descr));
}
public static Usage createFieldAssignUsage(final String name, final String owner, final String descr) {
return getUsage(new FieldAssignUsage(name, owner, descr));
}
public static Usage createMethodUsage(final String name, final String owner, final String descr) {
return getUsage(new MethodUsage(name, owner, descr));
}
public static Usage createClassUsage(final String name) {
return getUsage(new ClassUsage(name));
}
public static Usage createClassUsage(final StringCache.S name) {
return getUsage(new ClassUsage(name));
}
public static Usage createClassExtendsUsage(final StringCache.S name) {
return getUsage(new ClassExtendsUsage(name));
}
public static Usage createClassNewUsage(final StringCache.S name) {
return getUsage(new ClassNewUsage(name));
}
public static Usage createAnnotationUsage(final TypeRepr.ClassType type, final Collection<StringCache.S> usedArguments, final Collection<ElementType> targets) {
return getUsage(new AnnotationUsage(type, usedArguments, targets));
}
public static RW.Reader<Usage> reader = new RW.Reader<Usage>() {
public Usage read(final BufferedReader r) {
final String tag = RW.readString(r);
if (tag.equals("classUsage")) {
return getUsage(new ClassUsage(r));
} else if (tag.equals("fieldUsage")) {
return getUsage(new FieldUsage(r));
} else if (tag.equals("fieldAssignUsage")) {
return getUsage(new FieldAssignUsage(r));
} else if (tag.equals("methodUsage")) {
return getUsage(new MethodUsage(r));
} else if (tag.equals("classExtendsUsage")) {
return getUsage(new ClassExtendsUsage(r));
} else if (tag.equals("classNewUsage")) {
return getUsage(new ClassNewUsage(r));
} else return getUsage(new AnnotationUsage(r));
}
};
}