package aQute.bnd.osgi; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.osgi.annotation.versioning.ProviderType; import aQute.libg.generics.Create; public class Descriptors { Map<String,TypeRef> typeRefCache = Create.map(); Map<String,Descriptor> descriptorCache = Create.map(); Map<String,PackageRef> packageCache = Create.map(); // MUST BE BEFORE PRIMITIVES, THEY USE THE DEFAULT PACKAGE!! final static PackageRef DEFAULT_PACKAGE = new PackageRef(); final static PackageRef PRIMITIVE_PACKAGE = new PackageRef(); final static TypeRef VOID = new ConcreteRef("V", "void", PRIMITIVE_PACKAGE); final static TypeRef BOOLEAN = new ConcreteRef("Z", "boolean", PRIMITIVE_PACKAGE); final static TypeRef BYTE = new ConcreteRef("B", "byte", PRIMITIVE_PACKAGE); final static TypeRef CHAR = new ConcreteRef("C", "char", PRIMITIVE_PACKAGE); final static TypeRef SHORT = new ConcreteRef("S", "short", PRIMITIVE_PACKAGE); final static TypeRef INTEGER = new ConcreteRef("I", "int", PRIMITIVE_PACKAGE); final static TypeRef LONG = new ConcreteRef("J", "long", PRIMITIVE_PACKAGE); final static TypeRef DOUBLE = new ConcreteRef("D", "double", PRIMITIVE_PACKAGE); final static TypeRef FLOAT = new ConcreteRef("F", "float", PRIMITIVE_PACKAGE); public enum SignatureType { TYPEVAR, METHOD, FIELD; } public class Signature { public Map<String,Signature> typevariables = new HashMap<String,Signature>(); public Signature type; public List<Signature> parameters; } { packageCache.put("", DEFAULT_PACKAGE); } @ProviderType public interface TypeRef extends Comparable<TypeRef> { String getBinary(); String getShorterName(); String getFQN(); String getPath(); boolean isPrimitive(); TypeRef getComponentTypeRef(); TypeRef getClassRef(); PackageRef getPackageRef(); String getShortName(); boolean isJava(); boolean isObject(); String getSourcePath(); String getDottedOnly(); } public static class PackageRef implements Comparable<PackageRef> { final String binaryName; final String fqn; final boolean java; PackageRef(String binaryName) { this.binaryName = fqnToBinary(binaryName); this.fqn = binaryToFQN(binaryName); this.java = this.fqn.startsWith("java."); // && // !this.fqn.equals("java.sql)" // For some reason I excluded java.sql but the classloader will // delegate anyway. So lost the understanding why I did it?? } PackageRef() { this.binaryName = ""; this.fqn = "."; this.java = false; } public PackageRef getDuplicate() { return new PackageRef(binaryName + Constants.DUPLICATE_MARKER); } public String getFQN() { return fqn; } public String getBinary() { return binaryName; } public String getPath() { return binaryName; } public boolean isJava() { return java; } @Override public String toString() { return fqn; } boolean isDefaultPackage() { return this.fqn.equals("."); } boolean isPrimitivePackage() { return this == PRIMITIVE_PACKAGE; } public int compareTo(PackageRef other) { return fqn.compareTo(other.fqn); } @Override public boolean equals(Object o) { assert o instanceof PackageRef; return o == this; } @Override public int hashCode() { return super.hashCode(); } /** * Decide if the package is a metadata package. * */ public boolean isMetaData() { if (isDefaultPackage()) return true; for (int i = 0; i < Constants.METAPACKAGES.length; i++) { if (fqn.startsWith(Constants.METAPACKAGES[i])) return true; } return false; } } // We "intern" the private static class ConcreteRef implements TypeRef { final String binaryName; final String fqn; final boolean primitive; final PackageRef packageRef; ConcreteRef(PackageRef packageRef, String binaryName) { this.binaryName = binaryName; this.fqn = binaryToFQN(binaryName); this.primitive = false; this.packageRef = packageRef; } ConcreteRef(String binaryName, String fqn, PackageRef pref) { this.binaryName = binaryName; this.fqn = fqn; this.primitive = true; this.packageRef = pref; } public String getBinary() { return binaryName; } public String getPath() { return binaryName + ".class"; } public String getSourcePath() { return binaryName + ".java"; } public String getFQN() { return fqn; } public String getDottedOnly() { return fqn.replace('$', '.'); } public boolean isPrimitive() { return primitive; } public TypeRef getComponentTypeRef() { return null; } public TypeRef getClassRef() { return this; } public PackageRef getPackageRef() { return packageRef; } public String getShortName() { int n = binaryName.lastIndexOf('/'); return binaryName.substring(n + 1); } @Override public String getShorterName() { String name = getShortName(); int n = name.lastIndexOf('$'); if (n <= 0) return name; return name.substring(n + 1); } public boolean isJava() { return packageRef.isJava(); } @Override public String toString() { return fqn; } public boolean isObject() { return fqn.equals("java.lang.Object"); } @Override public boolean equals(Object other) { assert other instanceof TypeRef; return this == other; } public int compareTo(TypeRef other) { if (this == other) return 0; return fqn.compareTo(other.getFQN()); } @Override public int hashCode() { return super.hashCode(); } } private static class ArrayRef implements TypeRef { final TypeRef component; ArrayRef(TypeRef component) { this.component = component; } public String getBinary() { return "[" + component.getBinary(); } public String getFQN() { return component.getFQN() + "[]"; } public String getPath() { return component.getPath(); } public String getSourcePath() { return component.getSourcePath(); } public boolean isPrimitive() { return false; } public TypeRef getComponentTypeRef() { return component; } public TypeRef getClassRef() { return component.getClassRef(); } @Override public boolean equals(Object other) { if (other == null || other.getClass() != getClass()) return false; return component.equals(((ArrayRef) other).component); } public PackageRef getPackageRef() { return component.getPackageRef(); } public String getShortName() { return component.getShortName() + "[]"; } public boolean isJava() { return component.isJava(); } @Override public String toString() { return component.toString() + "[]"; } public boolean isObject() { return false; } public String getDottedOnly() { return component.getDottedOnly(); } public int compareTo(TypeRef other) { if (this == other) return 0; return getFQN().compareTo(other.getFQN()); } @Override public int hashCode() { return super.hashCode(); } @Override public String getShorterName() { String name = getShortName(); int n = name.lastIndexOf('$'); if (n <= 0) return name; return name.substring(n + 1); } } public TypeRef getTypeRef(String binaryClassName) { assert !binaryClassName.endsWith(".class"); TypeRef ref = typeRefCache.get(binaryClassName); if (ref != null) return ref; if (binaryClassName.startsWith("[")) { ref = getTypeRef(binaryClassName.substring(1)); ref = new ArrayRef(ref); } else { if (binaryClassName.length() == 1) { switch (binaryClassName.charAt(0)) { case 'V' : return VOID; case 'B' : return BYTE; case 'C' : return CHAR; case 'I' : return INTEGER; case 'S' : return SHORT; case 'D' : return DOUBLE; case 'F' : return FLOAT; case 'J' : return LONG; case 'Z' : return BOOLEAN; } // falls trough for other 1 letter class names } if (binaryClassName.startsWith("L") && binaryClassName.endsWith(";")) { binaryClassName = binaryClassName.substring(1, binaryClassName.length() - 1); } ref = typeRefCache.get(binaryClassName); if (ref != null) return ref; PackageRef pref; int n = binaryClassName.lastIndexOf('/'); if (n < 0) pref = DEFAULT_PACKAGE; else pref = getPackageRef(binaryClassName.substring(0, n)); ref = new ConcreteRef(pref, binaryClassName); } typeRefCache.put(binaryClassName, ref); return ref; } public PackageRef getPackageRef(String binaryPackName) { if (binaryPackName.indexOf('.') >= 0) { binaryPackName = binaryPackName.replace('.', '/'); } PackageRef ref = packageCache.get(binaryPackName); if (ref != null) return ref; // // Check here if a package is actually a nested class // com.example.Foo.Bar should have package com.example, // not com.example.Foo. // ref = new PackageRef(binaryPackName); packageCache.put(binaryPackName, ref); return ref; } public Descriptor getDescriptor(String descriptor) { Descriptor d = descriptorCache.get(descriptor); if (d != null) return d; d = new Descriptor(descriptor); descriptorCache.put(descriptor, d); return d; } public class Descriptor { final TypeRef type; final TypeRef[] prototype; final String descriptor; Descriptor(String descriptor) { this.descriptor = descriptor; int index = 0; List<TypeRef> types = Create.list(); if (descriptor.charAt(index) == '(') { index++; while (descriptor.charAt(index) != ')') { index = parse(types, descriptor, index); } index++; // skip ) prototype = types.toArray(new TypeRef[0]); types.clear(); } else prototype = null; index = parse(types, descriptor, index); type = types.get(0); } int parse(List<TypeRef> types, String descriptor, int index) { char c; StringBuilder sb = new StringBuilder(); while ((c = descriptor.charAt(index++)) == '[') { sb.append('['); } switch (c) { case 'L' : while ((c = descriptor.charAt(index++)) != ';') { // TODO sb.append(c); } break; case 'V' : case 'B' : case 'C' : case 'I' : case 'S' : case 'D' : case 'F' : case 'J' : case 'Z' : sb.append(c); break; default : throw new IllegalArgumentException( "Invalid type in descriptor: " + c + " from " + descriptor + "[" + index + "]"); } types.add(getTypeRef(sb.toString())); return index; } public TypeRef getType() { return type; } public TypeRef[] getPrototype() { return prototype; } @Override public boolean equals(Object other) { if (other == null || other.getClass() != getClass()) return false; return Arrays.equals(prototype, ((Descriptor) other).prototype) && type == ((Descriptor) other).type; } @Override public int hashCode() { return prototype == null ? type.hashCode() : type.hashCode() ^ Arrays.hashCode(prototype); } @Override public String toString() { return descriptor; } } /** * Return the short name of a FQN */ public static String getShortName(String fqn) { assert fqn.indexOf('/') < 0; int n = fqn.lastIndexOf('.'); if (n >= 0) { return fqn.substring(n + 1); } return fqn; } public static String binaryToFQN(String binary) { StringBuilder sb = new StringBuilder(); for (int i = 0, l = binary.length(); i < l; i++) { char c = binary.charAt(i); if (c == '/') sb.append('.'); else sb.append(c); } String result = sb.toString(); assert result.length() > 0; return result; } public static String fqnToBinary(String binary) { return binary.replace('.', '/'); } public static String getPackage(String binaryNameOrFqn) { int n = binaryNameOrFqn.lastIndexOf('/'); if (n >= 0) return binaryNameOrFqn.substring(0, n).replace('/', '.'); n = binaryNameOrFqn.lastIndexOf('.'); if (n >= 0) return binaryNameOrFqn.substring(0, n); return "."; } public static String fqnToPath(String s) { return fqnToBinary(s) + ".class"; } public TypeRef getTypeRefFromFQN(String fqn) { if (fqn.equals("boolean")) return BOOLEAN; if (fqn.equals("byte")) return BOOLEAN; if (fqn.equals("char")) return CHAR; if (fqn.equals("short")) return SHORT; if (fqn.equals("int")) return INTEGER; if (fqn.equals("long")) return LONG; if (fqn.equals("float")) return FLOAT; if (fqn.equals("double")) return DOUBLE; return getTypeRef(fqnToBinary(fqn)); } public TypeRef getTypeRefFromPath(String path) { assert path.endsWith(".class"); return getTypeRef(path.substring(0, path.length() - 6)); } // static class Rover { // int n = 0; // String string; // Rover(String string) { // this.string = string; // // } // // boolean at( String s) { // if ( n + s.length() > string.length()) // return false; // // for ( int i=0; i<s.length(); i++) { // if ( string.charAt(n+i) != s.charAt(n+i)) // return false; // } // // n += s.length(); // return true; // } // // String upTo(char c) { // for ( int i=n; i < string.length(); i++) { // if ( string.charAt(i) == c) { // String s = string.substring(n,i); // n = i; // return s; // } // } // throw new IllegalArgumentException("Looking for " + c + " in " + string + // " from " + n); // } // // } // public Signature getSignature(String signature) { // Signature s = new Signature(); // Rover r = new Rover(signature); // // // int n = parseTypeVarsDecl(s, rover); // n = parseParameters(s, descriptor, n); // n = parseTypeVarsDecl(s, descriptor, n); // // assert n == descriptor.length(); // return s; // } // // private int parseParameters(Signature s, String descriptor, int n) { // // TODO Auto-generated method stub // return 0; // } // // /** // * <X::Ljava/util/List<Ljava/lang/String;>;Y:Ljava/lang/Object;>(TY;)TX; // */ // private void parseTypeVarsDecl(Signature s, Rover rover) { // if ( rover.at("<")) { // while ( !rover.at(">")) { // String name = rover.upTo(':'); // rover.n++; // do { // Signature tr = parseTypeReference(s, rover); // s.typevariables.put(name, tr); // } while( rover.at(":")); // } // } // } // // private TypeRef parseTypeReference(String descriptor, int i) { // // TODO Auto-generated method stub // return null; // } }