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