/** * A helper method that returns the parsed default value of a given NamedParameter. * * @return null or an empty set if there is no default value, the default value (or set of values) * otherwise. * @throws ClassHierarchyException if a default value was specified, but could not be parsed, or * if a set of values were specified for a non-set parameter. */ @SuppressWarnings("unchecked") @Override public <T> T parseDefaultValue(final NamedParameterNode<T> name) { final String[] vals = name.getDefaultInstanceAsStrings(); final T[] ret = (T[]) new Object[vals.length]; for (int i = 0; i < vals.length; i++) { final String val = vals[i]; try { ret[i] = parse(name, val); } catch (final ParseException e) { throw new ClassHierarchyException("Could not parse default value", e); } } if (name.isSet()) { return (T) new HashSet<T>(Arrays.asList(ret)); } else if (name.isList()) { return (T) new ArrayList<T>(Arrays.asList(ret)); } else { if (ret.length == 0) { return null; } else if (ret.length == 1) { return ret[0]; } else { throw new IllegalStateException( "Multiple defaults for non-set named parameter! " + name.getFullName()); } } }
/** * Parse a string, assuming that it is of the type expected by a given NamedParameter. * * <p>This method does not deal with sets; if the NamedParameter is set valued, then the provided * string should correspond to a single member of the set. It is up to the caller to call parse * once for each value that should be parsed as a member of the set. * * @return a non-null reference to the parsed value. */ @Override @SuppressWarnings("unchecked") public <T> T parse(final NamedParameterNode<T> np, final String value) throws ParseException { final ClassNode<T> iface; try { iface = (ClassNode<T>) getNode(np.getFullArgName()); } catch (final NameResolutionException e) { throw new IllegalStateException( "Could not parse validated named parameter argument type. NamedParameter is " + np.getFullName() + " argument type is " + np.getFullArgName()); } Class<?> clazz; String fullName; try { clazz = classForName(iface.getFullName()); fullName = null; } catch (final ClassNotFoundException e) { clazz = null; fullName = iface.getFullName(); } try { if (clazz != null) { return (T) parameterParser.parse(clazz, value); } else { return parameterParser.parse(fullName, value); } } catch (final UnsupportedOperationException e) { try { final Node impl = getNode(value); if (impl instanceof ClassNode && isImplementation(iface, (ClassNode<?>) impl)) { return (T) impl; } throw new ParseException( "Name<" + iface.getFullName() + "> " + np.getFullName() + " cannot take non-subclass " + impl.getFullName(), e); } catch (final NameResolutionException e2) { throw new ParseException( "Name<" + iface.getFullName() + "> " + np.getFullName() + " cannot take non-class " + value, e); } } }
private static ClassHierarchyProto.Node serializeNode(Node n) { List<ClassHierarchyProto.Node> children = new ArrayList<>(); for (Node child : n.getChildren()) { children.add(serializeNode(child)); } if (n instanceof ClassNode) { ClassNode<?> cn = (ClassNode<?>) n; ConstructorDef<?>[] injectable = cn.getInjectableConstructors(); ConstructorDef<?>[] all = cn.getAllConstructors(); List<ConstructorDef<?>> others = new ArrayList<>(Arrays.asList(all)); others.removeAll(Arrays.asList(injectable)); List<ClassHierarchyProto.ConstructorDef> injectableConstructors = new ArrayList<>(); for (ConstructorDef<?> inj : injectable) { injectableConstructors.add(serializeConstructorDef(inj)); } List<ClassHierarchyProto.ConstructorDef> otherConstructors = new ArrayList<>(); for (ConstructorDef<?> other : others) { otherConstructors.add(serializeConstructorDef(other)); } List<String> implFullNames = new ArrayList<>(); for (ClassNode<?> impl : cn.getKnownImplementations()) { implFullNames.add(impl.getFullName()); } return newClassNode( cn.getName(), cn.getFullName(), cn.isInjectionCandidate(), cn.isExternalConstructor(), cn.isUnit(), injectableConstructors, otherConstructors, implFullNames, children); } else if (n instanceof NamedParameterNode) { NamedParameterNode<?> np = (NamedParameterNode<?>) n; return newNamedParameterNode( np.getName(), np.getFullName(), np.getSimpleArgName(), np.getFullArgName(), np.isSet(), np.isList(), np.getDocumentation(), np.getShortName(), np.getDefaultInstanceAsStrings(), children); } else if (n instanceof PackageNode) { return newPackageNode(n.getName(), n.getFullName(), children); } else { throw new IllegalStateException("Encountered unknown type of Node: " + n); } }
public String toString(final NamedParameterNode<?> n) { final StringBuilder sb = new StringBuilder("Name: " + n.getSimpleArgName() + " " + n.getFullName()); final String[] instances = n.getDefaultInstanceAsStrings(); if (instances.length != 0) { sb.append(" = "); sb.append(join(",", instances)); } if (!n.getDocumentation().equals("")) { sb.append(" //" + n.getDocumentation()); } return sb.toString(); }
@Override public ClassHierarchy merge(ClassHierarchy ch) { if (this == ch) { return this; } if (!(ch instanceof ProtocolBufferClassHierarchy)) { throw new UnsupportedOperationException( "Cannot merge with class hierarchies of type: " + ch.getClass().getName()); } final ProtocolBufferClassHierarchy pch = (ProtocolBufferClassHierarchy) ch; for (final String key : pch.lookupTable.keySet()) { if (!this.lookupTable.containsKey(key)) { this.lookupTable.put(key, pch.lookupTable.get(key)); } for (final Node n : ch.getNamespace().getChildren()) { if (!this.namespace.contains(n.getFullName())) { if (n instanceof NamedParameter) { final NamedParameterNode np = (NamedParameterNode) n; new NamedParameterNodeImpl<>( this.namespace, np.getName(), np.getFullName(), np.getFullArgName(), np.getSimpleArgName(), np.isSet(), np.isList(), np.getDocumentation(), np.getShortName(), np.getDefaultInstanceAsStrings()); } else if (n instanceof ClassNode) { final ClassNode cn = (ClassNode) n; new ClassNodeImpl( namespace, cn.getName(), cn.getFullName(), cn.isUnit(), cn.isInjectionCandidate(), cn.isExternalConstructor(), cn.getInjectableConstructors(), cn.getAllConstructors(), cn.getDefaultImplementation()); } } } } return this; }
private <T, U> Node buildPathToNode(final Class<U> clazz) throws ClassHierarchyException { final String[] path = clazz.getName().split("\\$"); Node root = namespace; for (int i = 0; i < path.length - 1; i++) { root = root.get(path[i]); } if (root == null) { throw new NullPointerException(); } final Node parent = root; final Type argType = ReflectionUtilities.getNamedParameterTargetOrNull(clazz); if (argType == null) { return JavaNodeFactory.createClassNode(parent, clazz); } else { // checked inside of NamedParameterNode, using reflection. @SuppressWarnings("unchecked") final NamedParameterNode<T> np = JavaNodeFactory.createNamedParameterNode( parent, (Class<? extends Name<T>>) clazz, argType); if (parameterParser.canParse(ReflectionUtilities.getFullName(argType)) && clazz.getAnnotation(NamedParameter.class).default_class() != Void.class) { throw new ClassHierarchyException( "Named parameter " + ReflectionUtilities.getFullName(clazz) + " defines default implementation for parsable type " + ReflectionUtilities.getFullName(argType)); } final String shortName = np.getShortName(); if (shortName != null) { final NamedParameterNode<?> oldNode = shortNames.get(shortName); if (oldNode != null) { if (oldNode.getFullName().equals(np.getFullName())) { throw new IllegalStateException( "Tried to double bind " + oldNode.getFullName() + " to short name " + shortName); } throw new ClassHierarchyException( "Named parameters " + oldNode.getFullName() + " and " + np.getFullName() + " have the same short name: " + shortName); } shortNames.put(shortName, np); } return np; } }
public String toHtmlString(final NamedParameterNode<?> n, final String pack) { final String fullName = stripPrefix(n.getFullName(), pack); final StringBuffer sb = new StringBuffer(); sb.append("<div id='" + n.getFullName() + "' class='decl-margin'>"); sb.append("<div class='decl'>"); sb.append(cell(n.getSimpleArgName(), "simpleName") + cell(fullName, FULLNAME)); final String instance; final String[] instances = n.getDefaultInstanceAsStrings(); if (instances.length != 0) { final StringBuffer sb2 = new StringBuffer(" = " + stripPrefix(instances[0], pack)); for (int i = 1; i < instances.length; i++) { sb2.append("," + stripPrefix(instances[i], pack)); } instance = sb2.toString(); } else { instance = ""; } sb.append(cell(instance, "instance")); final StringBuffer doc = new StringBuffer(); if (!n.getDocumentation().equals("")) { doc.append(n.getDocumentation()); } sb.append(cell(doc, "doc")); final StringBuffer uses = new StringBuffer(); for (final String u : getUsesOf(n)) { uses.append("<a href='#" + u + "'>" + stripPrefix(u, pack) + "</a> "); } sb.append(cell(uses, USES)); final StringBuffer settersStr = new StringBuffer(); for (final String f : getSettersOf(n)) { settersStr.append("<a href='#" + f + "'>" + stripPrefix(f, pack) + "</a> "); } sb.append(cell(settersStr, SETTERS)); sb.append("</div>"); sb.append("</div>"); return row(sb); }
@SuppressWarnings("unchecked") public Tint(final URL[] jars, final boolean checkTang) { final Object[] args = new Object[jars.length + 6]; for (int i = 0; i < jars.length; i++) { args[i] = jars[i]; } args[args.length - 1] = new TypeAnnotationsScanner(); args[args.length - 2] = new SubTypesScanner(); args[args.length - 3] = new MethodAnnotationsScanner(); args[args.length - 4] = new MethodParameterScanner(); args[args.length - 5] = "com.microsoft"; args[args.length - 6] = "org.apache"; final Reflections r = new Reflections(args); // Set<Class<?>> classes = new MonotonicSet<>(); final Set<String> strings = new TreeSet<>(); final Set<String> moduleBuilders = new MonotonicSet<>(); // Workaround bug in Reflections by keeping things stringly typed, and using Tang to parse them. // Set<Constructor<?>> injectConstructors = // (Set<Constructor<?>>)(Set)r.getMethodsAnnotatedWith(Inject.class); // for(Constructor<?> c : injectConstructors) { // classes.add(c.getDeclaringClass()); // } final Set<String> injectConstructors = r.getStore().getConstructorsAnnotatedWith(ReflectionUtilities.getFullName(Inject.class)); for (final String s : injectConstructors) { strings.add(s.replaceAll("\\.<.+$", "")); } final Set<String> parameterConstructors = r.getStore() .get(MethodParameterScanner.class, ReflectionUtilities.getFullName(Parameter.class)); for (final String s : parameterConstructors) { strings.add(s.replaceAll("\\.<.+$", "")); } // Set<Class> r.getConstructorsWithAnyParamAnnotated(Parameter.class); // for(Constructor<?> c : parameterConstructors) { // classes.add(c.getDeclaringClass()); // } final Set<String> defaultStrings = r.getStore() .get( TypeAnnotationsScanner.class, ReflectionUtilities.getFullName(DefaultImplementation.class)); strings.addAll(defaultStrings); strings.addAll( r.getStore() .get( TypeAnnotationsScanner.class, ReflectionUtilities.getFullName(NamedParameter.class))); strings.addAll( r.getStore() .get(TypeAnnotationsScanner.class, ReflectionUtilities.getFullName(Unit.class))); // classes.addAll(r.getTypesAnnotatedWith(DefaultImplementation.class)); // classes.addAll(r.getTypesAnnotatedWith(NamedParameter.class)); // classes.addAll(r.getTypesAnnotatedWith(Unit.class)); strings.addAll( r.getStore().get(SubTypesScanner.class, ReflectionUtilities.getFullName(Name.class))); moduleBuilders.addAll( r.getStore() .get( SubTypesScanner.class, ReflectionUtilities.getFullName(ConfigurationModuleBuilder.class))); // classes.addAll(r.getSubTypesOf(Name.class)); ch = Tang.Factory.getTang() .getDefaultClassHierarchy( jars, (Class<? extends ExternalConstructor<?>>[]) new Class[0]); // for(String s : defaultStrings) { // if(classFilter(checkTang, s)) { // try { // ch.getNode(s); // } catch(ClassHierarchyException | NameResolutionException | ClassNotFoundException e) // { // System.err.println(e.getMessage()); // } // } // } for (final String s : strings) { if (classFilter(checkTang, s)) { try { ch.getNode(s); } catch (ClassHierarchyException | NameResolutionException e) { System.err.println(e.getMessage()); } } } for (final String s : moduleBuilders) { if (classFilter(checkTang, s)) { try { ch.getNode(s); } catch (ClassHierarchyException | NameResolutionException e) { e.printStackTrace(); } } } final NodeVisitor<Node> v = new AbstractClassHierarchyNodeVisitor() { @Override public boolean visit(final NamedParameterNode<?> node) { final String nodeS = node.getFullName(); for (final String s : node.getDefaultInstanceAsStrings()) { if (!usages.contains(s, nodeS)) { usages.put(s, nodeS); } } return true; } @Override public boolean visit(final PackageNode node) { return true; } @Override public boolean visit(final ClassNode<?> node) { final String nodeS = node.getFullName(); for (final ConstructorDef<?> d : node.getInjectableConstructors()) { for (final ConstructorArg a : d.getArgs()) { if (a.getNamedParameterName() != null && !usages.contains(a.getNamedParameterName(), nodeS)) { usages.put(a.getNamedParameterName(), nodeS); } } } if (!knownClasses.contains(node)) { knownClasses.add(node); } return true; } }; int numClasses; do { numClasses = knownClasses.size(); Walk.preorder(v, null, ch.getNamespace()); for (final ClassNode<?> cn : knownClasses) { try { final String s = cn.getFullName(); if (classFilter(checkTang, s)) { final Class<?> c = ch.classForName(s); processDefaultAnnotation(c); processConfigurationModules(c); } } catch (final ClassNotFoundException e) { e.printStackTrace(); } } for (final Entry<Field, ConfigurationModule> entry : modules.entrySet()) { final String fS = ReflectionUtilities.getFullName(entry.getKey()); final Set<NamedParameterNode<?>> nps = entry.getValue().getBoundNamedParameters(); for (final NamedParameterNode<?> np : nps) { final String npS = np.getFullName(); if (!setters.contains(npS, fS)) { setters.put(npS, fS); } } } } while (numClasses != knownClasses .size()); // Note naive fixed point evaluation here. Semi-naive would be faster. }
private Node register(final String s) { final Class<?> c; try { c = classForName(s); } catch (final ClassNotFoundException e1) { return null; } try { final Node n = getAlreadyBoundNode(c); return n; } catch (final NameResolutionException e) { // node not bound yet } // First, walk up the class hierarchy, registering all out parents. This // can't be loopy. if (c.getSuperclass() != null) { register(ReflectionUtilities.getFullName(c.getSuperclass())); } for (final Class<?> i : c.getInterfaces()) { register(ReflectionUtilities.getFullName(i)); } // Now, we'd like to register our enclosing classes. This turns out to be // safe. // Thankfully, Java doesn't allow: // class A implements A.B { class B { } } // It also doesn't allow cycles such as: // class A implements B.BB { interface AA { } } // class B implements A.AA { interface BB { } } // So, even though grafting arbitrary DAGs together can give us cycles, Java // seems // to have our back on this one. final Class<?> enclosing = c.getEnclosingClass(); if (enclosing != null) { register(ReflectionUtilities.getFullName(enclosing)); } // Now register the class. This has to be after the above so we know our // parents (superclasses and enclosing packages) are already registered. final Node n = registerClass(c); // Finally, do things that might introduce cycles that invlove c. // This has to be below registerClass, which ensures that any cycles // this stuff introduces are broken. for (final Class<?> innerClass : c.getDeclaredClasses()) { register(ReflectionUtilities.getFullName(innerClass)); } if (n instanceof ClassNode) { final ClassNode<?> cls = (ClassNode<?>) n; for (final ConstructorDef<?> def : cls.getInjectableConstructors()) { for (final ConstructorArg arg : def.getArgs()) { register(arg.getType()); if (arg.getNamedParameterName() != null) { final NamedParameterNode<?> np = (NamedParameterNode<?>) register(arg.getNamedParameterName()); try { // TODO: When handling sets, need to track target of generic parameter, and check the // type here! if (!np.isSet() && !np.isList() && !ReflectionUtilities.isCoercable( classForName(arg.getType()), classForName(np.getFullArgName()))) { throw new ClassHierarchyException( "Named parameter type mismatch in " + cls.getFullName() + ". Constructor expects a " + arg.getType() + " but " + np.getName() + " is a " + np.getFullArgName()); } } catch (final ClassNotFoundException e) { throw new ClassHierarchyException( "Constructor refers to unknown class " + arg.getType(), e); } } } } } else if (n instanceof NamedParameterNode) { final NamedParameterNode<?> np = (NamedParameterNode<?>) n; register(np.getFullArgName()); } return n; }