This project has retired. For details please refer to its Attic page.
Source code
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 = "&nbsp;" + 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}