001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.reef.tang.util; 020 021import org.apache.reef.tang.ExternalConstructor; 022import org.apache.reef.tang.JavaClassHierarchy; 023import org.apache.reef.tang.Tang; 024import org.apache.reef.tang.annotations.*; 025import org.apache.reef.tang.exceptions.ClassHierarchyException; 026import org.apache.reef.tang.exceptions.NameResolutionException; 027import org.apache.reef.tang.formats.ConfigurationModule; 028import org.apache.reef.tang.formats.ConfigurationModuleBuilder; 029import org.apache.reef.tang.types.*; 030import org.apache.reef.tang.util.walk.AbstractClassHierarchyNodeVisitor; 031import org.apache.reef.tang.util.walk.NodeVisitor; 032import org.apache.reef.tang.util.walk.Walk; 033import org.reflections.Reflections; 034import org.reflections.scanners.MethodAnnotationsScanner; 035import org.reflections.scanners.MethodParameterScanner; 036import org.reflections.scanners.SubTypesScanner; 037import org.reflections.scanners.TypeAnnotationsScanner; 038 039import javax.inject.Inject; 040import java.io.File; 041import java.io.FileNotFoundException; 042import java.io.FileOutputStream; 043import java.io.PrintStream; 044import java.lang.reflect.Field; 045import java.lang.reflect.Modifier; 046import java.net.MalformedURLException; 047import java.net.URL; 048import java.util.Map; 049import java.util.Map.Entry; 050import java.util.Set; 051import java.util.TreeSet; 052 053public class Tint { 054 final private static String SETTERS = "setters"; 055 final private static String USES = "uses"; 056 final private static String FULLNAME = "fullName"; 057 final JavaClassHierarchy ch; 058 final Map<Field, ConfigurationModule> modules = new MonotonicHashMap<>(); 059 final MonotonicMultiMap<String, String> setters = new MonotonicMultiMap<>(); 060 // map from thing that was used to user of the thing. 061 final MonotonicMultiMap<String, String> usages = new MonotonicMultiMap<>(); 062 final Set<ClassNode<?>> knownClasses = new MonotonicSet<>(); 063 final Set<String> divs = new MonotonicSet<>(); 064 065 { 066 divs.add("doc"); 067 divs.add(USES); 068 divs.add(SETTERS); 069 070 } 071 072 public Tint() { 073 this(new URL[0]); 074 } 075 076 public Tint(URL[] jars) { 077 this(jars, false); 078 } 079 080 @SuppressWarnings("unchecked") 081 public Tint(URL[] jars, boolean checkTang) { 082 Object[] args = new Object[jars.length + 6]; 083 for (int i = 0; i < jars.length; i++) { 084 args[i] = jars[i]; 085 } 086 args[args.length - 1] = new TypeAnnotationsScanner(); 087 args[args.length - 2] = new SubTypesScanner(); 088 args[args.length - 3] = new MethodAnnotationsScanner(); 089 args[args.length - 4] = new MethodParameterScanner(); 090 args[args.length - 5] = "com.microsoft"; 091 args[args.length - 6] = "org.apache"; 092 Reflections r = new Reflections(args); 093// Set<Class<?>> classes = new MonotonicSet<>(); 094 Set<String> strings = new TreeSet<>(); 095 Set<String> moduleBuilders = new MonotonicSet<>(); 096 097 // Workaround bug in Reflections by keeping things stringly typed, and using Tang to parse them. 098// Set<Constructor<?>> injectConstructors = (Set<Constructor<?>>)(Set)r.getMethodsAnnotatedWith(Inject.class); 099// for(Constructor<?> c : injectConstructors) { 100// classes.add(c.getDeclaringClass()); 101// } 102 Set<String> injectConstructors = r.getStore().getConstructorsAnnotatedWith(ReflectionUtilities.getFullName(Inject.class)); 103 for (String s : injectConstructors) { 104 strings.add(s.replaceAll("\\.<.+$", "")); 105 } 106 Set<String> parameterConstructors = r.getStore().get(MethodParameterScanner.class, ReflectionUtilities.getFullName(Parameter.class)); 107 for (String s : parameterConstructors) { 108 strings.add(s.replaceAll("\\.<.+$", "")); 109 } 110// Set<Class> r.getConstructorsWithAnyParamAnnotated(Parameter.class); 111// for(Constructor<?> c : parameterConstructors) { 112// classes.add(c.getDeclaringClass()); 113// } 114 Set<String> defaultStrings = r.getStore().get(TypeAnnotationsScanner.class, ReflectionUtilities.getFullName(DefaultImplementation.class)); 115 strings.addAll(defaultStrings); 116 strings.addAll(r.getStore().get(TypeAnnotationsScanner.class, ReflectionUtilities.getFullName(NamedParameter.class))); 117 strings.addAll(r.getStore().get(TypeAnnotationsScanner.class, ReflectionUtilities.getFullName(Unit.class))); 118// classes.addAll(r.getTypesAnnotatedWith(DefaultImplementation.class)); 119// classes.addAll(r.getTypesAnnotatedWith(NamedParameter.class)); 120// classes.addAll(r.getTypesAnnotatedWith(Unit.class)); 121 122 strings.addAll(r.getStore().get(SubTypesScanner.class, ReflectionUtilities.getFullName(Name.class))); 123 124 moduleBuilders.addAll(r.getStore().get(SubTypesScanner.class, ReflectionUtilities.getFullName(ConfigurationModuleBuilder.class))); 125// classes.addAll(r.getSubTypesOf(Name.class)); 126 127 ch = Tang.Factory.getTang().getDefaultClassHierarchy(jars, (Class<? extends ExternalConstructor<?>>[]) new Class[0]); 128// for(String s : defaultStrings) { 129// if(classFilter(checkTang, s)) { 130// try { 131// ch.getNode(s); 132// } catch(ClassHierarchyException | NameResolutionException | ClassNotFoundException e) { 133// System.err.println(e.getMessage()); 134// } 135// } 136// } 137 138 for (String s : strings) { 139 if (classFilter(checkTang, s)) { 140 try { 141 ch.getNode(s); 142 } catch (ClassHierarchyException | NameResolutionException e) { 143 System.err.println(e.getMessage()); 144 } 145 } 146 } 147 for (String s : moduleBuilders) { 148 if (classFilter(checkTang, s)) { 149 try { 150 ch.getNode(s); 151 } catch (ClassHierarchyException | NameResolutionException e) { 152 e.printStackTrace(); 153 } 154 } 155 } 156 157 NodeVisitor<Node> v = new AbstractClassHierarchyNodeVisitor() { 158 159 @Override 160 public boolean visit(NamedParameterNode<?> node) { 161 String node_s = node.getFullName(); 162 for (String s : node.getDefaultInstanceAsStrings()) { 163 if (!usages.contains(s, node_s)) { 164 usages.put(s, node_s); 165 } 166 } 167 return true; 168 } 169 170 @Override 171 public boolean visit(PackageNode node) { 172 return true; 173 } 174 175 @Override 176 public boolean visit(ClassNode<?> node) { 177 String node_s = node.getFullName(); 178 for (ConstructorDef<?> d : node.getInjectableConstructors()) { 179 for (ConstructorArg a : d.getArgs()) { 180 if (a.getNamedParameterName() != null && 181 !usages.contains(a.getNamedParameterName(), node_s)) { 182 usages.put(a.getNamedParameterName(), node_s); 183 } 184 } 185 } 186 if (!knownClasses.contains(node)) { 187 knownClasses.add(node); 188 } 189 return true; 190 } 191 }; 192 int numClasses; 193 do { 194 numClasses = knownClasses.size(); 195 196 Walk.preorder(v, null, ch.getNamespace()); 197 198 for (ClassNode<?> cn : knownClasses) { 199 try { 200 String s = cn.getFullName(); 201 if (classFilter(checkTang, s)) { 202 Class<?> c = ch.classForName(s); 203 processDefaultAnnotation(c); 204 processConfigurationModules(c); 205 } 206 } catch (ClassNotFoundException e) { 207 e.printStackTrace(); 208 } 209 } 210 211 for (Field f : modules.keySet()) { 212 ConfigurationModule m = modules.get(f); 213 String f_s = ReflectionUtilities.getFullName(f); 214 Set<NamedParameterNode<?>> nps = m.getBoundNamedParameters(); 215 for (NamedParameterNode<?> np : nps) { 216 String np_s = np.getFullName(); 217 if (!setters.contains(np_s, f_s)) { 218 setters.put(np_s, f_s); 219 } 220 } 221 } 222 } while (numClasses != knownClasses.size()); // Note naive fixed point evaluation here. Semi-naive would be faster. 223 224 } 225 226 public static String stripCommonPrefixes(String s) { 227 return 228 stripPrefixHelper2( 229 stripPrefixHelper2(s, "java.lang"), 230 "java.util"); 231 } 232 233 public static String stripPrefixHelper2(String s, String prefix) { 234 String[] pretok = prefix.split("\\."); 235 String[] stok = s.split("\\."); 236 StringBuffer sb = new StringBuffer(); 237 int i; 238 for (i = 0; i < pretok.length && i < stok.length; i++) { 239 if (pretok[i].equals(stok[i])) { 240 sb.append(pretok[i].charAt(0) + "."); 241 } else { 242 break; 243 } 244 } 245 for (; i < stok.length - 1; i++) { 246 sb.append(stok[i] + "."); 247 } 248 sb.append(stok[stok.length - 1]); 249 return sb.toString(); 250 } 251 252 /* public static String stripPrefixHelper(String s, String prefix) { 253 if(!"".equals(prefix) && s.startsWith(prefix+".")) { 254 try { 255 String suffix = s.substring(prefix.length()+1); 256 if(suffix.matches("^[A-Z].+")){ 257 return suffix; 258 } else { 259 String shorten = prefix.replaceAll("([^.])[^.]+", "$1"); 260 return shorten + "." + suffix; 261 } 262 } catch(StringIndexOutOfBoundsException e) { 263 throw new RuntimeException("Couldn't strip " + prefix + " from " + s, e); 264 } 265 } else { 266 return s; 267 } 268 }*/ 269 public static String stripPrefix(String s, String prefix) { 270 return stripPrefixHelper2(stripPrefixHelper2(stripPrefixHelper2( 271 stripCommonPrefixes(stripPrefixHelper2(s, prefix)), 272 "org.apache.reef"), "org.apache.reef"), "org.apache.reef.wake"); 273 } 274 275 /** 276 * @param args 277 * @throws FileNotFoundException 278 * @throws MalformedURLException 279 */ 280 public static void main(String[] args) throws FileNotFoundException, MalformedURLException { 281 int i = 0; 282 String doc = null; 283 String jar = null; 284 boolean tangTests = false; 285 while (i < args.length) { 286 if (args[i].equals("--doc")) { 287 i++; 288 doc = args[i]; 289 } else if (args[i].equals("--jar")) { 290 i++; 291 jar = args[i]; 292 } else if (args[i].equals("--tang-tests")) { 293 tangTests = true; 294 } 295 296 i++; 297 } 298 299 final Tint t; 300 if (jar != null) { 301 final File f = new File(jar); 302 if (!f.exists()) { 303 throw new FileNotFoundException(jar); 304 } 305 t = new Tint(new URL[]{f.toURI().toURL()}, tangTests); 306 } else { 307 t = new Tint(new URL[0], tangTests); 308 } 309 310 if (doc != null) { 311 try (final PrintStream out = new PrintStream(new FileOutputStream(new File(doc)))) { 312 out.println("<html><head><title>TangDoc</title>"); 313 314 out.println("<style>"); 315 out.println("body { font-family: 'Segoe UI', 'Comic Sans MS'; font-size:12pt; font-weight: 200; margin: 1em; column-count: 2; }"); 316 out.println(".package { font-size:18pt; font-weight: 500; column-span: all; }"); 317// out.println(".class { break-after: never; }"); 318// out.println(".doc { break-before: never; }"); 319 out.println(".decl-margin { padding: 8pt; break-inside: avoid; }"); 320 out.println(".module-margin { padding: 8pt; column-span: all; break-inside: avoid; }"); 321 out.println(".decl { background-color: aliceblue; padding: 6pt;}"); 322 out.println(".fullName { font-size: 11pt; font-weight: 400; }"); 323 out.println(".simpleName { font-size: 11pt; font-weight: 400; }"); 324 out.println(".constructorArg { padding-left: 16pt; }"); 325 out.println("." + SETTERS + " { padding-top: 6pt; font-size: 10pt; }"); 326 out.println("." + USES + " { padding-top: 6pt; font-size: 10pt; }"); 327 out.println("pre { font-size: 10pt; }"); 328 out.println("</style>"); 329 330 out.println("</head><body>"); 331// out.println("<table border='1'><tr><th>Type</th><th>Name</th><th>Default value</th><th>Documentation</th><th>Used by</th><th>Set by</th></tr>"); 332 333 String currentPackage = ""; 334// int numcols = 0; 335 for (final Node n : t.getNamesUsedAndSet()) { 336 String fullName = n.getFullName(); 337 String tok[] = fullName.split("\\."); 338 StringBuffer sb = new StringBuffer(tok[0]); 339 for (int j = 1; j < tok.length; j++) { 340 if (tok[j].matches("^[A-Z].*") || j > 4) { 341 break; 342 } else 343 sb.append("." + tok[j]); 344 } 345 String pack = sb.toString(); 346 if (!currentPackage.equals(pack)) { 347 currentPackage = pack; 348 out.println(t.endPackage()); 349 out.println(t.startPackage(currentPackage)); 350// numcols = 0; 351// out.println("<div class='row'>"); 352 } 353// numcols++; 354// if(numcols == NUMCOLS) { 355// out.println("</div><div class='row'>"); 356// } 357 if (n instanceof NamedParameterNode<?>) { 358 out.println(t.toHtmlString((NamedParameterNode<?>) n, currentPackage)); 359 } else if (n instanceof ClassNode<?>) { 360 out.println(t.toHtmlString((ClassNode<?>) n, currentPackage)); 361 } else { 362 throw new IllegalStateException(); 363 } 364 } 365 out.println("</div>"); 366 out.println(t.endPackage()); 367// out.println("</table>"); 368 out.println("<div class='package'>Module definitions</div>"); 369 for (final Field f : t.modules.keySet()) { 370 String moduleName = ReflectionUtilities.getFullName(f); 371// String declaringClassName = ReflectionUtilities.getFullName(f.getDeclaringClass()); 372 out.println("<div class='module-margin' id='" + moduleName + "'><div class='decl'><span class='fullName'>" + moduleName + "</span>"); 373 out.println("<pre>"); 374 String conf = t.modules.get(f).toPrettyString(); 375 String[] tok = conf.split("\n"); 376 for (final String line : tok) { 377 out.println(stripPrefix(line, "no.such.prefix"));//t.modules.get(f).toPrettyString()); 378 } 379// List<Entry<String,String>> lines = t.modules.get(f).toStringPairs(); 380// for(Entry<String,String> line : lines) { 381// String k = t.stripPrefix(line.getKey(), declaringClassName); 382// String v = t.stripPrefix(line.getValue(), declaringClassName); 383// out.println(k+"="+v); 384// } 385 out.println("</pre>"); 386 out.println("</div></div>"); 387 } 388 389 out.println("<div class='package'>Interfaces and injectable classes</div>"); 390 for (final ClassNode<?> c : t.knownClasses) { 391 if (t.classFilter(tangTests, c.getFullName())) { 392 Class<?> clz = null; 393 try { 394 clz = t.ch.classForName(c.getFullName()); 395 } catch (ClassNotFoundException e) { 396 // TODO Auto-generated catch block 397 e.printStackTrace(); 398 } 399 String typ = clz.isInterface() ? "interface" : "class"; 400 out.println("<div class='module-margin' id='" + c.getFullName() + "'><div class='decl'><span class='fullName'>" + typ + " " + c.getFullName() + "</span>"); 401 for (ConstructorDef<?> d : c.getInjectableConstructors()) { 402 out.println("<div class='uses'>" + c.getFullName() + "("); 403 for (ConstructorArg a : d.getArgs()) { 404 if (a.getNamedParameterName() != null) { 405 out.print("<div class='constructorArg'><a href='#" + a.getType() + "'>" + stripPrefix(a.getType(), "xxx") + "</a> <a href='#" + a.getNamedParameterName() + "'>" + a.getNamedParameterName() + "</a></div>"); 406 } else { 407 out.print("<div class='constructorArg'><a href='#" + a.getType() + "'>" + stripPrefix(a.getType(), "xxx") + "</a></div>"); 408 } 409 } 410 out.println(")</div>"); 411 } 412 out.println("</div></div>"); 413 } 414/* 415 out.println("<h1>Default usage of classes and constants</h1>"); 416 for(String s : t.usages.keySet()) { 417 out.println("<h2>" + s + "</h2>"); 418 for(Node n : t.usages.getValuesForKey(s)) { 419 out.println("<p>" + n.getFullName() + "</p>"); 420 } 421 } */ 422 } 423 out.println("</body></html>"); 424 } 425 } 426 } 427 428 private final boolean classFilter(boolean checkTang, String s) { 429 return (checkTang || /*s.startsWith("org.apache.reef.tang.examples.timer") ||*/ !s.startsWith("org.apache.reef.tang")); 430 } 431 432 private void processDefaultAnnotation(Class<?> cmb) { 433 DefaultImplementation di = cmb.getAnnotation(DefaultImplementation.class); 434 // XXX hack: move to helper method + unify with rest of Tang! 435 if (di != null) { 436 String diName = di.value() == Void.class ? di.name() : ReflectionUtilities.getFullName(di.value()); 437 ClassNode<?> cn = (ClassNode<?>) ch.getNode(cmb); 438 String cn_s = cn.getFullName(); 439 if (!usages.contains(diName, cn_s)) { 440 usages.put(diName, cn_s); 441 if (!knownClasses.contains(cn)) { 442 knownClasses.add(cn); 443 } 444 } 445 } 446 } 447 448 private void processConfigurationModules(Class<?> cmb) { 449 for (Field f : cmb.getFields()) { 450 if (ReflectionUtilities.isCoercable(ConfigurationModule.class, f.getType())) { 451 int mod = f.getModifiers(); 452 boolean ok = true; 453 if (Modifier.isPrivate(mod)) { 454 System.err.println("Found private ConfigurationModule " + f); 455 ok = false; 456 } 457 if (!Modifier.isFinal(mod)) { 458 System.err.println("Found non-final ConfigurationModule " + f); 459 ok = false; 460 } 461 if (!Modifier.isStatic(f.getModifiers())) { 462 System.err.println("Found non-static ConfigurationModule " + f); 463 ok = false; 464 } 465 if (ok) { 466// System.err.println("OK: " + f); 467 try { 468 f.setAccessible(true); 469 String f_s = ReflectionUtilities.getFullName(f); 470 if (!modules.containsKey(f)) { 471 modules.put(f, (ConfigurationModule) (f.get(null))); 472 try { 473 modules.get(f).assertStaticClean(); 474 } catch (ClassHierarchyException e) { 475 System.err.println(f_s + ": " + e.getMessage()); 476 } 477 for (Entry<String, String> e : modules.get(f).toStringPairs()) { 478 //System.err.println("e: " + e.getKey() + "=" + e.getValue()); 479 try { 480 Node n = ch.getNode(e.getKey()); 481 if (!setters.contains(e.getKey(), f_s)) { 482 setters.put(e.getKey(), f_s); 483 } 484 if (n instanceof ClassNode) { 485 ClassNode<?> cn = (ClassNode<?>) n; 486 if (!knownClasses.contains(cn)) { 487 knownClasses.add(cn); 488 } 489 } 490 } catch (NameResolutionException ex) { 491 492 } 493 try { 494 String s = e.getValue(); 495 Node n = ch.getNode(s); 496 if (!usages.contains(ReflectionUtilities.getFullName(f), s)) { 497 // System.err.println("Added usage: " + ReflectionUtilities.getFullName(f) + "=" + s); 498 usages.put(s, ReflectionUtilities.getFullName(f)); 499 } 500 if (n instanceof ClassNode) { 501 ClassNode<?> cn = (ClassNode<?>) n; 502 if (!knownClasses.contains(cn)) { 503 System.err.println("Added " + cn + " to known classes"); 504 knownClasses.add(cn); 505 } 506 } 507 } catch (NameResolutionException ex) { 508 509 } 510 } 511 } 512 } catch (ExceptionInInitializerError e) { 513 System.err.println("Field " + ReflectionUtilities.getFullName(f) + ": " + e.getCause().getMessage()); 514 } catch (IllegalAccessException e) { 515 throw new RuntimeException(e); 516 } 517 } 518 } 519 } 520 } 521 522 public Set<NamedParameterNode<?>> getNames() { 523 final Set<NamedParameterNode<?>> names = new MonotonicSet<>(); 524 NodeVisitor<Node> v = new AbstractClassHierarchyNodeVisitor() { 525 526 @Override 527 public boolean visit(NamedParameterNode<?> node) { 528 names.add(node); 529 return true; 530 } 531 532 @Override 533 public boolean visit(PackageNode node) { 534 return true; 535 } 536 537 @Override 538 public boolean visit(ClassNode<?> node) { 539 return true; 540 } 541 }; 542 Walk.preorder(v, null, ch.getNamespace()); 543 return names; 544 } 545 546 public Set<Node> getNamesUsedAndSet() { 547 final Set<Node> names = new MonotonicSet<>(); 548 final Set<String> userKeys = usages.keySet(); 549 final Set<String> usedKeys = usages.values(); 550 final Set<String> setterKeys = setters.keySet(); 551 NodeVisitor<Node> v = new AbstractClassHierarchyNodeVisitor() { 552 553 @Override 554 public boolean visit(NamedParameterNode<?> node) { 555 names.add(node); 556 return true; 557 } 558 559 @Override 560 public boolean visit(PackageNode node) { 561 return true; 562 } 563 564 @Override 565 public boolean visit(ClassNode<?> node) { 566 String node_s = node.getFullName(); 567 if (userKeys.contains(node_s)) { 568 names.add(node); 569 } 570 if (setterKeys.contains(node_s)) { 571 names.add(node); 572 } 573 if (usedKeys.contains(node_s)) { 574 if (!names.contains(node)) { 575 names.add(node); 576 } 577 } 578 579 return true; 580 } 581 }; 582 Walk.preorder(v, null, ch.getNamespace()); 583 return names; 584 } 585 586 public Set<String> getUsesOf(final Node name) { 587 588 return usages.getValuesForKey(name.getFullName()); 589 } 590 591 public Set<String> getSettersOf(final Node name) { 592 return setters.getValuesForKey(name.getFullName()); 593 } 594 595 public String toString(NamedParameterNode<?> n) { 596 StringBuilder sb = new StringBuilder("Name: " + n.getSimpleArgName() + " " + n.getFullName()); 597 String[] instances = n.getDefaultInstanceAsStrings(); 598 if (instances.length != 0) { 599 sb.append(" = "); 600 sb.append(join(",", instances)); 601 } 602 if (!n.getDocumentation().equals("")) { 603 sb.append(" //" + n.getDocumentation()); 604 } 605 return sb.toString(); 606 } 607 608 private String join(String sep, String[] s) { 609 if (s.length > 0) { 610 StringBuffer sb = new StringBuffer(s[0]); 611 for (int i = 1; i < s.length; i++) { 612 sb.append(sep); 613 sb.append(s[i]); 614 } 615 return sb.toString(); 616 } else { 617 return null; 618 } 619 } 620 621 public String cell(String s, String clazz) { 622 if (clazz.equals(USES) && s.length() > 0) { 623 s = "<em>Used by:</em><br>" + s; 624 } 625 if (clazz.equals(SETTERS) && s.length() > 0) { 626 s = "<em>Set by:</em><br>" + s; 627 } 628 if (clazz.equals(FULLNAME)) { 629 s = " " + s; 630 } 631 if (divs.contains(clazz)) { 632 return "<div class=\"" + clazz + "\">" + s + "</div>"; 633 } else { 634 return "<span class=\"" + clazz + "\">" + s + "</span>"; 635 } 636 } 637 638 public String cell(StringBuffer sb, String clazz) { 639 return cell(sb.toString(), clazz); 640 } 641 642 public String row(StringBuffer sb) { 643 return sb.toString(); 644 } 645 646 public String toHtmlString(NamedParameterNode<?> n, String pack) { 647 String fullName = stripPrefix(n.getFullName(), pack); 648 StringBuffer sb = new StringBuffer(); 649 650 sb.append("<div id='" + n.getFullName() + "' class='decl-margin'>"); 651 sb.append("<div class='decl'>"); 652 653 sb.append(cell(n.getSimpleArgName(), "simpleName") + cell(fullName, FULLNAME)); 654 final String instance; 655 final String[] instances = n.getDefaultInstanceAsStrings(); 656 if (instances.length != 0) { 657 StringBuffer sb2 = new StringBuffer(" = " + stripPrefix(instances[0], pack)); 658 for (int i = 1; i < instances.length; i++) { 659 sb2.append("," + stripPrefix(instances[i], pack)); 660 } 661 instance = sb2.toString(); 662 } else { 663 instance = ""; 664 } 665 sb.append(cell(instance, "instance")); 666 StringBuffer doc = new StringBuffer(); 667 if (!n.getDocumentation().equals("")) { 668 doc.append(n.getDocumentation()); 669 } 670 sb.append(cell(doc, "doc")); 671 StringBuffer uses = new StringBuffer(); 672 for (String u : getUsesOf(n)) { 673 uses.append("<a href='#" + u + "'>" + stripPrefix(u, pack) + "</a> "); 674 } 675 sb.append(cell(uses, USES)); 676 StringBuffer setters = new StringBuffer(); 677 for (String f : getSettersOf(n)) { 678 setters.append("<a href='#" + f + "'>" + stripPrefix(f, pack) + "</a> "); 679 } 680 sb.append(cell(setters, SETTERS)); 681 sb.append("</div>"); 682 sb.append("</div>"); 683 return row(sb); 684 } 685 686 public String toHtmlString(ClassNode<?> n, String pack) { 687 String fullName = stripPrefix(n.getFullName(), pack); 688 689 final String type; 690 try { 691 if (ch.classForName(n.getFullName()).isInterface()) { 692 type = "interface"; 693 } else { 694 type = "class"; 695 } 696 } catch (ClassNotFoundException e) { 697 throw new RuntimeException(e); 698 } 699 StringBuffer sb = new StringBuffer(); 700 701 sb.append("<div class='decl-margin' id='" + n.getFullName() + "'>"); 702 sb.append("<div class='decl'>"); 703 sb.append(cell(type, "simpleName") + cell(fullName, FULLNAME)); 704 final String instance; 705 if (n.getDefaultImplementation() != null) { 706 instance = " = " + stripPrefix(n.getDefaultImplementation(), pack); 707 } else { 708 instance = ""; 709 } 710 sb.append(cell(instance, "simpleName")); 711 sb.append(cell("", "fullName")); // TODO: Documentation string? 712 StringBuffer uses = new StringBuffer(); 713 for (String u : getUsesOf(n)) { 714 uses.append("<a href='#" + u + "'>" + stripPrefix(u, pack) + "</a> "); 715 } 716 sb.append(cell(uses, USES)); 717 StringBuffer setters = new StringBuffer(); 718 for (String f : getSettersOf(n)) { 719 setters.append("<a href='#" + f + "'>" + stripPrefix(f, pack) + "</a> "); 720 } 721 sb.append(cell(setters, SETTERS)); 722 sb.append("</div>"); 723 sb.append("</div>"); 724 return row(sb); 725 } 726 727 public String startPackage(String pack) { 728 return "<div class=\"package\">" + pack + "</div>"; 729// return "<tr><td colspan='6'><br><b>" + pack + "</b></td></tr>"; 730 } 731 732 public String endPackage() { 733 return ""; 734 } 735}