@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. }
/** * @param args * @throws FileNotFoundException * @throws MalformedURLException */ public static void main(final String[] args) throws FileNotFoundException, MalformedURLException, UnsupportedEncodingException { int i = 0; String doc = null; String jar = null; boolean tangTests = false; while (i < args.length) { if (args[i].equals("--doc")) { i++; doc = args[i]; } else if (args[i].equals("--jar")) { i++; jar = args[i]; } else if (args[i].equals("--tang-tests")) { tangTests = true; } i++; } final Tint t; if (jar != null) { final File f = new File(jar); if (!f.exists()) { throw new FileNotFoundException(jar); } t = new Tint(new URL[] {f.toURI().toURL()}, tangTests); } else { t = new Tint(new URL[0], tangTests); } if (doc != null) { try (final PrintStream out = new PrintStream(doc, "UTF-8")) { out.println("<html><head><title>TangDoc</title>"); out.println("<style>"); out.println( "body { font-family: 'Segoe UI', 'Comic Sans MS'; font-size:12pt; font-weight: 200; " + "margin: 1em; column-count: 2; }"); out.println(".package { font-size:18pt; font-weight: 500; column-span: all; }"); // out.println(".class { break-after: never; }"); // out.println(".doc { break-before: never; }"); out.println(".decl-margin { padding: 8pt; break-inside: avoid; }"); out.println(".module-margin { padding: 8pt; column-span: all; break-inside: avoid; }"); out.println(".decl { background-color: aliceblue; padding: 6pt;}"); out.println(".fullName { font-size: 11pt; font-weight: 400; }"); out.println(".simpleName { font-size: 11pt; font-weight: 400; }"); out.println(".constructorArg { padding-left: 16pt; }"); out.println("." + SETTERS + " { padding-top: 6pt; font-size: 10pt; }"); out.println("." + USES + " { padding-top: 6pt; font-size: 10pt; }"); out.println("pre { font-size: 10pt; }"); out.println("</style>"); out.println("</head><body>"); String currentPackage = ""; for (final Node n : t.getNamesUsedAndSet()) { final String fullName = n.getFullName(); final String[] tok = fullName.split("\\."); final StringBuffer sb = new StringBuffer(tok[0]); for (int j = 1; j < tok.length; j++) { if (tok[j].matches("^[A-Z].*") || j > 4) { break; } else { sb.append("." + tok[j]); } } final String pack = sb.toString(); if (!currentPackage.equals(pack)) { currentPackage = pack; out.println(t.endPackage()); out.println(t.startPackage(currentPackage)); } if (n instanceof NamedParameterNode<?>) { out.println(t.toHtmlString((NamedParameterNode<?>) n, currentPackage)); } else if (n instanceof ClassNode<?>) { out.println(t.toHtmlString((ClassNode<?>) n, currentPackage)); } else { throw new IllegalStateException(); } } out.println("</div>"); out.println(t.endPackage()); out.println("<div class='package'>Module definitions</div>"); for (final Field f : t.modules.keySet()) { final String moduleName = ReflectionUtilities.getFullName(f); // String declaringClassName = // ReflectionUtilities.getFullName(f.getDeclaringClass()); out.println( "<div class='module-margin' id='" + moduleName + "'><div class='decl'><span class='fullName'>" + moduleName + "</span>"); out.println("<pre>"); final String conf = t.modules.get(f).toPrettyString(); final String[] tok = conf.split("\n"); for (final String line : tok) { out.println(stripPrefix(line, "no.such.prefix")); // t.modules.get(f).toPrettyString()); } // List<Entry<String,String>> lines = t.modules.get(f).toStringPairs(); // for(Entry<String,String> line : lines) { // String k = t.stripPrefix(line.getKey(), declaringClassName); // String v = t.stripPrefix(line.getValue(), declaringClassName); // out.println(k+"="+v); // } out.println("</pre>"); out.println("</div></div>"); } out.println("<div class='package'>Interfaces and injectable classes</div>"); for (final ClassNode<?> c : t.knownClasses) { if (t.classFilter(tangTests, c.getFullName())) { Class<?> clz = null; try { clz = t.ch.classForName(c.getFullName()); } catch (final ClassNotFoundException e) { // TODO[JIRA REEF-864] Clarify handling in this case e.printStackTrace(); } final String typ = clz == null ? "undefined" : clz.isInterface() ? "interface" : "class"; out.println( "<div class='module-margin' id='" + c.getFullName() + "'><div class='decl'>" + "<span class='fullName'>" + typ + " " + c.getFullName() + "</span>"); for (final ConstructorDef<?> d : c.getInjectableConstructors()) { out.println("<div class='uses'>" + c.getFullName() + "("); for (final ConstructorArg a : d.getArgs()) { if (a.getNamedParameterName() != null) { out.print( "<div class='constructorArg'><a href='#" + a.getType() + "'>" + stripPrefix(a.getType(), "xxx") + "</a> <a href='#" + a.getNamedParameterName() + "'>" + a.getNamedParameterName() + "</a></div>"); } else { out.print( "<div class='constructorArg'><a href='#" + a.getType() + "'>" + stripPrefix(a.getType(), "xxx") + "</a></div>"); } } out.println(")</div>"); } out.println("</div></div>"); } /* out.println("<h1>Default usage of classes and constants</h1>"); for(String s : t.usages.keySet()) { out.println("<h2>" + s + "</h2>"); for(Node n : t.usages.getValuesForKey(s)) { out.println("<p>" + n.getFullName() + "</p>"); } } */ } out.println("</body></html>"); } } }