summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormichelou <michelou@epfl.ch>2003-10-17 15:00:31 +0000
committermichelou <michelou@epfl.ch>2003-10-17 15:00:31 +0000
commit2c1ac0cc2a4dd0a7ee8b8bd0ac8b7f38d700f3ff (patch)
tree687103bd054bcf14343089f6eb550357546bf9b9
parent2e5258021fabd15ebd10d5108821965aec97ac64 (diff)
downloadscala-2c1ac0cc2a4dd0a7ee8b8bd0ac8b7f38d700f3ff.tar.gz
scala-2c1ac0cc2a4dd0a7ee8b8bd0ac8b7f38d700f3ff.tar.bz2
scala-2c1ac0cc2a4dd0a7ee8b8bd0ac8b7f38d700f3ff.zip
- moved scaladoc into scala.tools package
-rw-r--r--Makefile6
-rw-r--r--config/list/scaladoc.lst2
-rw-r--r--sources/bin/.scala_wrapper.tmpl2
-rw-r--r--sources/scala/tools/scaladoc/Anchors.java124
-rw-r--r--sources/scala/tools/scaladoc/Comment.java170
-rw-r--r--sources/scala/tools/scaladoc/DocModule.java129
-rw-r--r--sources/scala/tools/scaladoc/HTMLGenerator.java1711
-rw-r--r--sources/scala/tools/scaladoc/HTMLGeneratorCommand.java166
-rw-r--r--sources/scala/tools/scaladoc/HTMLGeneratorPhase.java50
-rw-r--r--sources/scala/tools/scaladoc/HTMLGeneratorPhases.java49
-rw-r--r--sources/scala/tools/scaladoc/Main.java48
-rw-r--r--sources/scala/tools/scaladoc/OneTree.java245
-rw-r--r--sources/scala/tools/scaladoc/ScalaSearch.java508
-rw-r--r--sources/scala/tools/scaladoc/StandardDocModule.java56
-rw-r--r--sources/scala/tools/scaladoc/SymbolTablePrinter.java607
-rw-r--r--sources/scala/tools/scaladoc/Tag.java150
-rw-r--r--sources/scala/tools/scaladoc/resources/script.js5
-rw-r--r--sources/scala/tools/scaladoc/resources/style.css124
18 files changed, 4147 insertions, 5 deletions
diff --git a/Makefile b/Makefile
index d82ac284b7..609ce921e5 100644
--- a/Makefile
+++ b/Makefile
@@ -100,13 +100,13 @@ INTERPRETER_SOURCES += $(INTERPRETER_LIST:%=$(INTERPRETER_ROOT)/%)
INTERPRETER_JC_FILES = $(INTERPRETER_SOURCES)
# scaladoc
-SCALADOC_ROOT = $(PROJECT_SOURCEDIR)/scaladoc
+SCALADOC_ROOT = $(PROJECT_SOURCEDIR)/scala/tools/scaladoc
SCALADOC_LIST = $(call READLIST,$(PROJECT_LISTDIR)/scaladoc.lst)
SCALADOC_SOURCES += $(SCALADOC_LIST:%=$(SCALADOC_ROOT)/%)
SCALADOC_JC_FILES = $(SCALADOC_SOURCES)
SCALADOC_RSRC_LIST = resources/style.css resources/script.js
SCALADOC_RSRC_FILES += $(SCALADOC_RSRC_LIST:%=$(SCALADOC_ROOT)/%)
-SCALADOC_RSRC_OUTPUTDIR = $(PROJECT_OUTPUTDIR)/scaladoc
+SCALADOC_RSRC_OUTPUTDIR = $(PROJECT_OUTPUTDIR)/scala/tools/scaladoc
# dtd2scala
DTD2SCALA_ROOT = $(PROJECT_SOURCEDIR)/scala/tools/dtd2scala
@@ -132,7 +132,7 @@ TOOLS_JAR_ARCHIVE = $(PROJECT_LIBRARYDIR)/$(TOOLS_NAME).jar
TOOLS_JAR_INPUTDIR = $(PROJECT_OUTPUTDIR)
TOOLS_JAR_FILES += ch
TOOLS_JAR_FILES += scalac
-TOOLS_JAR_FILES += scaladoc
+TOOLS_JAR_FILES += scala/tools/scaladoc
TOOLS_JAR_FILES += scalai
TOOLS_JAR_FILES += scala/tools/dtd2scala
TOOLS_JAR_FILES += scalap
diff --git a/config/list/scaladoc.lst b/config/list/scaladoc.lst
index d3cadea587..047a945471 100644
--- a/config/list/scaladoc.lst
+++ b/config/list/scaladoc.lst
@@ -1,5 +1,5 @@
############################################################-*-Makefile-*-####
-# scaladoc source files (paths are relative to ./sources/scaladoc)
+# scaladoc source files (paths are relative to ./sources/scala/tools/scaladoc)
##############################################################################
# $Id$
diff --git a/sources/bin/.scala_wrapper.tmpl b/sources/bin/.scala_wrapper.tmpl
index 1493314307..c0c43f8d77 100644
--- a/sources/bin/.scala_wrapper.tmpl
+++ b/sources/bin/.scala_wrapper.tmpl
@@ -500,7 +500,7 @@ configure;
case "$SCRIPT" in
scala-info ) scala_info "$@";;
scalac* ) exec_compile scalac.Main "$@";;
- scaladoc* ) exec_compile scaladoc.Main "$@";;
+ scaladoc* ) exec_compile scala.tools.scaladoc.Main "$@";;
scalarun* ) exec_interpret scalai.Main "$@";;
scalaint* ) exec_interpret scalai.Main -interactive "$@";;
dtd2scala* ) exec_dtd2scala scala.tools.dtd2scala.Main "$@";;
diff --git a/sources/scala/tools/scaladoc/Anchors.java b/sources/scala/tools/scaladoc/Anchors.java
new file mode 100644
index 0000000000..2a68a336b9
--- /dev/null
+++ b/sources/scala/tools/scaladoc/Anchors.java
@@ -0,0 +1,124 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+\* */
+
+// $Id$
+
+package scala.tools.scaladoc;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import scalac.ast.Tree;
+import scalac.ast.Tree.*;
+import scalac.symtab.Symbol;
+import scalac.util.*;
+
+/**
+ * Computes a unique URL for every defined symbol.
+ */
+class Anchors {
+
+ protected final Map/*<Symbol, String>*/ anchors = new HashMap();
+
+ /**
+ * Main function.
+ *
+ * @param tree
+ */
+ static Map/*<Symbol, String>*/ apply(Tree tree) {
+ Anchors a = new Anchors();
+ a.traverse(tree, new IntIterator(), null);
+ return a.anchors;
+ }
+
+ /**
+ * ..
+ */
+ int currentpage = 0;
+
+ /**
+ * ..
+ *
+ * @param sym
+ */
+ String symbolAnchor(Symbol sym) {
+ String anchorId = (String) anchors.get(sym);
+ if (anchorId == null) {
+ //anchorId = sym.isRoot() ? "root.html" : new Integer(anchors.size()).toString() + ".html";
+ if (sym.isRoot())
+ anchorId = "root.html";
+ else {
+ anchorId = new Integer(currentpage).toString() + ".html";
+ currentpage++;
+ }
+ anchors.put(sym, anchorId);
+ }
+ return anchorId;
+ }
+
+ /**
+ * The natural numbers.
+ */
+ static class IntIterator {
+ private int current = 0;
+ public boolean hasNext() { return true; }
+ public int next() { int res = current; current = current + 1; return res; }
+ }
+
+ /**
+ * Computes a unique URL for every defined symbol.
+ *
+ * @param members
+ * @param intIt
+ * @param ownerpage
+ */
+ void traverse(Tree[] members, IntIterator intIt, String ownerpage) {
+ for(int i = 0; i< members.length; i++)
+ traverse(members[i], intIt, ownerpage);
+ }
+
+ void traverse(Tree tree, IntIterator intIt, String ownerpage) {
+ if (tree.hasSymbol() && !ScalaSearch.isGenerated(tree.symbol())) {
+ switch (tree) {
+ case PackageDef(Tree packaged, Template impl):
+ traverse(impl.body, null, null);
+ break;
+ case ClassDef(int mods, Name name, AbsTypeDef[] tparams, ValDef[][] vparams, Tree tpe, Template impl):
+ String anchor = symbolAnchor(tree.symbol());
+ for(int i = 0; i < tparams.length; i++)
+ anchors.put(tparams[i].symbol(), anchor);
+ for(int i = 0; i < vparams.length; i++)
+ for(int j = 0; j < vparams[i].length; j++)
+ anchors.put(vparams[i][j].symbol(), anchor);
+ traverse(impl.body, new IntIterator(), anchor);
+ break;
+ case ModuleDef(int mods, Name name, Tree tpe, Template impl):
+ String anchor = symbolAnchor(tree.symbol());
+ if (tpe.isMissing())
+ traverse(impl.body, new IntIterator(), anchor);
+ break;
+ case ValDef(int mods, Name name, Tree tpe, Tree rhs):
+ anchors.put(tree.symbol(), ownerpage + "#" + intIt.next());
+ break;
+ case DefDef(int mods, Name name, AbsTypeDef[] tparams, ValDef[][] vparams, Tree tpe, Tree rhs):
+ String anchor = ownerpage + "#" + intIt.next();
+ anchors.put(tree.symbol(), anchor);
+ for(int i = 0; i < tparams.length; i++)
+ anchors.put(tparams[i].symbol(), anchor);
+ for(int i = 0; i < vparams.length; i++)
+ for(int j = 0; j < vparams[i].length; j++)
+ anchors.put(vparams[i][j].symbol(), anchor);
+ break;
+ case AbsTypeDef(_, _, _, _):
+ case AliasTypeDef(_, _, _, _):
+ anchors.put(tree.symbol(), ownerpage + "#" + intIt.next());
+ break;
+ default:
+ }
+ }
+ }
+
+}
diff --git a/sources/scala/tools/scaladoc/Comment.java b/sources/scala/tools/scaladoc/Comment.java
new file mode 100644
index 0000000000..c9fd675c89
--- /dev/null
+++ b/sources/scala/tools/scaladoc/Comment.java
@@ -0,0 +1,170 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+\* */
+
+// $Id$
+
+package scala.tools.scaladoc;
+
+import java.util.*;
+import java.util.regex.*;
+
+import ch.epfl.lamp.util.Pair;
+
+import scalac.ast.Tree;
+import scalac.symtab.Symbol;
+
+import scaladoc.*;
+
+/**
+ * Class <code>Comment</code> contains all information in comment part.
+ * It allows users to get first sentence of this comment, get comment
+ * for different tags...
+ */
+public class Comment {
+
+ /**
+ * Holder of the comment.
+ */
+ public final Symbol holder;
+
+ /**
+ * Raw text of the comment.
+ */
+ public final String rawText;
+
+ /**
+ * Text minus any tags.
+ */
+ public String text;
+
+ /**
+ * Tags minus text.
+ */
+ public Tag[] tags;
+
+ /**
+ * Constructor.
+ *
+ * @param holder
+ * @param rawText
+ */
+ public Comment(Symbol holder, String rawText) {
+ this.holder = holder;
+ this.rawText = cleanComment(rawText);
+ parseComment();
+ }
+
+ /**
+ * Returns true if this comment is empty.
+ */
+ public boolean isEmpty() {
+ return "".equals(rawText);
+ }
+
+ /**
+ * Removes the leading white spaces and asterixes.
+ *
+ * @param comment
+ */
+ protected String cleanComment(String comment) {
+ if (comment == null)
+ return "";
+ else {
+ comment = comment.substring(3, comment.length() - 2);
+ StringBuffer buff = new StringBuffer();
+ boolean startLine = true;
+ int i = 0;
+ while (i < comment.length()) {
+ char ch = comment.charAt(i);
+ if (startLine && ((ch == '\t') ||
+ (ch == ' ') ||
+ (ch == '*')))
+ i++;
+ else if (startLine)
+ startLine = false;
+ else {
+ if ((ch == '\n') || (ch == '\r'))
+ startLine = true;
+ buff.append(ch);
+ i++;
+ }
+ }
+ return buff.toString();
+ }
+ }
+
+ /**
+ * Parses the comment string separating the description
+ * text from tags.
+ */
+ protected void parseComment() {
+ String[] parts = rawText.split("\n@|\\A@");
+ if (parts.length == 0) {
+ text = "";
+ tags = new Tag[0];
+ }
+ else {
+ int startTag;
+ if (parts[0].startsWith("@")) {
+ text = "";
+ startTag = 0;
+ } else {
+ text = parts[0];
+ startTag = 1;
+ }
+ List tagList = new LinkedList();
+ for(int i = startTag; i < parts.length; i++) {
+ Pair fields = Tag.split(parts[i]);
+ String name = (String) fields.fst;
+ String description = (String) fields.snd;
+ tagList.add(new Tag(holder, "@" + name, description));
+ }
+ tags = (Tag[]) tagList.toArray(new Tag[tagList.size()]);
+ }
+ }
+
+ /**
+ * Returns an array of tags with text and inline.
+ *
+ * @param holder
+ * @param s
+ * @see See Tags for a Doc comment.
+ */
+ public static Tag[] makeTags(Symbol holder, String s) {
+ final List tagList = new LinkedList();
+ Pattern p = Pattern.compile("\\{@([^\\}]*)\\}");
+ Matcher m = p.matcher(s);
+
+ int start = 0;
+ while (m.find()) {
+ String txt = s.substring(start, m.start());
+ if (!txt.equals(""))
+ tagList.add(new Tag(holder, "@text", txt));
+ Pair fields = Tag.split(m.group(1));
+ String name = (String) fields.fst;
+ String description = (String) fields.snd;
+ tagList.add(new Tag(holder, "@" + name, description));
+ start = m.end();
+ }
+ String txt = s.substring(start, s.length());
+ if (!txt.equals(""))
+ tagList.add(new Tag(holder, "@text", txt));
+ return (Tag[]) tagList.toArray(new Tag[tagList.size()]);
+ }
+
+ /**
+ * Returns the first sentence of this comment.
+ */
+ public String firstSentence() {
+ Pattern p = Pattern.compile("\\.(\\s)");
+ Matcher m = p.matcher(text);
+ if (m.find()) {
+ return text.substring(0, m.start(1));;
+ } else
+ return text;
+ }
+
+}
diff --git a/sources/scala/tools/scaladoc/DocModule.java b/sources/scala/tools/scaladoc/DocModule.java
new file mode 100644
index 0000000000..7a55a4b9bb
--- /dev/null
+++ b/sources/scala/tools/scaladoc/DocModule.java
@@ -0,0 +1,129 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+\* */
+
+// $Id$
+
+package scala.tools.scaladoc;
+
+import ch.epfl.lamp.util.Position;
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import scalac.Global;
+import scalac.util.Debug;
+import scalac.util.Reporter;
+
+/**
+ * This class is used to dynamically load a documentation module.
+ */
+public class DocModule {
+
+ /** Name of the method to be called (convention).
+ */
+ protected static final String METHOD_NAME = "apply";
+
+ /**
+ * Complete the given path with standard class paths.
+ *
+ * @param path
+ */
+ public static String completePath(String path) {
+ String completePath = path;
+ String path1 = System.getProperty("env.class.path");
+ String path2 = System.getProperty("java.class.path");
+ if (path1 != null)
+ completePath += File.pathSeparator + path1;
+ if (path2 != null)
+ completePath += File.pathSeparator + path2;
+ return completePath;
+ }
+
+ /**
+ * Get class loader from a given path.
+ *
+ * @param path
+ */
+ public static ClassLoader getClassLoader(String path) {
+ List urlList = new LinkedList();
+ StringTokenizer st = new StringTokenizer(path, File.pathSeparator);
+ try {
+ while (st.hasMoreTokens())
+ urlList.add(new File(st.nextToken()).toURI().toURL());
+ } catch(java.net.MalformedURLException e) {
+ throw Debug.abort(e);
+ }
+ URL[] urls = (URL[]) urlList.toArray(new URL[urlList.size()]);
+ return new URLClassLoader(urls);
+ }
+
+ /**
+ * Loads the class with the specified name.
+ *
+ * @param className the name of the class
+ * @param classLoader the class loader responsible for loading the class
+ * @return the resulting <code>Class</code> object
+ */
+ public static Class getClass(String className, ClassLoader classLoader) {
+ Class cls = null;
+ try {
+ cls = classLoader.loadClass(className);
+ } catch (ClassNotFoundException exc) {
+ throw Debug.abort("Class not found", className);
+ }
+ return cls;
+ }
+
+ /**
+ * Get the method.
+ *
+ * @param methodName the name of the method
+ * @param cls the class containing the method
+ * @return the resulting <code>Method</code> object
+ */
+ public static Method getMethod(String methodName, Class cls) {
+ Method meth = null;
+ try {
+ meth = cls.getMethod(methodName,
+ new Class[] { Class.forName("scalac.Global") });
+ } catch (Exception e) {
+ throw Debug.abort(e);
+ }
+ return meth;
+ }
+
+ /**
+ * ..
+ *
+ * @param global
+ */
+ public static void apply(Global global) {
+ assert global.args instanceof HTMLGeneratorCommand;
+ HTMLGeneratorCommand args = (HTMLGeneratorCommand) global.args;
+
+ // complete path
+ String path = completePath(args.docmodulePath.value);
+ // class loader
+ ClassLoader classLoader = getClassLoader(args.docmodulePath.value);
+ // module class
+ Class cls = getClass(args.docmodule.value, classLoader);
+ // method
+ Method meth = getMethod(METHOD_NAME, cls);
+ // call method
+ Thread.currentThread().setContextClassLoader(classLoader);
+ try {
+ meth.invoke(null, new Object[] { global });
+ } catch (Exception e) {
+ throw Debug.abort(e);
+ }
+ }
+
+}
diff --git a/sources/scala/tools/scaladoc/HTMLGenerator.java b/sources/scala/tools/scaladoc/HTMLGenerator.java
new file mode 100644
index 0000000000..1f63e0c6c3
--- /dev/null
+++ b/sources/scala/tools/scaladoc/HTMLGenerator.java
@@ -0,0 +1,1711 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+\* */
+
+// $Id$
+
+package scala.tools.scaladoc;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.InputStream;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Stack;
+
+import ch.epfl.lamp.util.XMLAttribute;
+import ch.epfl.lamp.util.HTMLPrinter;
+import ch.epfl.lamp.util.HTMLRepresentation;
+import ch.epfl.lamp.util.Pair;
+import ch.epfl.lamp.util.Position;
+import ch.epfl.lamp.util.XHTMLPrinter;
+
+import scalac.Global;
+import scalac.ast.Tree;
+import scalac.ast.Tree.AbsTypeDef;
+import scalac.ast.Tree.Template;
+import scalac.ast.Tree.ValDef;
+import scalac.symtab.Kinds;
+import scalac.symtab.Modifiers;
+import scalac.symtab.NoSymbol;
+import scalac.symtab.Scope;
+import scalac.symtab.Scope.SymbolIterator;
+import scalac.symtab.Symbol;
+import scalac.symtab.Type;
+import scalac.symtab.Type.*;
+import scalac.util.Debug;
+import scalac.util.Name;
+import scalac.util.Strings;
+
+/**
+ * The class <code>HTMLGenerator</code> generates
+ * the HTML documentation for a given Scala tree.
+ */
+public class HTMLGenerator {
+
+ public static final String PRODUCT =
+ System.getProperty("scala.product", "scaladoc");
+ public static final String VERSION =
+ System.getProperty("scala.version", "unknown version");
+
+ public static final String DEFAULT_DOCTITLE = "";
+ public static final String DEFAULT_WINDOWTITLE = "Generated Documentation";
+
+ /*
+ * Names of predefined page names.
+ */
+ protected final String PAGE_DEFAULT_SUFFIX = ".html";
+ protected final String PAGE_OBJECT_SUFFIX = "-object" + PAGE_DEFAULT_SUFFIX;
+
+ protected final String FRAME_PAGE_NAME = "index.html";
+ protected final String INDEX_PAGE_NAME = "all.html";
+ protected final String PAGE_ALLCLASSES_FRAME = "allclasses.html";
+ protected final String PAGE_HELP = "help-doc.html";
+ protected final String PAGE_OVERVIEW_FRAME = "overview-frame.html";
+ protected final String PAGE_OVERVIEW_SUMMARY = "overview-summary.html";
+ protected final String PAGE_PACKAGE_FRAME = "package-frame.html";
+ protected final String PAGE_PACKAGE_SUMMARY = "package-summary.html";
+
+ /*
+ * Names of frames.
+ */
+ protected final String ROOT_FRAME_NAME = "rootFrame";
+ protected final String PACKAGES_FRAME_NAME = "packagesFrame";
+ protected final String CLASSES_FRAME_NAME = "classesFrame";
+
+ /*
+ * HTML meta information.
+ */
+ protected final String GENERATOR = PRODUCT + " (" + VERSION + ")";
+ protected final SimpleDateFormat df = new SimpleDateFormat("EEE MMM d HH:mm:ss z yyyy");
+
+ /*
+ * XML attributes.
+ */
+ protected final XMLAttribute[] ATTRS_DOCTAG =
+ new XMLAttribute[]{
+ new XMLAttribute("style", "margin-top:10px;")
+ };
+ protected final XMLAttribute[] ATTRS_ENTITY =
+ new XMLAttribute[]{ new XMLAttribute("class", "entity") };
+
+ protected final XMLAttribute[] ATTRS_LIST =
+ new XMLAttribute[] { new XMLAttribute("class", "list")};
+
+ protected final XMLAttribute[] ATTRS_MEMBER =
+ new XMLAttribute[]{
+ new XMLAttribute("cellpadding", "3"),
+ new XMLAttribute("class", "member")
+ };
+ protected final XMLAttribute[] ATTRS_MEMBER_DETAIL =
+ new XMLAttribute[]{
+ new XMLAttribute("cellpadding", "3"),
+ new XMLAttribute("class", "member-detail")
+ };
+
+ protected final XMLAttribute[] ATTRS_MEMBER_TITLE =
+ new XMLAttribute[]{ new XMLAttribute("class", "member-title") };
+
+ protected final XMLAttribute[] ATTRS_META =
+ new XMLAttribute[]{ new XMLAttribute("generator", GENERATOR) };
+
+ protected final XMLAttribute[] ATTRS_MODIFIERS =
+ new XMLAttribute[]{
+ new XMLAttribute("valign", "top"),
+ new XMLAttribute("class", "modifiers")
+ };
+ protected final XMLAttribute[] ATTRS_NAVIGATION =
+ new XMLAttribute[]{ new XMLAttribute("class", "navigation") };
+
+ protected final XMLAttribute[] ATTRS_NAVIGATION_LINKS =
+ new XMLAttribute[]{
+ new XMLAttribute("valign", "top"),
+ new XMLAttribute("class", "navigation-links")
+ };
+ protected final XMLAttribute[] ATTRS_NAVIGATION_ENABLED = new XMLAttribute[]{
+ new XMLAttribute("class", "navigation-enabled") };
+
+ protected final XMLAttribute[] ATTRS_NAVIGATION_SELECTED = new XMLAttribute[]{
+ new XMLAttribute("class", "navigation-selected") };
+
+ protected final XMLAttribute[] ATTRS_NAVIGATION_PRODUCT =
+ new XMLAttribute[]{
+ new XMLAttribute("align", "right"),
+ new XMLAttribute("valign", "top"),
+ new XMLAttribute("style", "white-space:nowrap;"),
+ new XMLAttribute("rowspan", "2")
+ };
+ protected final XMLAttribute[] ATTRS_PAGE_TITLE = new XMLAttribute[]{
+ new XMLAttribute("class", "page-title")
+ };
+ protected final XMLAttribute[] ATTRS_SIGNATURE =
+ new XMLAttribute[]{ new XMLAttribute("class", "signature") };
+
+ protected final XMLAttribute[] ATTRS_TITLE_SUMMARY =
+ new XMLAttribute[]{
+ new XMLAttribute("colspan", "2"),
+ new XMLAttribute("class", "title")
+ };
+ protected final XMLAttribute[] ATTRS_VALIDATION =
+ new XMLAttribute[]{
+ new XMLAttribute("style", "margin-top:5px; text-align:center; font-size:9pt;")
+ };
+
+ /** The unique documented tree.
+ */
+ protected final Tree tree;
+
+ /** Global compiler environment.
+ */
+ protected final Global global;
+
+ /** Maps classes to their direct implementing classes or modules.
+ */
+ protected final Map subs;
+
+ /** Directory where to put generated HTML pages.
+ */
+ protected File directory;
+
+ /** HTML anchors for defined symbols.
+ */
+ protected final Map/*<Symbol, String>*/ anchors;
+
+ /** Comments associated with symbols.
+ */
+ protected Map/*<Symbol, Comment>*/ comments = new HashMap();
+
+ /** The underlying HTML printer.
+ */
+ protected HTMLPrinter page;
+
+ /**
+ * The underlying symbol table printer.
+ */
+ protected SymbolTablePrinter symtab;
+
+ /**
+ * The underlying document representation of the generated documentation.
+ */
+ protected HTMLRepresentation representation;
+
+ /**
+ * The command option settings.
+ */
+ protected String windowtitle;
+ protected String doctitle;
+ protected String stylesheet;
+ protected boolean noindex;
+ protected boolean validate;
+
+ /**
+ * HTML pages may be generated recursively,
+ * so we need to save active printers.
+ */
+ protected final Stack stack = new Stack();
+
+ /**
+ * Dummy symbols used as markers for the generation of special HTML pages
+ */
+ protected final Symbol indexPage = new NoSymbol();
+ protected final Symbol helpPage = new NoSymbol();
+
+ /**
+ * Creates a new instance.
+ *
+ * @param tree
+ * @param global
+ */
+ protected HTMLGenerator(Tree tree, Global global) {
+ this.tree = tree;
+ this.global = global;
+ this.subs = ScalaSearch.subTemplates(tree);
+ this.anchors = Anchors.apply(tree);
+
+ assert global.args instanceof HTMLGeneratorCommand;
+ HTMLGeneratorCommand args = (HTMLGeneratorCommand) global.args;
+ this.representation = new HTMLRepresentation(
+ args.doctype.value,
+ args.docencoding.value,
+ HTMLRepresentation.DEFAULT_DOCLANGUAGE);
+ this.windowtitle = args.windowtitle.value;
+ this.doctitle = args.doctitle.value;
+ this.stylesheet = args.stylesheet.value;
+ this.noindex = args.noindex.value;
+ this.validate = args.validate.value;
+ }
+
+ /**
+ * Creates two new printer objects to generate HTML documents.
+ *
+ * @param anchor
+ * @param title The title of the generated HTML document.
+ */
+ protected void createPrinters(String anchor, String title) {
+ try {
+ BufferedWriter out =
+ new BufferedWriter(new FileWriter(directory.getPath() + File.separator + anchor));
+ stack.push(page);
+ stack.push(symtab);
+ if (representation.isXHTMLType())
+ page = new XHTMLPrinter(out, title, representation, stylesheet);
+ else
+ page = new HTMLPrinter(out, title, representation, stylesheet);
+ symtab = new SymbolTablePrinter(this, page);
+ } catch (IOException exception) {
+ throw Debug.abort(exception); // !!! reporting an error would be wiser
+ }
+ }
+
+ private String getRelativePath(String path, String name) {
+ StringBuffer buf = new StringBuffer();
+ int i = path.indexOf(File.separatorChar);
+ while (i >= 0) {
+ buf.append("../");
+ i = path.indexOf(File.separatorChar, i + 1);
+ }
+ buf.append(name);
+ return buf.toString();
+ }
+
+ protected String createPrinters(Symbol sym, boolean isSummary) {
+ try {
+ String title, name;
+ if (sym.isRoot()) {
+ title = (isSummary) ? "Overview" : "All Classes";
+ name = (isSummary) ? PAGE_OVERVIEW_SUMMARY : PAGE_ALLCLASSES_FRAME;
+ } else if (sym.isPackage()) {
+ title = sym.fullNameString();
+ name = sym.nameString() + "/" + ((isSummary) ? PAGE_PACKAGE_SUMMARY : PAGE_PACKAGE_FRAME);
+ } else {
+ title = sym.nameString().replaceAll("::", "_cons");
+ name = title + (sym.isModule() ? PAGE_OBJECT_SUFFIX : PAGE_DEFAULT_SUFFIX);
+ }
+
+ String path;
+ if (sym.isRoot())
+ path = name;
+ else if (sym.owner().isRoot())
+ path = name.replace('/', File.separatorChar);
+ else {
+ path = sym.owner().fullNameString().replace('.', File.separatorChar);
+ File f = new File(directory, path);
+ f.mkdirs();
+ path = path + File.separator + name.replace('/', File.separatorChar);
+ }
+ BufferedWriter out =
+ new BufferedWriter(new FileWriter(directory.getPath() + File.separator + path));
+ stack.push(page);
+ stack.push(symtab);
+ String stylePath = getRelativePath(path, stylesheet);
+ String scriptPath = getRelativePath(path, HTMLPrinter.DEFAULT_JAVASCRIPT);
+ if (representation.isXHTMLType())
+ page = new XHTMLPrinter(out, title, representation, stylePath, scriptPath);
+ else
+ page = new HTMLPrinter(out, title, representation, stylePath, scriptPath);
+ symtab = new SymbolTablePrinter(this, page);
+ return title;
+ } catch (IOException exception) {
+ throw Debug.abort(exception); // !!! reporting an error would be wiser
+ }
+ }
+
+ /**
+ * Closes the Writer object associated with both printer objects.
+ */
+ protected void closePrinters() {
+ try {
+ page.getCodePrinter().getWriter().close();
+ symtab = (SymbolTablePrinter) stack.pop();
+ page = (HTMLPrinter) stack.pop();
+ } catch (IOException exception) {
+ throw Debug.abort(exception); // !!! reporting an error would be wiser
+ }
+ }
+
+ private boolean checkOutpath() {
+ String text = "Output path \"" + global.outpath + "\" ";
+ boolean ok = false;
+ try {
+ directory = new File(global.outpath);
+ if (! directory.exists())
+ global.reporter.error(null, text + "does not exist");
+ else if (! directory.isDirectory())
+ global.reporter.error(null, text + "is not a directory");
+ else if (! directory.canWrite())
+ global.reporter.error(null, text + "cannot be modified");
+ else
+ ok = true;
+ } catch (NullPointerException e) {
+ global.reporter.error(null, e.getMessage());
+ }
+ return ok;
+ }
+
+ /**
+ * Generates the HTML pages.
+ */
+ protected void apply() {
+ if (! checkOutpath())
+ return;
+
+ // page with list of packages
+ createPackageIndexPage("Overview");
+
+ // class and object pages
+ createPages(tree);
+
+ if (!noindex) {
+ // page with index of Scala documented entities.
+ createIndexPage("Scala Library Index");
+ }
+
+ createHelpPage("API Help");
+
+ // page with list of objects and classes.
+ createContainerIndexPage(tree);
+
+ // frame description page
+ createFramePage(windowtitle);
+
+ // style sheet
+ createResource(HTMLPrinter.DEFAULT_STYLESHEET);
+
+ // script
+ createResource(HTMLPrinter.DEFAULT_JAVASCRIPT);
+ }
+
+ /**
+ * Main function.
+ */
+ public static void apply(Tree tree, Global global) {
+ new HTMLGenerator(tree, global).apply();
+ }
+
+ /**
+ * Tests if the definition of this symbol appears in the
+ * documentation.
+ *
+ * @param sym
+ */
+ protected boolean isReferenced(Symbol sym) {
+ return anchors.containsKey(OneTree.symbol(sym));
+ }
+
+ /**
+ * Returns the URL location of <code>user</code> relative to
+ * the <code>sym</code> location, i.e.
+ * if the name of <code>user</code> is "<code>scala.concurrent.pilib</code>"
+ * and the name of <code>sym</code> is "<code>scala.Monitor</code>"
+ * then the relative location of <code>sym2</code> is "<code>../</code>".
+ *
+ * @param sym The symbol used by the <code>user</code> symbol
+ * @param user The symbol using the symbol <code>sym</code>
+ */
+ private String getRelativeLocation(Symbol sym, Symbol user) {
+ assert sym != null && user != null;
+ if (user == Symbol.NONE)
+ return sym.nameString();
+
+ String sName = sym.fullNameString();
+ if (user.isRoot())
+ return sName.replace('.', '/');
+ if (sym.isPackage()) sName += ".";
+ String uName = user.fullNameString();
+ if (user.isPackage()) uName = uName + ".";
+ int offset = 0;
+ int inx;
+ while ((inx = sName.indexOf('.', offset)) > 0 &&
+ sName.regionMatches(offset, uName, offset, inx - offset + 1)) {
+ offset = inx + 1;
+ }
+ int sOffset = offset;
+ int uOffset = offset;
+ int n1 = 0;
+ while ((inx = sName.indexOf('.', sOffset)) > 0) {
+ sOffset = inx + 1;
+ n1++;
+ }
+ int n2 = 0;
+ while ((inx = uName.indexOf('.', uOffset)) > 0) {
+ uOffset = inx + 1;
+ n2++;
+ }
+ StringBuffer buf = new StringBuffer();
+ while (n2 > 0) { buf.append("../"); n2--; }
+ if (n1 > 0)
+ buf.append(sName.substring(offset, sOffset).replace('.', '/'));
+ return buf.toString();
+ }
+
+ /**
+ * Gives the HTML reference of this symbol.
+ *
+ * @param sym
+ */
+ protected String ref(Symbol sym, Symbol user, String targetName) {
+ assert isReferenced(sym) : sym;
+ String name;
+ String suffix;
+ if (sym.isPackage()) {
+ name = getRelativeLocation(sym, user);
+ suffix = name.endsWith("/") ? targetName : "/" + targetName;
+ } else if (sym.isModule() || sym.isModuleClass()) {
+ name = getRelativeLocation(sym, user);
+ if (name.length() == 0 || name.endsWith("/"))
+ name += sym.nameString();
+ suffix = PAGE_OBJECT_SUFFIX;
+ } else if (sym.isClass() || sym.isTrait()) {
+ name = getRelativeLocation(sym, user);
+ if (name.length() == 0 || name.endsWith("/"))
+ name += sym.nameString();
+ suffix = PAGE_DEFAULT_SUFFIX;
+ } else if (sym.isMethod()) {
+ name = getRelativeLocation(sym.owner(), user);
+ suffix = PAGE_DEFAULT_SUFFIX + "#" + sym.nameString();
+ } else {
+ name = getRelativeLocation(sym, user) + sym.nameString();
+ suffix = PAGE_DEFAULT_SUFFIX;
+ }
+ return name.replaceAll("::", "_cons") + suffix;
+ }
+
+ protected String ref(Symbol sym, Symbol user) {
+ return ref(sym, user, PAGE_PACKAGE_SUMMARY);
+ }
+
+ protected String ref(Symbol sym) {
+ return ref(sym, Symbol.NONE);
+ }
+
+ /**
+ * Give the HTML label of this symbol.
+ *
+ * @param sym
+ */
+ protected String label(Symbol sym) {
+ String anchor = (String) anchors.get(OneTree.symbol(sym));
+ return anchor.substring(anchor.indexOf('#') + 1);
+ }
+
+ /**
+ * Returns the comment associated with a given symbol.
+ *
+ * @param sym
+ */
+ protected Comment getComment(Symbol sym) {
+ Comment comment = (Comment) comments.get(sym);
+ if (comment == null) {
+ String s = (String) global.mapSymbolComment.get(sym);
+ comment = new Comment(sym, s);
+ comments.put(sym, comment);
+ }
+ return comment;
+ }
+
+ /**
+ * Returns members of a class or object definition tree.
+ *
+ * @param tree
+ */
+ protected Tree[][] members(Tree tree) {
+ switch (tree) {
+ case ClassDef(int mods, Name name, AbsTypeDef[] tparams, ValDef[][] vparams, Tree tpe, Template impl):
+ return members(impl.body);
+ case ModuleDef(int mods, Name name, Tree tpe, Template impl):
+ return members(impl.body);
+ default:
+ throw Debug.abort("illegal tree", tree);
+ }
+ }
+
+ private Tree[][] members(Tree[] trees) {
+ List fields = new LinkedList();
+ List methods = new LinkedList();
+ List objects = new LinkedList();
+ List traits = new LinkedList();
+ List classes = new LinkedList();
+ List packages = new LinkedList();
+ for (int i = 0; i < trees.length; i++) {
+ Symbol sym = trees[i].symbol();
+ if (sym.isTrait()) traits.add(trees[i]);
+ else if (sym.isClass()) classes.add(trees[i]);
+ else if (sym.isPackage()) packages.add(trees[i]);
+ else if (sym.isModule()) objects.add(trees[i]);
+ else if (sym.isMethod()) methods.add(trees[i]);
+ else fields.add(trees[i]);
+ }
+ return new Tree[][] {
+ (Tree[]) fields.toArray(new Tree[fields.size()]),
+ (Tree[]) methods.toArray(new Tree[fields.size()]),
+ (Tree[]) objects.toArray(new Tree[objects.size()]),
+ (Tree[]) traits.toArray(new Tree[traits.size()]),
+ (Tree[]) classes.toArray(new Tree[classes.size()]),
+ (Tree[]) packages.toArray(new Tree[packages.size()])
+ };
+ }
+
+ protected String getGenerator() {
+ return "Generated by " + GENERATOR + " on " + df.format(new Date());
+ }
+
+ /**
+ * Generates a HTML page for a class or object definition as well
+ * as pages for every inner class or object.
+ *
+ * @param tree
+ */
+ protected void createPages(Tree tree) {
+ Symbol sym = tree.symbol();
+ String title = createPrinters(sym, true /*isSummary*/);
+
+ page.printHeader(ATTRS_META, getGenerator());
+ String windowTitle = title + " (" + doctitle.replaceAll("<.*>", " ") + ")";
+ page.printOpenBody(new XMLAttribute[]{
+ new XMLAttribute("onload", "setWindowTitle('" + windowTitle + "');")
+ });
+ addNavigationBar(sym);
+ page.printlnHLine();
+
+ addTitle(sym);
+ addDocumentationComment(sym);
+ page.printlnHLine();
+
+ String[] titles = new String[]{ "Field", "Method", "Object",
+ "Trait", "Class", "Package" }; // "Constructor"
+ String[] inherited = new String[]{ "Fields", "Methods", "Objects",
+ "Traits", "Classes", "Packages" };
+ Tree[][] members = members(tree);
+ for (int i = 0; i < titles.length; i++) {
+ addMemberSummary(members[i], titles[i] + " Summary");
+ if (i == 1) addInheritedMembers(sym, inherited[i]);
+ }
+ for (int i = 0; i < titles.length; i++)
+ addMemberDetail(members[i], titles[i] + " Detail");
+
+ page.printlnHLine();
+ addNavigationBar(sym);
+ if (validate)
+ addValidationBar();
+ page.printFootpage();
+
+ closePrinters();
+ }
+
+ /**
+ * Returns the string representation of the kind of a symbol.
+ *
+ * @param sym
+ */
+ protected String kind(Symbol sym) {
+ return symtab.getSymbolKeyword(sym);
+ }
+
+ /**
+ * Writes the product name and version to the current page.
+ *
+ * @param attrs
+ */
+ protected void addDocumentationTitle(XMLAttribute[] attrs) {
+ page.printlnOTag("div", attrs).indent();
+ page.println(doctitle).undent();
+ page.printlnCTag("div");
+ }
+
+ /**
+ * Writes the navigation bar to the current page.
+ *
+ * @param sym
+ */
+ protected void addNavigationBar(Symbol sym) {
+ String overviewLink, indexLink, helpLink;
+ if (sym == indexPage || sym == helpPage || sym.isRoot()) {
+ overviewLink = PAGE_OVERVIEW_SUMMARY;
+ indexLink = INDEX_PAGE_NAME;
+ helpLink = PAGE_HELP;
+ } else {
+ String path = sym.fullNameString().replace('.', File.separatorChar);
+ if (sym.isPackage())
+ path = path + File.separator;
+ overviewLink = getRelativePath(path, PAGE_OVERVIEW_SUMMARY);
+ indexLink = getRelativePath(path, INDEX_PAGE_NAME);
+ helpLink = getRelativePath(path, PAGE_HELP);
+ }
+
+ page.printlnOTag("table", ATTRS_NAVIGATION).indent();
+ page.printlnOTag("tr").indent();
+
+ // links
+ page.printlnOTag("td", ATTRS_NAVIGATION_LINKS).indent();
+ page.printlnOTag("table").indent();
+ page.printlnOTag("tr").indent();
+ if (sym == indexPage || sym == helpPage) {
+ page.printOTag("td", ATTRS_NAVIGATION_ENABLED);
+ page.printAhref(overviewLink, "Overview");
+ page.printlnCTag("td");
+ page.printlnTag("td", "Package");
+ page.printlnTag("td", "Class");
+ if (sym == indexPage) {
+ page.printlnTag("td", ATTRS_NAVIGATION_SELECTED, "Index");
+ page.printOTag("td", ATTRS_NAVIGATION_ENABLED);
+ page.printAhref(helpLink, "Help");
+ page.printlnCTag("td");
+ } else { // help page
+ page.printOTag("td", ATTRS_NAVIGATION_ENABLED);
+ page.printAhref(indexLink, "Index");
+ page.printlnCTag("td");
+ page.printlnTag("td", ATTRS_NAVIGATION_SELECTED, "Help");
+ }
+ } else {
+ if (sym.isRoot()) { // overview page
+ page.printlnTag("td", ATTRS_NAVIGATION_SELECTED, "Overview");
+ page.printlnTag("td", "Package");
+ page.printlnTag("td", "Class");
+ } else if (sym.isPackage()) {
+ page.printOTag("td", ATTRS_NAVIGATION_ENABLED);
+ page.printAhref(overviewLink, "Overview");
+ page.printlnCTag("td");
+ page.printlnTag("td", ATTRS_NAVIGATION_SELECTED, "Package");
+ page.printlnTag("td", "Class");
+ } else {
+ page.printOTag("td", ATTRS_NAVIGATION_ENABLED);
+ page.printAhref(overviewLink, "Overview");
+ page.printlnCTag("td");
+ page.printOTag("td", ATTRS_NAVIGATION_ENABLED);
+ page.printAhref(PAGE_PACKAGE_SUMMARY, "Package");
+ page.printlnCTag("td");
+ page.printlnTag("td", ATTRS_NAVIGATION_SELECTED, "Class");
+ }
+ if (! noindex) {
+ page.printOTag("td", ATTRS_NAVIGATION_ENABLED);
+ page.printAhref(indexLink, "Index");
+ page.printlnCTag("td");
+ }
+ page.printOTag("td", ATTRS_NAVIGATION_ENABLED);
+ page.printAhref(helpLink, "Help");
+ page.printlnCTag("td");
+ }
+ page.undent();
+ page.printlnCTag("tr").undent();
+ page.printlnCTag("table").undent();
+ page.printlnCTag("td");
+
+ // product & version
+ page.printlnOTag("td", ATTRS_NAVIGATION_PRODUCT).indent();
+ addDocumentationTitle(new XMLAttribute[]{
+ new XMLAttribute("class", "doctitle")});
+ page.undent();
+ page.printlnCTag("td").undent();
+
+ page.printlnCTag("tr");
+
+ page.printlnOTag("tr").indent();
+ page.printlnTag("td", "&nbsp;").undent();
+ page.printlnCTag("tr").undent();
+ page.printlnCTag("table");
+ }
+
+ /**
+ * Writes the validation bar to the current page.
+ */
+ protected void addValidationBar() {
+ page.printlnOTag("div", ATTRS_VALIDATION);
+ page.indent();
+ page.printlnAhref(
+ "http://validator.w3.org/check/referer",
+ "validate html");
+ page.undent();
+ page.printlnCTag("div");
+ }
+
+ /**
+ * Writes the signature of the class or object to the current page.
+ *
+ * @param sym
+ */
+ protected void addTitle(Symbol sym) {
+ if (sym.isRoot()) {
+ page.printlnOTag("div", ATTRS_PAGE_TITLE).indent();
+ page.println(doctitle.replaceAll("<.*>", " "));
+ page.printlnSTag("br");
+ page.println("API Specification").undent();
+ page.printlnCTag("div");
+ page.println("This document is the API specification for "
+ + doctitle.replaceAll("<.*>", " ") + ".");
+ page.printlnSTag("p");
+ } else if (sym.isPackage()) {
+ page.printlnOTag("div", ATTRS_ENTITY).indent();
+ page.print("Package ");
+ if (sym.owner().isRoot())
+ page.println(sym.nameString()).undent();
+ else
+ page.println(sym.fullNameString()).undent();
+ page.printlnCTag("div");
+ } else {
+ // owner
+ page.printlnOTag("div", ATTRS_ENTITY).indent();
+ page.printOTag("span",
+ new XMLAttribute[]{ new XMLAttribute("style", "font-size:small;") });
+ page.print(sym.owner().fullNameString());
+ page.printCTag("span");
+ page.printlnSTag("br");
+
+ // kind and name
+ page.print(kind(sym) + " ");
+ page.printlnTag("span", ATTRS_ENTITY, sym.nameString()).undent();
+ page.printlnCTag("div");
+ page.printlnHLine();
+
+ // complete signature
+ // !!! page.println(printer().printTemplateHtmlSignature(sym, false).toString());
+ symtab.printTemplateHtmlSignature(sym, sym.owner(), false);
+
+ // implementing classes or modules
+ if (sym.isClass()) {
+ List subList = (List) subs.get(sym);
+ if (subList != null && subList.size() != 0) {
+ page.printlnOTag("dl").indent();
+ page.printlnOTag("dt");
+ page.printlnBold("Implementing classes or objects:");
+ page.printlnCTag("dt");
+ Iterator it = subList.iterator();
+ while (it.hasNext()) {
+ Pair p = (Pair) it.next();
+ Symbol sub = (Symbol) p.fst;
+ Type tipe = (Type) p.snd;
+ page.printlnOTag("dd");
+ defString(sub, sym, true /*addLink*/);
+ if (sub.owner() != sym.owner()) {
+ page.print(" in ");
+ page.printlnAhref(ref(sub.owner(), sym.owner()),
+ sub.owner().fullNameString());
+ }
+ page.printlnCTag("dd");
+ }
+ page.undent();
+ page.printlnCTag("dl");
+ }
+ }
+ }
+ }
+
+ /**
+ * Writes a documentation comment to the current page.
+ *
+ * @param sym
+ */
+ protected void addDocumentationComment(Symbol sym) {
+ Comment comment = getComment(sym);
+ if (!comment.isEmpty()) {
+ page.printlnHLine();
+ addComments(comment);
+ }
+ }
+
+ /**
+ * Writes a sorted list of all members with a short summary
+ * for each one.
+ *
+ * @param members
+ * @param title
+ */
+ protected void addMemberSummary(Tree[] members, String title) {
+ if (members.length > 0) {
+ Tree[] sortedMembers = new Tree[members.length];
+ for (int i = 0; i < members.length; i++)
+ sortedMembers[i] = members[i];
+ Arrays.sort(sortedMembers, ScalaSearch.alphaOrder);
+
+ // open table
+ page.printlnOTag("table", ATTRS_MEMBER).indent();
+
+ // title
+ page.printlnOTag("tr").indent();
+ page.printlnOTag("td", ATTRS_TITLE_SUMMARY).indent();
+ page.println(title).undent();
+ page.printlnCTag("td").undent();
+ page.printlnCTag("tr");
+
+ // members
+ for (int i = 0; i < members.length; i++)
+ addMemberSummary(sortedMembers[i].symbol());
+
+ // close table
+ page.undent();
+ page.printlnCTag("table");
+ page.printlnSTag("br");
+ }
+ }
+
+ /**
+ * Writes the summary of a member symbol to the current page.
+ *
+ * @param sym
+ */
+ protected void addMemberSummary(Symbol sym) {
+ if (isReferenced(sym)) {
+ String anchor = (String) anchors.get(sym);
+ if (anchor != null) {
+ page.printlnOTag("tr").indent();
+
+ // modifiers
+ String mods = Modifiers.Helper.toString(sym.flags);
+ page.printlnOTag("td", ATTRS_MODIFIERS).indent();
+ if (mods.length() > 0)
+ page.printlnTag("code", mods);
+ else
+ page.printlnNbsp(1);
+ page.undent();
+ page.printlnCTag("td");
+
+ // signature
+ page.printlnOTag("td", ATTRS_SIGNATURE).indent();
+ page.printOTag("code");
+ defString(sym, Symbol.NONE, true /*addLink*/);
+ page.printlnCTag("code");
+
+ // short description
+ String firstSentence = firstSentence(getComment(sym));
+ if (! firstSentence.equals("")) {
+ page.printlnSTag("br");
+ page.printNbsp(4);
+ page.println(firstSentence);
+ }
+ page.undent();
+ page.printlnCTag("td").undent();
+ page.printlnCTag("tr");
+ }
+ }
+ }
+
+ /**
+ * Adds a list of all members with all details.
+ *
+ * @param members
+ */
+ protected void addMemberDetail(Tree[] members, String title) {
+ boolean first = true;
+ for (int i = 0; i < members.length; i++) {
+ Symbol sym = members[i].symbol();
+ if (sym.isClass() || sym.isModule()) {
+ createPages(members[i]);
+ if (sym.isPackage())
+ createContainerIndexPage(members[i]);
+ } else {
+ if (first) {
+ page.printlnOTag("table", ATTRS_MEMBER_DETAIL).indent();
+ page.printlnOTag("tr").indent();
+ page.printlnTag("td", ATTRS_MEMBER_TITLE, title).undent();
+ page.printlnCTag("tr").undent();
+ page.printlnCTag("table");
+ first = false;
+ } else
+ page.printlnHLine();
+ addMemberDetail(sym);
+ }
+ }
+ }
+
+ /**
+ * Writes the detail of a member symbol to the page, but create
+ * instead a separate page for a class or an object.
+ *
+ * @param sym
+ */
+ protected void addMemberDetail(Symbol sym) {
+ if (isReferenced(sym)) {
+ // title with label
+ page.printlnAname(sym.nameString(), "");
+ page.printTag("h3", sym.nameString());
+
+ // signature
+ page.printlnOTag("pre");
+ String mods = Modifiers.Helper.toString(sym.flags);
+ if (mods.length() > 0) page.print(mods + " ");
+ symtab.printSignature(sym, Symbol.NONE, false /*addLink*/);
+ page.printlnCTag("pre");
+
+ // comment
+ addComments(getComment(sym));
+ }
+ }
+
+ /**
+ * Add for each "strict" base type of this class or object symbol
+ * the members that are inherited by this class or object.
+ *
+ * @param sym
+ */
+ protected void addInheritedMembers(Symbol sym, String inheritedMembers) {
+ Symbol[] syms = ScalaSearch.findMembers(sym);
+ Pair grouped = ScalaSearch.groupSymbols(syms);
+ Symbol[] owners = (Symbol[]) grouped.fst;
+ Map/*<Symbol, Symbol[]>*/ group = (Map) grouped.snd;
+ for (int i = 0; i < owners.length; i++) {
+ if (owners[i] != sym.moduleClass()) {
+ page.printlnOTag("table", ATTRS_MEMBER).indent();
+
+ // owner
+ page.printlnOTag("tr").indent();
+ page.printlnOTag("td", new XMLAttribute[]{
+ new XMLAttribute("class", "inherited-owner")}).indent();
+ page.print(inheritedMembers + " inherited from ");
+ String ownerName = owners[i].fullNameString();
+ if (isReferenced(owners[i]))
+ page.printlnAhref(ref(owners[i], sym), ROOT_FRAME_NAME, ownerName);
+ else
+ page.println(ownerName);
+ page.undent();
+ page.printlnCTag("td").undent();
+ page.printlnCTag("tr");
+
+ // members
+ page.printlnOTag("tr").indent();
+ page.printlnOTag("td", new XMLAttribute[]{
+ new XMLAttribute("class", "inherited-members")}).indent();
+ Symbol[] members = (Symbol[]) group.get(owners[i]);
+ for (int j = 0; j < members.length; j++) {
+ if (j > 0) page.print(", ");
+ String memberName = members[j].nameString();
+ if (isReferenced(members[j]))
+ page.printAhref(ref(members[j], sym), ROOT_FRAME_NAME, memberName);
+ else
+ page.print(memberName);
+ }
+ page.undent();
+ page.printlnCTag("td").undent();
+ page.printlnCTag("tr").undent();
+ page.printlnCTag("table");
+ page.printlnSTag("br");
+ }
+ }
+ }
+
+ /**
+ * Writes the string representation of the signature of a definition.
+ *
+ * @param sym
+ * @param addLink Generates an hypertext reference if the parameter
+ * <code>addLink</code> is <code>true</code>
+ */
+ void defString(Symbol sym, Symbol user, boolean addLink) {
+ if (sym.isRoot())
+ page.print("Root class");
+ else if (sym.isClass() || sym.isModule())
+ symtab.printTemplateSignature(sym, user, addLink);
+ else if (sym.isType() && !sym.isParameter())
+ symtab.printShortSignature(sym, user, addLink);
+ else
+ symtab.printSignature(sym, user, addLink);
+ }
+
+ /**
+ * Removes the longest prefix of this type which corresponds to a
+ * nested of class and object definitions. The idea is that this
+ * prefix is redondant and can be recovered directly from the tree
+ * itself.
+ *
+ * @param prefix
+ */
+ protected Type cleanPrefix(Type prefix) {
+ if (prefix == null) return null;
+ if (prefix.symbol().kind == Kinds.NONE) return null;
+ if (prefix.symbol().isRoot()) return null;
+
+ // Next line should be removed in theory.
+ if (prefix.symbol().moduleClass() == global.definitions.JAVALANG_CLASS)
+ return null;
+
+ switch(prefix) {
+ case ThisType(Symbol sym):
+ if (sym.isPackage() && isReferenced(sym.module()))
+ return null;
+ else if (isReferenced(sym))
+ return null;
+ else
+ return prefix;
+ case TypeRef(Type pre, Symbol sym, Type[] args):
+ Type pre1 = cleanPrefix(pre);
+ if (pre1 == null && args.length == 0 && isReferenced(sym))
+ return null;
+ else {
+ pre1 = pre1 == null ? global.definitions.ROOT_TYPE : pre1;
+ return Type.typeRef(pre1, sym, args);
+ }
+ case SingleType(Type pre, Symbol sym):
+ Type pre1 = cleanPrefix(pre);
+ if (pre1 == null) {
+ if (sym.isClass() || sym.isModule())
+ if (isReferenced(sym)) {
+ return null;
+ }
+ else
+ return Type.singleType(global.definitions.ROOT_TYPE, sym);
+ else
+ return Type.singleType(global.definitions.ROOT_TYPE, sym);
+ }
+ else
+ return Type.singleType(pre1, sym);
+ default:
+ return prefix;
+ }
+ }
+
+ /**
+ * Creates the page describing the different frames.
+ *
+ * @param title The page title
+ */
+ protected void createFramePage(String title) {
+ createPrinters(FRAME_PAGE_NAME, title);
+
+ page.printHeader(ATTRS_META, getGenerator());
+ page.printlnOTag("frameset", new XMLAttribute[] {
+ new XMLAttribute("cols", "25%, 75%")}).indent();
+ page.printlnOTag("frameset", new XMLAttribute[] {
+ new XMLAttribute("rows", "50%, 50%")}).indent();
+
+ page.printlnOTag("frame", new XMLAttribute[] {
+ new XMLAttribute("src", PAGE_OVERVIEW_FRAME),
+ new XMLAttribute("name", PACKAGES_FRAME_NAME)});
+ page.printlnOTag("frame", new XMLAttribute[] {
+ new XMLAttribute("src", PAGE_ALLCLASSES_FRAME),
+ new XMLAttribute("name", CLASSES_FRAME_NAME)}).undent();
+ page.printlnCTag("frameset");
+ page.printlnOTag("frame", new XMLAttribute[] {
+ new XMLAttribute("src", PAGE_OVERVIEW_SUMMARY),
+ new XMLAttribute("name", ROOT_FRAME_NAME)});
+
+ page.printlnOTag("noframes").indent();
+ page.printlnSTag("p");
+ page.print("Here is the ");
+ page.printAhref(PAGE_OVERVIEW_SUMMARY, "non-frame based version");
+ page.println(" of the documentation.").undent();
+ page.printlnCTag("noframes").undent();
+
+ page.printlnCTag("frameset");
+ page.printlnCTag("html");
+
+ closePrinters();
+ }
+
+ /**
+ * Generates a resource file.
+ *
+ * @param name The name of the resource file
+ */
+ protected void createResource(String name) {
+ String rsrcName = "resources/" + name;
+ InputStream in = HTMLGenerator.class.getResourceAsStream(rsrcName);
+ if (in == null)
+ throw Debug.abort("Resource file \"" + rsrcName + "\" not found");
+ try {
+ FileOutputStream out = new FileOutputStream(
+ directory.getPath() + File.separator + name);
+
+ byte[] buf = new byte[1024];
+ int len;
+ while (true) {
+ len = in.read(buf, 0, buf.length);
+ if (len <= 0) break;
+ out.write(buf, 0, len);
+ }
+
+ in.close();
+ out.close();
+ } catch (IOException exception) {
+ throw Debug.abort(exception); // !!! reporting an error would be wiser
+ }
+ }
+
+ /**
+ * Writes a table containing a list of packages to the current page.
+ *
+ * @param trees The package list
+ * @param title The title of the package list
+ */
+ private void printPackagesTable(Tree[] trees, String title) {
+ if (trees.length > 0) {
+ page.printlnBold(title);
+ page.printlnOTag("table", ATTRS_LIST).indent();
+ page.printlnOTag("tr").indent();
+ page.printlnOTag("td", new XMLAttribute[] {
+ new XMLAttribute("style", "white-space:nowrap;")}).indent();
+ for (int i = 1; i < trees.length; i++) {
+ Symbol sym = trees[i].symbol();
+ page.printAhref(
+ ref(sym, global.definitions.ROOT, PAGE_PACKAGE_FRAME),
+ CLASSES_FRAME_NAME,
+ sym.fullNameString());
+ page.printlnSTag("br");
+ }
+ page.undent();
+ page.printlnCTag("td").undent();
+ page.printlnCTag("tr").undent();
+ page.printlnCTag("table");
+ page.printlnSTag("p");
+ }
+ }
+
+ /**
+ * Writes a table containing a list of trees to the current page.
+ *
+ * @param trees
+ * @param title
+ */
+ private void addTreeTable(Tree[] trees, String title, boolean useFullName) {
+ if (trees.length > 0) {
+ page.printlnBold(title);
+ page.printlnOTag("table", ATTRS_LIST).indent();
+ page.printlnOTag("tr").indent();
+ page.printlnOTag("td", new XMLAttribute[] {
+ new XMLAttribute("style", "white-space:nowrap;")}).indent();
+ for (int i = 0; i < trees.length; i++) {
+ Symbol sym = trees[i].symbol();
+ if (! sym.isRoot()) {
+ String name = sym.nameString();
+ if (sym.isPackage())
+ page.printAhref(ref(sym, global.definitions.ROOT), CLASSES_FRAME_NAME, name);
+ else {
+ Symbol user = (useFullName) ? global.definitions.ROOT : Symbol.NONE;
+ page.printAhref(ref(sym, user), ROOT_FRAME_NAME, name);
+ }
+ page.printlnSTag("br");
+ }
+ }
+ page.undent();
+ page.printlnCTag("td").undent();
+ page.printlnCTag("tr").undent();
+ page.printlnCTag("table");
+ page.printlnSTag("p");
+ }
+ }
+
+ /**
+ * Creates a page with a list of packages.
+ *
+ * @param title
+ */
+ protected void createPackageIndexPage(String title) {
+ createPrinters(PAGE_OVERVIEW_FRAME, title);
+
+ Tree[] packages = ScalaSearch.getSortedPackageList(tree);
+ for (int i = 1; i < packages.length; i++) {
+ Symbol sym = packages[i].symbol();
+ String path = sym.fullNameString().replace('.', File.separatorChar);
+ File f = new File(directory, path);
+ f.mkdir();
+ }
+
+ page.printHeader(ATTRS_META, getGenerator());
+ page.printOpenBody();
+ addDocumentationTitle(new XMLAttribute[]{
+ new XMLAttribute("class", "doctitle-larger")});
+ page.printAhref(PAGE_ALLCLASSES_FRAME, CLASSES_FRAME_NAME, "All Classes");
+ page.printlnSTag("p");
+ printPackagesTable(packages, "Packages");
+ if (validate)
+ addValidationBar();
+ page.printFootpage();
+
+ closePrinters();
+ }
+
+ /**
+ * Creates a page with a list of classes or objects.
+ *
+ * @param tree
+ */
+ protected void createContainerIndexPage(Tree tree) {
+ Symbol sym = tree.symbol();
+ String title = createPrinters(sym, false/*isSummary*/);
+
+ page.printHeader(ATTRS_META, getGenerator());
+ page.printOpenBody();
+
+ String[] titles = new String[]{ "Objects", "Traits", "Classes" };
+ if (sym.isRoot()) {
+ Tree[][] members = ScalaSearch.getSortedPackageMemberList(tree);
+ for (int i = 0; i < titles.length; i++)
+ addTreeTable(members[i], "All " + titles[i], true);
+ } else {
+ page.printlnAhref(PAGE_PACKAGE_SUMMARY, ROOT_FRAME_NAME, title);
+ page.printlnSTag("p");
+ Tree[][] members = members(tree);
+ for (int i = 0; i < titles.length; i++)
+ addTreeTable(members[i + 2], titles[i], false);
+ }
+
+ if (validate)
+ addValidationBar();
+ page.printFootpage();
+
+ closePrinters();
+ }
+
+ /**
+ * Creates the index page.
+ *
+ * @param title The page title
+ */
+ protected void createIndexPage(String title) {
+ createPrinters(INDEX_PAGE_NAME, title);
+
+ page.printHeader(ATTRS_META, getGenerator());
+ String windowTitle = title + " (" + doctitle.replaceAll("<.*>", " ") + ")";
+ page.printOpenBody(new XMLAttribute[]{
+ new XMLAttribute("onload", "setWindowTitle('" + windowTitle + "');")
+ });
+ addNavigationBar(indexPage);
+ page.printlnHLine();
+
+ page.printlnOTag("table", ATTRS_MEMBER).indent();
+ page.printlnOTag("tr").indent();
+ page.printlnOTag("td", ATTRS_MEMBER_TITLE).indent();
+ page.println("Index").undent();
+ page.printlnCTag("td").undent();
+ page.printlnCTag("tr").undent();
+ page.printlnCTag("table");
+ page.printlnSTag("br");
+
+ Pair index = ScalaSearch.index(tree);
+ Character[] chars = (Character[]) index.fst;
+ Map map = (Map) index.snd;
+ for (int i = 0; i < chars.length; i++)
+ page.printlnAhref("#" + i, HTMLPrinter.encode(chars[i]));
+ page.printlnHLine();
+ for (int i = 0; i < chars.length; i++) {
+ Character car = chars[i];
+ page.printlnAname(String.valueOf(i), "");
+ page.printlnOTag("h2");
+ page.printBold(HTMLPrinter.encode(car));
+ page.printlnCTag("h2");
+ page.printlnOTag("dl").indent();
+ Tree[] trees = (Tree[]) map.get(car);
+ for (int j = 0; j < trees.length; j++) {
+ page.printOTag("dt");
+ addIndexEntry(trees[j].symbol());
+ page.printlnCTag("dt");
+ page.printlnTag("dd", firstSentence(getComment(trees[j].symbol())));
+ }
+ page.undent().printlnCTag("dl");
+ }
+
+ page.printlnHLine();
+ addNavigationBar(indexPage);
+ if (validate)
+ addValidationBar();
+ page.printFootpage();
+
+ closePrinters();
+ }
+
+ /**
+ * Creates the help page.
+ *
+ * @param title The page title
+ */
+ protected void createHelpPage(String title) {
+ createPrinters(PAGE_HELP, title);
+
+ page.printHeader(ATTRS_META, getGenerator());
+ String windowTitle = title + " (" + doctitle.replaceAll("<.*>", " ") + ")";
+ page.printOpenBody(new XMLAttribute[]{
+ new XMLAttribute("onload", "setWindowTitle('" + windowTitle + "');")
+ });
+ addNavigationBar(helpPage);
+ page.printlnHLine();
+
+ XMLAttribute[] h3 = new XMLAttribute[]{
+ new XMLAttribute("style", "margin:15px 0px 0px 0px; "
+ + "font-size:large; font-weight: bold;")
+ };
+ XMLAttribute[] em = new XMLAttribute[]{
+ new XMLAttribute("style", "margin:15px 0px 15px 0px; "
+ + "font-size:small; font-style: italic;")
+ };
+ page.printlnTag("div", ATTRS_PAGE_TITLE, "How This API Document Is Organized");
+ page.println("This API (Application Programming Interface) document "
+ + "has pages corresponding to the items in the navigation bar, "
+ + "described as follows.");
+
+ page.printlnTag("div", h3, "Overview");
+ page.printlnOTag("blockquote").indent();
+ page.print("The ");
+ page.printAhref(PAGE_OVERVIEW_SUMMARY, "Overview");
+ page.println(" page is the front page of this API document and "
+ + "provides a list of all packages with a summary for each. "
+ + "This page can also contain an overall description of the "
+ + "set of packages.").undent();
+ page.printlnCTag("blockquote");
+
+ page.printlnTag("div", h3, "Package");
+ page.printlnOTag("blockquote").indent();
+ page.println("Each package has a page that contains a list of "
+ + "its objects, traits and classes, with a summary for each. "
+ + "This page can contain three categories:");
+ page.printlnOTag("ul").indent();
+ page.printlnTag("li", "Objects");
+ page.printlnTag("li", "Traits");
+ page.printlnTag("li", "Classes").undent();
+ page.printlnCTag("ul").undent();
+ page.printlnCTag("blockquote");
+
+ page.printlnTag("div", h3, "Object/Trait/Class");
+ page.printlnOTag("blockquote").indent();
+ page.println("Each object, trait, class, nested object, nested "
+ + "trait and nested class has its own separate page. Each "
+ + "of these pages has three sections consisting of a object"
+ + "/trait/class description, summary tables, and detailed "
+ + "member descriptions:");
+ page.printlnOTag("ul").indent();
+ page.printlnTag("li", "Class inheritance diagram");
+ page.printlnTag("li", "Direct Subclasses");
+ page.printlnTag("li", "All Known Subinterfaces");
+ page.printlnTag("li", "All Known Implementing Classes");
+ page.printlnTag("li", "Class/interface declaration");
+ page.printlnTag("li", "Class/interface description<p/>");
+ page.printlnTag("li", "Nested Class Summary");
+ page.printlnTag("li", "Field Summary");
+ page.printlnTag("li", "Constructor Summary");
+ page.printlnTag("li", "Method Summary<p/>");
+ page.printlnTag("li", "Field Detail");
+ page.printlnTag("li", "Constructor Detail");
+ page.printlnTag("li", "Method Detail").undent();
+ page.printlnCTag("ul").undent();
+ page.println("Each summary entry contains the first sentence from "
+ + "the detailed description for that item. The summary entries "
+ + "are alphabetical, while the detailed descriptions are in "
+ + "the order they appear in the source code. This preserves "
+ + "the logical groupings established by the programmer.");
+ page.printlnCTag("blockquote");
+
+
+ page.printlnTag("div", h3, "Index");
+ page.printlnOTag("blockquote").indent();
+ page.print("The ");
+ page.printAhref(INDEX_PAGE_NAME, "Index");
+ page.print(" contains an alphabetic list of all classes, interfaces, "
+ + "constructors, methods, and fields.");
+ page.printlnCTag("blockquote");
+
+ page.printlnOTag("div", em);
+ page.println("This help file applies to API documentation generated "
+ + "using the standard doclet.");
+ page.printlnCTag("div");
+
+ page.printlnHLine();
+ addNavigationBar(helpPage);
+ if (validate)
+ addValidationBar();
+ page.printFootpage();
+
+ closePrinters();
+ }
+
+ /**
+ * Writes the string representation of a symbol entry in the index.
+ *
+ * @param symbol
+ */
+ protected void addIndexEntry(Symbol symbol) {
+ // kind
+ String keyword = symtab.getSymbolKeyword(symbol);
+ if (keyword != null) page.print(keyword).space();
+ // name
+ symtab.printDefinedSymbolName(symbol, Symbol.NONE, true);
+ // owner
+ if (!symbol.isRoot()) {
+ page.print(" in ");
+ page.printAhref(ref(symbol.owner(), global.definitions.ROOT), //true),
+ ScalaSearch.getOwnersString(symbol.owner()));
+ }
+ }
+
+ /**
+ * Writes documentation comments to the current page.
+ *
+ * @param comment
+ */
+ protected void addComments(Comment comment) {
+ if (!comment.isEmpty()) {
+ page.printlnOTag("dl").indent();
+ // text with inlined links
+ page.printlnTag("dd", inlineLinkTags(comment.holder, comment.text));
+ page.undent().printlnCTag("dl");
+
+ // tags
+ addTags(comment.tags);
+ }
+ }
+
+ /** Inline all the {@link ...} tags inside the text.
+ */
+ protected String inlineLinkTags(Symbol holder, String text) {
+ StringBuffer buff = new StringBuffer();
+ Tag[] tags = Comment.makeTags(holder, text);
+ for (int i = 0; i < tags.length; i++) {
+ if (tags[i].isText())
+ buff.append(tags[i].text);
+ else if (tags[i].isReference())
+ buff.append(inlineRefTag(tags[i]));
+ }
+ return buff.toString();
+ }
+
+ /**
+ * Returns the first sentence of a documentation comment where all
+ * links {@link ...} have been inlined.
+ *
+ * @param comment
+ */
+ protected String firstSentence(Comment comment) {
+ return
+ inlineLinkTags(comment.holder, comment.firstSentence());
+ }
+
+ // TODO: remove this !
+ private static String ahref(String dest, String target, String text) {
+ return "<a href=\"" + dest + "\" target=\"" + target + "\">" +
+ text + "/a>";
+ }
+
+ /** Inline a @see documentation tag.
+ */
+ protected String inlineRefTag(Tag tag) {
+ switch(Tag.parseReference(tag)) {
+ case Bad(String ref):
+ return ref;
+ case Url(String ref):
+ return ref;
+ case Literal(String ref):
+ return ref;
+ case Scala(String container, String member, String label):
+ Symbol sym = findSymbolFromString(tag.holder, container, member);
+ if (sym == Symbol.NONE) {
+ System.err.println("Warning: Scaladoc: not found: " + tag);
+ return tag.text;
+ }
+ else if (!isReferenced(sym)) {
+ System.err.println("Warning: Scaladoc: not referenced: " + tag);
+ return tag.text;
+ }
+ else {
+ String labl = label.equals("") ? sym.nameString() : label;
+ return ahref(ref(sym), ROOT_FRAME_NAME, labl);
+ }
+ default:
+ throw Debug.abort("illegal case", tag);
+ }
+ }
+
+ /**
+ * Writes a set of Scaladoc tags to a page.
+ *
+ * @param tags
+ */
+ protected void addTags(Tag[] tags) {
+ if (tags.length > 0) {
+ Tag returnTag = null;
+ Tag sinceTag = null;
+ Tag versionTag = null;
+ final List paramTagList = new LinkedList();;
+ final List seeTagList = new LinkedList();
+ final List throwsTagList = new LinkedList();
+ final List authorTagList = new LinkedList();
+ final List otherTagList = new LinkedList();
+
+ // partitioning the tags
+ for (int i = 0; i < tags.length; i++) {
+ if ("@return".equals(tags[i].name))
+ returnTag = tags[i];
+ else if ("@since".equals(tags[i].name))
+ sinceTag = tags[i];
+ else if ("@version".equals(tags[i].name))
+ versionTag = tags[i];
+ else if ("@param".equals(tags[i].name))
+ paramTagList.add(tags[i]);
+ else if ("@see".equals(tags[i].name))
+ seeTagList.add(tags[i]);
+ else if (tags[i].isException())
+ throwsTagList.add(tags[i]);
+ else if ("@author".equals(tags[i].name))
+ authorTagList.add(tags[i]);
+ else
+ otherTagList.add(tags[i]);
+ }
+
+ page.printlnOTag("dl");
+
+ // Author.
+ if (authorTagList.size() > 0) {
+ addTagSection("Author");
+ page.printlnOTag("dd").indent();
+ Iterator it = authorTagList.iterator();
+ Tag authorTag = (Tag) it.next();
+ page.print(authorTag.text);
+ while (it.hasNext()) {
+ authorTag = (Tag) it.next();
+ page.print(", " + authorTag.text);
+ }
+ page.println().undent();
+ page.printlnCTag("dd");
+ }
+ // Since.
+ if (sinceTag != null) {
+ addTagSection("Since");
+ page.printlnTag("dd", sinceTag.text);
+ }
+ // Version.
+ if (versionTag != null) {
+ addTagSection("Version");
+ page.printlnTag("dd", versionTag.text);
+ }
+ // Parameters.
+ if (paramTagList.size() > 0) {
+ addTagSection("Parameters");
+ Iterator it = paramTagList.iterator();
+ Tag paramTag = null;
+ while (it.hasNext()) {
+ paramTag = (Tag) it.next();
+ Pair fields = Tag.split(paramTag.text);
+ String paramName = (String) fields.fst;
+ String paramDesc = (String) fields.snd;
+ page.printOTag("dd");
+ page.printTag("code", paramName);
+ page.println(" - ");
+ page.println(inlineLinkTags(paramTag.holder, paramDesc));
+ page.printlnCTag("dd");
+ }
+ }
+ // Returns.
+ if (returnTag != null) {
+ addTagSection("Returns");
+ page.printlnTag("dd", returnTag.text);
+ }
+ // Throws.
+ if (throwsTagList.size() > 0) {
+ addTagSection("Throws");
+ Iterator it = throwsTagList.iterator();
+ Tag throwsTag = null;
+ while (it.hasNext()) {
+ throwsTag = (Tag) it.next();
+ Pair fields = Tag.split(throwsTag.text);
+ String exceptionName = (String) fields.fst;
+ String exceptionDesc = (String) fields.snd;
+ page.printOTag("dd");
+ page.printTag("code", exceptionName);
+ page.println(" - "); // TODO: hypertext link
+ page.println(inlineLinkTags(throwsTag.holder, exceptionDesc));
+ page.printlnCTag("dd");
+ }
+ }
+ // See Also.
+ if (seeTagList.size() > 0) {
+ addTagSection("See Also");
+ page.printlnOTag("dd");
+ Iterator it = seeTagList.iterator();
+ Tag seetag = (Tag) it.next();
+ page.println(inlineRefTag(seetag));
+ while (it.hasNext()) {
+ seetag = (Tag) it.next();
+ page.print(", ");
+ page.println(inlineRefTag(seetag));
+ }
+ page.printlnCTag("dd");
+ }
+ // Others.
+ if (otherTagList.size() > 0) {
+ Iterator it = otherTagList.iterator();
+ while (it.hasNext())
+ addStandardTag((Tag) it.next());
+ }
+
+ page.printlnCTag("dl");
+ }
+ }
+
+ /**
+ * Returns the HTML representation of a documentation tag.
+ *
+ * @param tagName
+ */
+ protected void addTagSection(String tagName) {
+ page.printOTag("dt", ATTRS_DOCTAG);
+ page.printBold(tagName + ":");
+ page.printlnCTag("dt");
+ }
+
+ /**
+ * Returns the HTML representation of a standard documentation tag.
+ *
+ * @param tag
+ */
+ protected void addStandardTag(Tag tag) {
+ String sectionName = "";
+ if (tag.name.length() > 1) {
+ sectionName += Character.toUpperCase(tag.name.charAt(1));
+ if (tag.name.length() > 2)
+ sectionName += tag.name.substring(2);
+ }
+ addTagSection(sectionName);
+ page.printTag("dd", inlineLinkTags(tag.holder, tag.text));
+ }
+
+ /**
+ * Returns the symbol contained in a specified class or object and
+ * described by its label or its name. Return Symbol.NONE if not
+ * found.
+ *
+ * @param context
+ * @param classOrObject
+ * @param label
+ */
+ protected Symbol findSymbolFromString(Symbol context, String classOrObject, String label) {
+ String path;
+ // absolute path
+ if (classOrObject.startsWith(new Character(ScalaSearch.classChar).toString()) ||
+ classOrObject.startsWith(new Character(ScalaSearch.objectChar).toString()))
+ path = classOrObject;
+ else // relative path
+ path = ScalaSearch.getOwnersString(context.owner()) + classOrObject;
+ path = path.substring(1);
+ Symbol sym = ScalaSearch.lookup(global.definitions.ROOT_CLASS, path);
+ if (sym == Symbol.NONE)
+ return Symbol.NONE;
+ else {
+ if (label == null)
+ return sym;
+ else {
+ // look for a member in the scope that has a tag
+ // @label with the given label
+ Scope scope = sym.moduleClass().members();
+ SymbolIterator it = scope.iterator(true);
+ while (it.hasNext()) {
+ Symbol member = (Symbol) it.next();
+ Tag[] tags = getComment(member).tags;
+ for (int i = 0; i < tags.length; i++)
+ if ("@label".equals(tags[i].name) &&
+ label.equals(tags[i].text))
+ return member;
+ }
+ // look for the first term with label as name
+ return sym.moduleClass().lookup(Name.fromString(label).toTermName());
+ }
+ }
+ }
+
+ //########################################################################
+}
diff --git a/sources/scala/tools/scaladoc/HTMLGeneratorCommand.java b/sources/scala/tools/scaladoc/HTMLGeneratorCommand.java
new file mode 100644
index 0000000000..2ea6a03a1a
--- /dev/null
+++ b/sources/scala/tools/scaladoc/HTMLGeneratorCommand.java
@@ -0,0 +1,166 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+\* */
+
+// $Id$
+
+package scala.tools.scaladoc;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import ch.epfl.lamp.util.HTMLPrinter;
+import ch.epfl.lamp.util.HTMLRepresentation;
+
+import scalac.CompilerCommand;
+import scalac.util.BooleanOptionParser;
+import scalac.util.ClassPath;
+import scalac.util.OptionParser;
+import scalac.util.Reporter;
+import scalac.util.StringOptionParser;
+import scalac.util.Strings;
+
+/**
+ * The class <code>HTMLGeneratorCommand</code> describes the options
+ * passed as arguments to the HTML generator command.
+ */
+public class HTMLGeneratorCommand extends CompilerCommand {
+
+ //########################################################################
+ // Public Fields
+
+ public final StringOptionParser docencoding;
+ public final StringOptionParser docmodule;
+ public final StringOptionParser docmodulePath;
+ public final StringOptionParser doctitle;
+ public final StringOptionParser doctype;
+ public final StringOptionParser stylesheet;
+ public final StringOptionParser windowtitle;
+
+ public final BooleanOptionParser noindex;
+ public final BooleanOptionParser validate;
+
+ //########################################################################
+ // Public Constructors
+
+ /**
+ * Creates an instance variable for an HTML generator command.
+ *
+ * @param product
+ * @param version
+ * @param reporter
+ * @param phases
+ */
+ public HTMLGeneratorCommand(String product, String version,
+ Reporter reporter, HTMLGeneratorPhases phases)
+ {
+ this(product, version, "<source files>", reporter, phases);
+ }
+
+ /**
+ * Creates an instance variable for an HTML generator command.
+ *
+ * @param product
+ * @param version
+ * @param syntax
+ * @param reporter
+ * @param phases
+ */
+ public HTMLGeneratorCommand(String product, String version, String syntax,
+ Reporter reporter, HTMLGeneratorPhases phases)
+ {
+ super(product, version, syntax, reporter, phases);
+
+ this.docencoding = new StringOptionParser(this,
+ "docencoding",
+ "Output encoding name (" + HTMLRepresentation.DEFAULT_DOCENCODING + ", etc.)",
+ "name",
+ HTMLRepresentation.DEFAULT_DOCENCODING);
+
+ this.docmodule = new StringOptionParser(this,
+ "docmodule",
+ "Specify module used by scaladoc",
+ "class",
+ "scala.tools.scaladoc.StandardDocModule");
+
+ this.docmodulePath = new StringOptionParser(this,
+ "docmodulepath",
+ "Specify where to find doc module class files",
+ "path",
+ ClassPath.CLASS_PATH);
+
+ this.doctitle = new StringOptionParser(this,
+ "doctitle",
+ "Include title for the overview page",
+ "html-code",
+ HTMLGenerator.DEFAULT_DOCTITLE);
+
+ this.doctype = new StringOptionParser(this,
+ "doctype",
+ "Output type name (" + HTMLRepresentation.DEFAULT_DOCTYPE + ", etc.)",
+ "name",
+ HTMLRepresentation.DEFAULT_DOCTYPE);
+
+ this.stylesheet = new StringOptionParser(this,
+ "stylesheetfile",
+ "File to change style of the generated documentation",
+ "path",
+ HTMLPrinter.DEFAULT_STYLESHEET);
+
+ this.windowtitle = new StringOptionParser(this,
+ "windowtitle",
+ "Browser window title for the documentation",
+ "text",
+ HTMLGenerator.DEFAULT_WINDOWTITLE);
+
+ this.noindex = new BooleanOptionParser(this,
+ "noindex",
+ "Do not generate index",
+ false);
+
+ this.validate = new BooleanOptionParser(this,
+ "validate",
+ "Add a link at the bottom of each generated page",
+ false);
+
+ remove(nowarn);
+ remove(verbose);
+ remove(debug);
+ remove(explaintypes);
+ remove(uniqid);
+ remove(types);
+ remove(prompt);
+ remove(separate);
+ remove(classpath);
+ remove(sourcepath);
+ remove(bootclasspath);
+ remove(extdirs);
+ // outpath;
+ remove(target);
+ remove(noimports);
+ remove(nopredefs);
+ remove(skip);
+ remove(check);
+ remove(print);
+ remove(printer);
+ remove(printfile);
+ remove(graph);
+ remove(stop);
+ remove(log);
+
+ // similar order as javadoc options
+ add(2, windowtitle);
+ add(3, doctitle);
+ add(4, noindex);
+ add(5, docmodule);
+ add(6, docmodulePath);
+ add(7, stylesheet);
+ add(8, doctype);
+ add(9, docencoding);
+ add(10, validate);
+ }
+
+ //########################################################################
+}
diff --git a/sources/scala/tools/scaladoc/HTMLGeneratorPhase.java b/sources/scala/tools/scaladoc/HTMLGeneratorPhase.java
new file mode 100644
index 0000000000..e3540cb235
--- /dev/null
+++ b/sources/scala/tools/scaladoc/HTMLGeneratorPhase.java
@@ -0,0 +1,50 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+\* */
+
+// $Id$
+
+package scala.tools.scaladoc;
+
+import ch.epfl.lamp.util.Position;
+
+import scalac.Global;
+import scalac.Phase;
+import scalac.PhaseDescriptor;
+import scalac.Unit;
+
+/**
+ * This class implements a phase that generates the HTML documentation
+ * for Scala source code.
+ */
+public class HTMLGeneratorPhase extends Phase {
+
+ //########################################################################
+ // Public Constructors
+
+ /**
+ * Initializes this instance.
+ *
+ * @param global
+ * @param descriptor
+ */
+ public HTMLGeneratorPhase(Global global, PhaseDescriptor descriptor) {
+ super(global, descriptor);
+ }
+
+ //########################################################################
+ // Public Methods
+
+ /**
+ * Applies this phase to the given compilation units.
+ *
+ * @param units
+ */
+ public void apply(Unit[] units) {
+ DocModule.apply(global);
+ }
+
+ //########################################################################
+}
diff --git a/sources/scala/tools/scaladoc/HTMLGeneratorPhases.java b/sources/scala/tools/scaladoc/HTMLGeneratorPhases.java
new file mode 100644
index 0000000000..7d97bd9593
--- /dev/null
+++ b/sources/scala/tools/scaladoc/HTMLGeneratorPhases.java
@@ -0,0 +1,49 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+\* */
+
+// $Id$
+
+package scala.tools.scaladoc;
+
+import scalac.CompilerPhases;
+import scalac.PhaseDescriptor;
+
+/**
+ * The class <code>HTMLGeneratorPhases</code> defines the set
+ * of processing phases belonging the the HTML generator tool.
+ */
+public class HTMLGeneratorPhases extends CompilerPhases {
+
+ //########################################################################
+ // Public Fields
+
+ /**
+ * The phase descriptor of the HTML generator.
+ */
+ public final PhaseDescriptor HTMLGENERATOR;
+
+ //########################################################################
+ // Public Constructors
+
+ /**
+ * Creates an instance variable.
+ */
+ public HTMLGeneratorPhases() {
+ this.HTMLGENERATOR = new PhaseDescriptor(
+ "html generator",
+ "generate html documentation",
+ "generating html",
+ scala.tools.scaladoc.HTMLGeneratorPhase.class);
+
+ int pos = insertAfter(HTMLGENERATOR, ANALYZER);
+ PhaseDescriptor[] array = phases();
+ // we skip all phases between HTMLGENERATOR and TERMINAL.
+ for (int i = pos + 1; i < array.length - 1; i++)
+ array[i].addSkipFlag();
+ }
+
+ //########################################################################
+}
diff --git a/sources/scala/tools/scaladoc/Main.java b/sources/scala/tools/scaladoc/Main.java
new file mode 100644
index 0000000000..163a187c07
--- /dev/null
+++ b/sources/scala/tools/scaladoc/Main.java
@@ -0,0 +1,48 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+\* */
+
+// $Id$
+
+package scala.tools.scaladoc;
+
+import scalac.Global;
+import scalac.util.Reporter;
+
+/**
+ * The main class for scaladoc, an HTML documentation generator
+ * for the programming language Scala.
+ *
+ * @author Vincent Cremet, Stephane Micheloud
+ * @version 1.0
+ */
+public class Main {
+
+ //########################################################################
+ // Public Constants
+
+ public static final String PRODUCT =
+ System.getProperty("scala.product", "scaladoc");
+ public static final String VERSION =
+ System.getProperty("scala.version", "1.0");
+
+ //########################################################################
+ // Public Methods
+
+ public static void main(String[] args) {
+ Reporter reporter = new Reporter();
+ HTMLGeneratorCommand command = new HTMLGeneratorCommand(
+ PRODUCT, VERSION, reporter, new HTMLGeneratorPhases());
+ if (command.parse(args) && command.files.list.size() > 0) {
+ Global global = new Global(command);
+ global.compile(command.files.toArray(), false);
+ global.stop("total");
+ global.reporter.printSummary();
+ }
+ System.exit((reporter.errors() > 0) ? 1 : 0);
+ }
+
+ //########################################################################
+}
diff --git a/sources/scala/tools/scaladoc/OneTree.java b/sources/scala/tools/scaladoc/OneTree.java
new file mode 100644
index 0000000000..980b190121
--- /dev/null
+++ b/sources/scala/tools/scaladoc/OneTree.java
@@ -0,0 +1,245 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+\* */
+
+// $Id$
+
+package scala.tools.scaladoc;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import ch.epfl.lamp.util.Position;
+
+import scalac.Global;
+import scalac.ast.Tree;
+import scalac.ast.Tree.*;
+import scalac.symtab.Kinds;
+import scalac.symtab.Modifiers;
+import scalac.symtab.Symbol;
+import scalac.symtab.TermSymbol;
+import scalac.util.*;
+
+/**
+ * This class is used to merge several compilation units into one
+ * single tree by creating nodes for java packages. It addionally
+ * removes any node which is not a declaration.
+ */
+public class OneTree {
+
+ /**
+ * Global compiler environment.
+ */
+ protected Global global;
+
+ /**
+ * Root node of the unique tree.
+ */
+ protected Node.PackageNode rootNode;
+
+ /**
+ * ..
+ */
+ OneTree(Global global) {
+ super();
+ this.global = global;
+ rootNode = Node.PackageNode(global.definitions.ROOT, new LinkedList());
+ }
+
+ /**
+ * Main function.
+ */
+ public static Tree apply(Global global) {
+ return new OneTree(global).enterNodes();
+ }
+
+ /**
+ * Returns the symbol in the unique tree corresponding to the given symbol.
+ *
+ * @param sym
+ */
+ public static Symbol symbol(Symbol sym) {
+ if (sym.isRoot())
+ return sym.moduleClass();
+ else if (sym.isPackage() && (sym.kind == Kinds.CLASS))
+ return sym.module();
+ else if (sym.isModuleClass())
+ return sym.module();
+ else return sym;
+ }
+
+
+ /**
+ * Enters all the nodes in the unique tree.
+ */
+ protected Tree enterNodes() {
+ for (int i = 0; i < global.units.length; i++)
+ enter(global.units[i].body);
+ return rootNode.toTree(global);
+ }
+
+ /**
+ * Transforms a Java package symbol into a tree definition.
+ *
+ * @param sym
+ * @param body
+ * @param global
+ */
+ protected static Tree packageSymbolTree(Symbol sym, Tree[] body, Global global) {
+ if (sym.isRoot())
+ return global.treeGen.ClassDef(sym.moduleClass(), Tree.EMPTY_ARRAY, Symbol.NONE, body);
+ else {
+ Template t = global.make.Template(Position.NOPOS,
+ Tree.EMPTY_ARRAY,
+ body);
+ return global.make.ModuleDef(Position.NOPOS, sym.module(), Tree.Empty, t);
+ }
+ }
+
+ /**
+ * Intermediate datastructure representing the unique tree.
+ */
+ protected static class Node {
+ case PackageNode(Symbol sym, List decls); // package
+ case TreeNode(Tree tree); // tree (class or object)
+
+ /** Lookups for a child node in a package node. */
+ Node.PackageNode lookup(Symbol sym) {
+ Iterator it = ((Node.PackageNode) this).decls.iterator();
+ while (it.hasNext()) {
+ Node node = (Node) it.next();
+ switch(node) {
+ case PackageNode(Symbol sym1, List decls1):
+ if (sym == sym1)
+ return (Node.PackageNode) node;
+ break;
+ }
+ }
+ return null;
+ }
+
+ /** Adds a child node to a package node.
+ */
+ void add(Node node) {
+ ((Node.PackageNode) this).decls.add(node);
+ }
+
+ /** Transforms a node into a tree.
+ */
+ static Tree[] nodeToTree(Node[] nodes, Global global) {
+ Tree[] trees = new Tree[nodes.length];
+ for(int i = 0; i < nodes.length; i++)
+ trees[i] = nodes[i].toTree(global);
+ return trees;
+ }
+ Tree toTree(Global global) {
+ Tree res = null;
+ switch(this) {
+ case TreeNode(Tree tree):
+ res = tree;
+ break;
+ case PackageNode(Symbol sym, List decls):
+ res = packageSymbolTree(sym,
+ nodeToTree((Node[]) decls.toArray(new Node[decls.size()]),
+ global),
+ global);
+ break;
+ }
+ return res;
+ }
+ }
+
+ /**
+ * Returns the node associated with this symbol, creates it if it
+ * does not exist.
+ *
+ * @param sym
+ */
+ protected Node.PackageNode getNode(Symbol sym) {
+ if (sym.isRoot())
+ return rootNode;
+ else {
+ Node.PackageNode parent = getNode(sym.owner());
+ Node.PackageNode thisnode = parent.lookup(sym);
+ if (thisnode == null) {
+ thisnode = Node.PackageNode(sym, new LinkedList());
+ parent.add(thisnode);
+ }
+ return thisnode;
+ }
+ }
+
+ /**
+ * Enter a global declaration in the unique tree.
+ *
+ * @param body
+ */
+ protected void enter(Tree[] body) {
+ for(int i = 0; i< body.length; i++)
+ enter(body[i]);
+ }
+ protected void enter(Tree tree) {
+ if (tree instanceof PackageDef)
+ enter(((PackageDef) tree).impl.body);
+ else if ((tree instanceof ClassDef || tree instanceof ModuleDef) &&
+ tree.symbol().owner().isPackage())
+ //getNode(tree.symbol().owner()).add(Node.TreeNode(tree));
+ getNode(tree.symbol().owner()).add(Node.TreeNode(keepDeclarations(tree, new LinkedList())));
+ }
+
+ /** Do we keep this symbol ?
+ */
+ protected boolean keep(Tree tree) {
+ return tree.hasSymbol() &&
+ !tree.symbol().isPrivate() &&
+ !ScalaSearch.isGenerated(tree.symbol());
+ }
+
+ /**
+ * Removes nodes which are not declarations in a tree.
+ *
+ * @param tree
+ * @param ownerDeclList
+ */
+ protected Tree keepDeclarations(Tree tree, List ownerDeclList) {
+ if (!keep(tree) && !(tree instanceof Template))
+ return null;
+ switch (tree) {
+ case ClassDef(int mods, Name name, AbsTypeDef[] tparams, ValDef[][] vparams, Tree tpe, Template impl):
+ List declList = new LinkedList();
+ Template impl1 = (Template) keepDeclarations(impl, declList);
+ //Tree tree1 = global.make.ClassDef(tree.pos, mods, name, tparams, vparams, tpe, impl1);
+ Tree tree1 = global.treeGen.ClassDef(tree.symbol(), impl1.parents, TermSymbol.newLocalDummy(tree.symbol()), impl1.body);
+ ownerDeclList.add(tree1);
+ return tree1;
+ case ModuleDef(int mods, Name name, Tree tpe, Template impl):
+ List declList = new LinkedList();
+ Template impl1 = (Template) keepDeclarations(impl, declList);
+ assert impl1 != null: "Null impl";
+ Tree tree1 = global.make.ModuleDef(tree.pos, tree.symbol(), tpe, impl1);
+ ownerDeclList.add(tree1);
+ return tree1;
+ case ValDef(int mods, Name name, Tree tpe, Tree rhs):
+ ownerDeclList.add(tree);
+ return tree;
+ case DefDef(int mods, Name name, AbsTypeDef[] tparams, ValDef[][] vparams, Tree tpe, Tree rhs):
+ ownerDeclList.add(tree);
+ return tree;
+ case AbsTypeDef(_, _, _, _):
+ case AliasTypeDef(_, _, _, _):
+ ownerDeclList.add(tree);
+ return tree;
+ case Template(Tree[] parents, Tree[] body):
+ for(int i = 0; i < body.length; i++)
+ keepDeclarations(body[i], ownerDeclList);
+ Tree[] ownerDecls = (Tree[]) ownerDeclList.toArray(new Tree[ownerDeclList.size()]);
+ return global.make.Template(tree.pos, parents, ownerDecls);
+ default:
+ return tree;
+ }
+ }
+
+}
diff --git a/sources/scala/tools/scaladoc/ScalaSearch.java b/sources/scala/tools/scaladoc/ScalaSearch.java
new file mode 100644
index 0000000000..cac508002a
--- /dev/null
+++ b/sources/scala/tools/scaladoc/ScalaSearch.java
@@ -0,0 +1,508 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+\* */
+
+// $Id$
+
+package scala.tools.scaladoc;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import ch.epfl.lamp.util.Pair;
+
+import scalac.Global;
+import scalac.ast.Tree;
+import scalac.ast.Tree.*;
+import scalac.symtab.Scope;
+import scalac.symtab.Scope.SymbolIterator;
+import scalac.symtab.Symbol;
+import scalac.symtab.Type;
+import scalac.util.Debug;
+import scalac.util.Name;
+import scalac.util.NameTransformer;
+
+/**
+ * This class contains functions to retrieve information from a Scala
+ * library.
+ */
+public class ScalaSearch {
+
+ /**
+ * Character which ends a name to indicates it is a class symbol
+ * name.
+ */
+ public static final char classChar = ',';
+
+ /**
+ * Character which ends a name to indicates it is an object symbol
+ * name.
+ */
+ public static final char objectChar = '.';
+
+ /**
+ * Returns the list of owners of a symbol (including itself).
+ *
+ * @param sym
+ */
+ public static Symbol[] getOwners(Symbol sym) {
+ List l = getOwnersAux(sym);
+ return (Symbol[]) l.toArray(new Symbol[l.size()]);
+ }
+
+ // where
+ private static List getOwnersAux(Symbol sym) {
+ if (sym == Symbol.NONE)
+ return new LinkedList();
+ else {
+ List ownerStaticPath = getOwnersAux(sym.owner());
+ sym = sym.isModuleClass() ? sym.module() : sym;
+ ownerStaticPath.add(sym);
+ return ownerStaticPath;
+ }
+ }
+
+ /**
+ * Gives a string representation of this symbol.
+ *
+ * @param sym
+ * @param showOwners
+ */
+ public static String getSymbolName(Symbol sym, boolean showOwners) {
+ return (showOwners) ? getOwnersString(sym) : sym.nameString();
+ }
+
+ /**
+ * Returns a string representation of the path leading to this
+ * symbol.
+ *
+ * @param sym
+ * @return the string representation of this symbol
+ */
+ public static String getOwnersString(Symbol sym) {
+ Symbol[] elements = getOwners(sym);
+ StringBuffer buff = new StringBuffer();
+ // we ignore elements[0] which contains the root symbol
+ for (int i = 1; i < elements.length; i++) {
+ if (i > 1) buff.append(objectChar);
+ buff.append(elements[i].nameString());
+ }
+ return buff.toString();
+ }
+
+ /**
+ * Class representing functions on trees.
+ */
+ public static abstract class TreeFun {
+ abstract void apply(Tree tree);
+ }
+
+ /**
+ * Apply a function to all definition nodes in a tree.
+ *
+ * @param trees
+ * @param fun
+ */
+ public static void foreach(Tree[] trees, TreeFun fun) {
+ for (int i = 0; i < trees.length; i++)
+ foreach(trees[i], fun);
+ }
+
+ /**
+ * ..
+ *
+ * @param trees
+ * @param fun
+ */
+ public static void foreach(Tree tree, TreeFun fun) {
+ switch(tree) {
+ case ClassDef(int mods, Name name, AbsTypeDef[] tparams, ValDef[][] vparams, Tree tpe, Template impl):
+ fun.apply(tree);
+ foreach(impl, fun);
+ break;
+ case ModuleDef(int mods, Name name, Tree tpe, Template impl):
+ fun.apply(tree);
+ foreach(impl, fun);
+ break;
+ case ValDef(int mods, Name name, Tree tpe, Tree rhs):
+ fun.apply(tree);
+ break;
+ case DefDef(int mods, Name name, AbsTypeDef[] tparams, ValDef[][] vparams, Tree tpe, Tree rhs):
+ fun.apply(tree);
+ break;
+ case AbsTypeDef(_, _, _, _):
+ case AliasTypeDef(_, _, _, _):
+ fun.apply(tree);
+ break;
+ case Template(Tree[] parents, Tree[] body):
+ foreach(body, fun);
+ break;
+ default:
+ }
+ }
+
+ /**
+ * Use the simple name of symbols to order them.
+ */
+ public static Comparator symAlphaOrder = new Comparator() {
+ public int compare(Object o1, Object o2) {
+ Symbol symbol1 = (Symbol) o1;
+ Symbol symbol2 = (Symbol) o2;
+ String name1 = symbol1.nameString();
+ String name2 = symbol2.nameString();
+ return name1.compareTo(name2);
+ }
+ public boolean equals(Object o) {
+ return false;
+ }
+ };
+
+ /**
+ * Use the fully qualified name of symbols to order them.
+ */
+ public static Comparator symPathOrder = new Comparator() {
+ public int compare(Object o1, Object o2) {
+ Symbol symbol1 = (Symbol) o1;
+ Symbol symbol2 = (Symbol) o2;
+ String name1 = symbol1.fullName().toString();
+ String name2 = symbol2.fullName().toString();
+ return name1.compareTo(name2);
+ }
+ public boolean equals(Object o) {
+ return false;
+ }
+ };
+
+ /**
+ * Use the simple name of trees to order them.
+ */
+ public static Comparator alphaOrder = new Comparator() {
+ public int compare(Object o1, Object o2) {
+ Tree tree1 = (Tree) o1;
+ Tree tree2 = (Tree) o2;
+ assert tree1.hasSymbol() : tree1;
+ assert tree2.hasSymbol() : tree2;
+ String name1 = NameTransformer.decode(tree1.symbol().name).toString();
+ String name2 = NameTransformer.decode(tree2.symbol().name).toString();
+ return name1.compareTo(name2);
+ }
+ public boolean equals(Object o) {
+ return false;
+ }
+ };
+
+ /**
+ * Use the fully qualified name of trees to order them.
+ */
+ public static Comparator fullPathOrder = new Comparator() {
+ public int compare(Object o1, Object o2) {
+ Tree tree1 = (Tree) o1;
+ Tree tree2 = (Tree) o2;
+ assert tree1.hasSymbol();
+ assert tree2.hasSymbol();
+ String name1 = tree1.symbol().fullName().toString();
+ String name2 = tree2.symbol().fullName().toString();
+ return name1.compareTo(name2);
+ }
+ public boolean equals(Object o) {
+ return false;
+ }
+ };
+
+ /**
+ * Use the non qualified name of trees to order them.
+ */
+ public static Comparator pathOrder = new Comparator() {
+ public int compare(Object o1, Object o2) {
+ Tree tree1 = (Tree) o1;
+ Tree tree2 = (Tree) o2;
+ assert tree1.hasSymbol();
+ assert tree2.hasSymbol();
+ String name1 = tree1.symbol().nameString();
+ String name2 = tree2.symbol().nameString();
+ return name1.compareTo(name2);
+ }
+ public boolean equals(Object o) {
+ return false;
+ }
+ };
+
+ /**
+ * Returns the sorted list of packages in the root tree.
+ *
+ * @param root
+ */
+ public static Tree[] getSortedPackageList(Tree root) {
+ final List packagesAcc = new LinkedList();
+ foreach(root,
+ new TreeFun() {
+ void apply(Tree tree) {
+ if (tree.hasSymbol() && tree.symbol().isPackage())
+ packagesAcc.add(tree);
+ }
+ });
+ Tree[] packages = (Tree[]) packagesAcc.toArray(new Tree[packagesAcc.size()]);
+ Arrays.sort(packages, fullPathOrder);
+ return packages;
+ }
+
+ /**
+ * Returns a pair consisting of the sorted list of classes
+ * and the sorted list of objects in the root tree.
+ *
+ * @param root
+ */
+ public static Tree[][] getSortedPackageMemberList(Tree root) {
+ final List objectsAcc = new LinkedList();
+ final List traitsAcc = new LinkedList();
+ final List classesAcc = new LinkedList();
+ foreach(root,
+ new TreeFun() {
+ void apply(Tree tree) {
+ if (tree.hasSymbol()) {
+ Symbol sym = tree.symbol();
+ if (sym.owner().isPackage()) {
+ if (sym.isTrait())
+ traitsAcc.add(tree);
+ else if (sym.isClass())
+ classesAcc.add(tree);
+ else if (sym.isModule() && !sym.isPackage())
+ objectsAcc.add(tree);
+ }
+ }
+ }
+ });
+ Tree[] objects = (Tree[]) objectsAcc.toArray(new Tree[objectsAcc.size()]);
+ Tree[] traits = (Tree[]) traitsAcc.toArray(new Tree[traitsAcc.size()]);
+ Tree[] classes = (Tree[]) classesAcc.toArray(new Tree[classesAcc.size()]);
+ Arrays.sort(objects, pathOrder);
+ Arrays.sort(traits, pathOrder);
+ Arrays.sort(classes, pathOrder);
+ return new Tree[][]{ objects, traits, classes };
+ }
+
+ /**
+ * Returns a pair consisting of the sorted list of classes
+ * and the sorted list of objects in the root tree.
+ *
+ * @param root
+ */
+ public static Pair containersSortedList(Tree root) {
+ final List classesAcc = new LinkedList();
+ final List objectsAcc = new LinkedList();
+ foreach(root,
+ new TreeFun() {
+ void apply(Tree tree) {
+ if (tree.hasSymbol()) {
+ Symbol sym = tree.symbol();
+ if (sym.isClass())
+ classesAcc.add(tree);
+ else if (sym.isModule() && !sym.isPackage())
+ objectsAcc.add(tree);
+ }
+ }
+ });
+ Tree[] classes = (Tree[]) classesAcc.toArray(new Tree[classesAcc.size()]);
+ Tree[] objects = (Tree[]) objectsAcc.toArray(new Tree[objectsAcc.size()]);
+ Arrays.sort(classes, pathOrder);
+ Arrays.sort(objects, pathOrder);
+ return new Pair(classes, objects);
+ }
+
+ /**
+ * Returns a hashtable which maps each class symbol to the list of
+ * its direct sub-classes or sub-modules. We also keep track of
+ * the exact involved type.
+ * Result type = Map<Symbol, List<Pair<Symbol, Type>>
+ *
+ * @param root
+ */
+ public static Map subTemplates(Tree root) {
+ final Map subs = new HashMap();
+ foreach(root, new TreeFun() { void apply(Tree tree) {
+ if (tree.hasSymbol()) {
+ Symbol sym = tree.symbol();
+ if (sym.isClass() || sym.isModule()) {
+ Type[] parents = sym.moduleClass().parents();
+ for (int i = 0; i < parents.length; i++) {
+ Symbol parentSymbol = parents[i].symbol();
+ List subList = (List) subs.get(parentSymbol);
+ if (subList == null) {
+ subList = new LinkedList();
+ subs.put(parentSymbol, subList);
+ }
+ subList.add(new Pair(sym, parents[i]));
+ }
+ }
+ }
+ }});
+ return subs;
+ }
+
+ /**
+ * Returns the list of characters with the sorted list of
+ * members starting with a character.
+ * Result type = Pair<Character[], Map<Character, Tree[]>>
+ *
+ * @param root
+ */
+ public static Pair index(Tree root) {
+ final Map index = new HashMap();
+ // collecting
+ foreach(root, new TreeFun() { void apply(Tree tree) {
+ if (tree.hasSymbol() /*&& !tree.symbol().isGenerated()*/) {
+ Symbol sym = tree.symbol();
+ String name = NameTransformer.decode(sym.name).toString();
+ if (name.length() > 0) {
+ char ch = Character.toUpperCase(name.charAt(0));
+ Character unicode = new Character(ch);
+ List symList = (List) index.get(unicode);
+ if (symList == null) {
+ symList = new LinkedList();
+ index.put(unicode, symList);
+ }
+ symList.add(tree);
+ }
+ }
+ }});
+ // sorting
+ Character[] chars = (Character[]) index.keySet()
+ .toArray(new Character[index.keySet().size()]);
+ Arrays.sort(chars);
+ for (int i = 0; i < chars.length; i++) {
+ Character car = chars[i];
+ List treeList = (List) index.get(car);
+ Tree[] trees = (Tree[]) treeList.toArray(new Tree[treeList.size()]);
+ Arrays.sort(trees, alphaOrder);
+ index.put(car, trees);
+ }
+ return new Pair(chars, index);
+ }
+
+ /**
+ * "Try" to determine if a symbol has been generated by the
+ * compiler.
+ *
+ * @param sym
+ */
+ public static boolean isGenerated(Symbol sym) {
+ return
+ (sym.isSynthetic() && !sym.isRoot()) ||
+ (sym.isGenerated() &&
+ NameTransformer.decode(sym.name).toString().equals(sym.name.toString())) ||
+ NameTransformer.decode(sym.name).toString().endsWith("_=");
+ }
+
+ /**
+ * Finds all local and inherited members of a given class or
+ * object.
+ *
+ * @param sym
+ */
+ public static Symbol[] findMembers(Symbol sym) {
+ Type thistype = sym.thisType();
+ Name[] names = potentialMemberNames(thistype); // potentialMembers
+ List/*<Symbol>*/ members = new LinkedList(); // actual members
+ for (int i = 0; i < names.length; i++) {
+ Symbol member = thistype.lookup(names[i]);
+ if (member != Symbol.NONE)
+ if (!member.isConstructor())
+ members.add(member);
+ }
+ List unloadedMembers = new LinkedList();
+ Iterator it = members.iterator();
+ while (it.hasNext()) {
+ Symbol[] alts = ((Symbol) it.next()).alternativeSymbols();
+ for (int i = 0; i < alts.length; i++)
+ unloadedMembers.add(alts[i]);
+ }
+ return (Symbol[]) unloadedMembers.toArray(new Symbol[unloadedMembers.size()]);
+ }
+
+ // where
+ protected static Name[] potentialMemberNames(Type tpe) {
+ List names = new LinkedList();
+ potentialMemberNames(tpe, names);
+ return (Name[]) names.toArray(new Name[names.size()]);
+ }
+
+ // where
+ protected static void potentialMemberNames(Type tpe, List/*<Name>*/ names) {
+ // local members
+ Scope.SymbolIterator it = tpe.members().iterator();
+ while (it.hasNext()) {
+ Name name = ((Symbol) it.next()).name;
+ if (!names.contains(name))
+ names.add(name);
+ }
+ // inherited members
+ Type[] parents = tpe.parents();
+ for (int i = 0; i < parents.length; i++)
+ potentialMemberNames(parents[i], names);
+ }
+
+ /**
+ * Groups symbols with respect to their owner and sort the owners
+ * by name.
+ *
+ * @param syms
+ */
+ public static Pair/*<Symbol[], Map<Symbol, Symbol[]>>*/ groupSymbols(Symbol[] syms) {
+ Map/*<Symbol, List>*/ groups = new HashMap();
+ for (int i = 0; i < syms.length; i++) {
+ List group = (List) groups.get(syms[i].owner());
+ if (group == null) {
+ group = new LinkedList();
+ groups.put(syms[i].owner(), group);
+ }
+ group.add(syms[i]);
+ }
+ Symbol[] owners = (Symbol[]) groups.keySet().toArray(new Symbol[groups.keySet().size()]);
+ Arrays.sort(owners, symPathOrder);
+ for (int i = 0; i < owners.length; i++) {
+ List groupList = (List) groups.get(owners[i]);
+ Symbol[] group = (Symbol[]) groupList.toArray(new Symbol[groupList.size()]);
+ Arrays.sort(group, symAlphaOrder);
+ groups.put(owners[i], group);
+ }
+ return new Pair(owners, groups);
+ }
+
+ // where
+ public static Symbol lookup(Symbol root, String path) {
+ if (path.equals(""))
+ return root;
+ else {
+ int classEnd = path.indexOf(classChar);
+ int objectEnd = path.indexOf(objectChar);
+ if (classEnd > 0 && (objectEnd == -1 || objectEnd > classEnd)) {
+ String name = path.substring(0, classEnd);
+ String rest = path.substring(classEnd + 1);
+ Symbol member = root.moduleClass().lookup(Name.fromString(name).toTypeName());
+ if (member.isClass())
+ return lookup(member, rest);
+ else
+ return Symbol.NONE;
+ }
+ else if (objectEnd > 0 && (classEnd == -1 || classEnd > objectEnd)) {
+ String name = path.substring(0, objectEnd);
+ String rest = path.substring(objectEnd + 1);
+ Symbol member = root.moduleClass().lookup(Name.fromString(name).toTermName());
+ if (member.isModule())
+ return lookup(member, rest);
+ else
+ return Symbol.NONE;
+ }
+ else
+ throw Debug.abort("illegal path", path);
+ }
+ }
+
+}
diff --git a/sources/scala/tools/scaladoc/StandardDocModule.java b/sources/scala/tools/scaladoc/StandardDocModule.java
new file mode 100644
index 0000000000..fa99ff8847
--- /dev/null
+++ b/sources/scala/tools/scaladoc/StandardDocModule.java
@@ -0,0 +1,56 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+\* */
+
+// $Id$
+
+package scala.tools.scaladoc;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import scalac.Global;
+import scalac.Unit;
+import scalac.ast.Tree;
+import scalac.symtab.Symbol;
+import scalac.util.Debug;
+
+/**
+ * Phase which generates HTML documentation.
+ */
+public class StandardDocModule {
+
+ private final Global global;
+
+ /**
+ * Constructor
+ *
+ * @param globlal
+ */
+ public StandardDocModule(Global global) {
+ super();
+ this.global = global;
+ }
+
+ /**
+ * ..
+ */
+ public void apply() {
+ Tree tree = OneTree.apply(global);
+ HTMLGenerator.apply(tree, global);
+ }
+
+ /**
+ * ..
+ *
+ * @param global
+ */
+ public static void apply(Global global) {
+ new StandardDocModule(global).apply();
+ }
+
+}
diff --git a/sources/scala/tools/scaladoc/SymbolTablePrinter.java b/sources/scala/tools/scaladoc/SymbolTablePrinter.java
new file mode 100644
index 0000000000..b7c7621148
--- /dev/null
+++ b/sources/scala/tools/scaladoc/SymbolTablePrinter.java
@@ -0,0 +1,607 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+\* */
+
+// $Id$
+
+package scala.tools.scaladoc;
+
+import java.io.StringWriter;
+
+import ch.epfl.lamp.util.CodePrinter;
+import ch.epfl.lamp.util.HTMLPrinter;
+
+import scalac.Global;
+import scalac.symtab.*;
+import scalac.symtab.Scope.SymbolIterator;
+import scalac.symtab.Type.Constraint;
+import scalac.util.Debug;
+import scalac.util.Name;
+import scalac.util.NameTransformer;
+
+// Lines modified or added by Vincent are marked "vincent".
+
+/**
+ * This class provides methods to print symbols and types.
+ */
+public class SymbolTablePrinter extends scalac.symtab.SymbolTablePrinter {
+
+ //########################################################################
+ // Private Fields
+
+ /** The global environment */
+ private final Global global;
+
+ /** The underlying code printer */
+ private final HTMLPrinter page;
+
+ /** The HTML generator using this object */ // vincent
+ HTMLGenerator htmlGenerator;
+
+ //########################################################################
+ // Public Constructors
+
+ /**
+ * Creates a new instance.
+ *
+ * @param htmlgenerator
+ * @param page
+ */
+ public SymbolTablePrinter(HTMLGenerator generator, HTMLPrinter page) {
+ super(page.getCodePrinter());
+ this.global = Global.instance;
+ this.page = page;
+ this.htmlGenerator = generator;
+ }
+
+ //########################################################################
+ // Public Methods - Printing scopes
+
+ /**
+ * Prints the members of the given scope.
+ *
+ * @param scope
+ */
+ public SymbolTablePrinter printScope(Scope scope) {
+ return printScope(scope, false);
+ }
+
+ /**
+ * Prints the members of the given scope.
+ *
+ * @param scope
+ * @param lazy
+ */
+ public SymbolTablePrinter printScope(Scope scope, boolean lazy) {
+ boolean first = true;
+ for (SymbolIterator i = scope.iterator(); i.hasNext(); ) {
+ Symbol member = i.next();
+ if (!mustShowMember(member)) continue;
+ if (first) print("{").indent(); else print(", ");
+ first = false;
+ line();
+ printSignature(member, Symbol.NONE, false);
+ }
+ if (!first)
+ line().undent().print("}");
+ else if (!lazy)
+ print("{}");
+ return this;
+ }
+
+ /**
+ * Returns <code>true</code> iff the given member must be printed.
+ * The default implementation always returns <code>true</code>.
+ *
+ * @param member
+ */
+ public boolean mustShowMember(Symbol member) {
+ return true;
+ }
+
+ //########################################################################
+ // Public Methods - Printing symbols
+
+ /**
+ * Returns the inner string of the given symbol.
+ *
+ * @param symbol
+ */
+ public String getSymbolInnerString(Symbol symbol) {
+ if (symbol.kind == Kinds.TYPE)
+ return "&lt;:"; // HTML encoded "<:" symbol
+ else
+ return super.getSymbolInnerString(symbol);
+ }
+
+ /**
+ * Prints the name of the given symbol usage.
+ *
+ * @param symbol
+ * @param user
+ */
+ public SymbolTablePrinter printUsedSymbolName(Symbol symbol, Symbol user) {
+ Name name = symbol.name;
+ if (!global.debug) name = NameTransformer.decode(name);
+ String s = name.toString();
+ if (htmlGenerator.isReferenced(symbol))
+ page.printAhref(htmlGenerator.ref(symbol, user), s);
+ else
+ page.print(s);
+ printSymbolUniqueId(symbol);
+ return this;
+ }
+
+ /**
+ * Prints the name of the given symbol definition.
+ *
+ * @param symbol
+ * @param user
+ * @param addLink
+ */
+ public SymbolTablePrinter printDefinedSymbolName(Symbol symbol, Symbol user, boolean addLink) {
+ Name name = symbol.name;
+ if (!global.debug) name = NameTransformer.decode(name);
+ String s = name.toString();
+ if (htmlGenerator.isReferenced(symbol))
+ if (addLink)
+ page.printAhref(htmlGenerator.ref(symbol, user), s);
+ else
+ page.printBold(s);
+ else
+ page.print(s);
+ printSymbolUniqueId(symbol);
+ return this;
+ }
+
+ /**
+ * Prints the type of the given symbol with the given inner string.
+ *
+ * @param symbol
+ * @param inner
+ */
+ public SymbolTablePrinter printSymbolType(Symbol symbol, String inner, Symbol user) {
+ Type type = symbol.rawFirstInfo();
+ if (!(type instanceof Type.LazyType)) type = symbol.info();
+ boolean star = false;
+ if ((symbol.flags & Modifiers.REPEATED) != 0 &&
+ type.symbol() == global.definitions.SEQ_CLASS &&
+ type.typeArgs().length == 1)
+ {
+ type = type.typeArgs()[0];
+ star = true;
+ }
+ printType(type, inner, user);
+ if (star) print("*");
+ return this;
+ }
+
+ /**
+ * Prints the signature of the given symbol.
+ *
+ * @param symbol
+ * @param addLink
+ */
+ public SymbolTablePrinter printSignature(Symbol symbol, Symbol user, boolean addLink) {
+ String keyword = getSymbolKeyword(symbol);
+ if (keyword != null) print(keyword).space();
+ String inner = getSymbolInnerString(symbol);
+ String name = symbol.nameString();
+ if (addLink)
+ page.printAhref("#" + name, name);
+ else
+ page.print(name);
+ return printType(symbol.loBound(), ">:", user)
+ .printSymbolType(symbol, inner, user);
+ }
+
+ /**
+ * Prints the signature of the given symbol.
+ *
+ * @param symbol
+ * @param addLink
+ */
+ public SymbolTablePrinter printShortSignature(Symbol symbol, Symbol user, boolean addLink) {
+ String keyword = getSymbolKeyword(symbol);
+ if (keyword != null) print(keyword).space();
+ String inner = getSymbolInnerString(symbol);
+ String name = symbol.nameString();
+ if (addLink)
+ page.printAhref("#" + name, name);
+ else
+ page.print(name);
+ return printType(symbol.loBound(), ">:", user);
+ }
+
+ /**
+ * Prints the signature of a class symbol.
+ *
+ * @param symbol
+ * @param addLink
+ * @return the current symbol table printer
+ */
+ public SymbolTablePrinter printTemplateSignature(Symbol symbol, Symbol user, boolean addLink) {
+ // kind
+ String keyword = getSymbolKeyword(symbol);
+ if (keyword != null) print(keyword).space();
+ String inner = getSymbolInnerString(symbol);
+
+ // name
+ printDefinedSymbolName(symbol, user, addLink);
+ if (symbol.isClass()) {
+ // type parameters
+ Symbol[] tparams = symbol.typeParams();
+ if (tparams.length != 0 || global.debug) {
+ print('[');
+ for (int i = 0; i < tparams.length; i++) {
+ if (i > 0) print(",");
+ printSignature(tparams[i], user, false);
+ }
+ print(']');
+ }
+ // value parameters
+ Symbol[] vparams = symbol.valueParams();
+ print('(');
+ for (int i = 0; i < vparams.length; i++) {
+ if (i > 0) print(", "); // vincent
+ if (vparams[i].isDefParameter()) print("def ");
+ htmlGenerator.defString(vparams[i], user, false);
+ }
+ print(')');
+ }
+
+ // parents
+// Type[] parts = symbol.moduleClass().parents();
+// if (parts.length != 0) space().print(inner).space();
+// printTypes(parts," with ");
+ return this;
+ }
+
+ /**
+ * Prints the signature of a class symbol.
+ *
+ * @param symbol
+ * @param addLink
+ */
+ public SymbolTablePrinter printTemplateHtmlSignature(Symbol symbol, Symbol user, boolean addLink) {
+ // modifiers
+ String mods = Modifiers.Helper.toString(symbol.flags);
+ page.printlnOTag("dl");
+ page.printlnOTag("dt");
+ print(mods).space();
+
+ // kind
+ String keyword = getSymbolKeyword(symbol);
+ if (keyword != null) print(keyword).space();
+ String inner = getSymbolInnerString(symbol);
+
+ // name
+ printDefinedSymbolName(symbol, user, addLink);
+ if (symbol.isClass()) {
+ // type parameters
+ Symbol[] tparams = symbol.typeParams();
+ if (tparams.length != 0 || global.debug) {
+ print('[');
+ for (int i = 0; i < tparams.length; i++) {
+ if (i > 0) print(",");
+ printSignature(tparams[i], user, false);
+ }
+ print(']');
+ }
+ // value parameters
+ Symbol[] vparams = symbol.valueParams();
+ print('(');
+ for (int i = 0; i < vparams.length; i++) {
+ if (i > 0) print(", ");
+ if (vparams[i].isDefParameter()) print("def ");
+ htmlGenerator.defString(vparams[i], user, false);
+ }
+ print(')');
+ }
+
+ // parents
+ Type[] parts = symbol.moduleClass().parents();
+ page.printlnCTag("dt");
+ for (int i = 0; i < parts.length; i++) {
+ page.printOTag("dd");
+ print((i == 0) ? "extends " : "with ");
+ printType(parts[i], symbol);
+ page.printlnCTag("dd");
+ }
+ page.printCTag("dl");
+ return this;
+ }
+
+ //########################################################################
+ // Public Methods - Printing types
+
+ /**
+ * Prints the given types separated by infix.
+ *
+ * @param types
+ * @param infix
+ */
+ public SymbolTablePrinter printTypes(Type[] types, String infix, Symbol user) {
+ for (int i = 0; i < types.length; i++) {
+ if (i > 0) print(infix);
+ printType(types[i], user);
+ }
+ return this;
+ }
+
+ /**
+ * Prints the given type.
+ *
+ * @param type
+ * @param user The symbol using the type <code>type</code>
+ */
+ public SymbolTablePrinter printType(Type type, Symbol user) {
+ return printType0(getTypeToPrintForType(type), user);
+ }
+
+ /**
+ * ..
+ *
+ * @param type
+ * @param user
+ */
+ public SymbolTablePrinter printType0(Type type, Symbol user) {
+ printCommonPart(type, user);
+ switch (type) {
+ case ThisType(_):
+ case SingleType(_,_):
+ print(".type");
+ return this;
+ default:
+ return this;
+ }
+ }
+
+ /**
+ * Prints the given type with the given inner string.
+ *
+ * @param type
+ * @param inner
+ */
+ public SymbolTablePrinter printType(Type type, String inner, Symbol user) {
+ if ("<:".equals(inner) && type.symbol() == global.definitions.ANY_CLASS ||
+ ">:".equals(inner) && type.symbol() == global.definitions.ALL_CLASS)
+ return this;
+ else
+ return printType0(getTypeToPrintForType(type), inner, user);
+ }
+
+ /**
+ * ..
+ *
+ * @param type
+ * @param inner
+ */
+ public SymbolTablePrinter printType0(Type type, String inner, Symbol user) {
+ switch (type) {
+ case MethodType(Symbol[] vparams, Type result):
+ print('(');
+ for (int i = 0; i < vparams.length; i++) {
+ if (i > 0) print(", ");
+ if (vparams[i].isDefParameter()) print("def ");
+ htmlGenerator.defString(vparams[i], user, false);
+ //print(NameTransformer.decode(vparams[i].name.toString()) + ": ");// vincent
+ //printSymbolType(vparams[i], null);
+ }
+ print(')');
+ return printType(result, inner, user);
+ case PolyType(Symbol[] tparams, Type result):
+ if (tparams.length != 0 || global.debug) {
+ print('[');
+ for (int i = 0; i < tparams.length; i++) {
+ if (i > 0) print(",");
+ printSignature(tparams[i], user, false);
+ }
+ print(']');
+ }
+ return printType(result, inner, user);
+ default:
+ if (inner != null) {
+ if (!inner.startsWith(":")) space();
+ print(inner).space();
+ }
+ return printType0(type, user);
+ }
+ }
+
+ /**
+ * Prints a function type with the given type arguments.
+ *
+ * @param types
+ */
+ public SymbolTablePrinter printFunctionType(Type[] types) {
+ Type[] args = new Type[types.length - 1];
+ for (int i = 0; i < args.length; i++) args[i] = types[i];
+ print('(');
+ printTypes(args, ", ", Symbol.NONE);
+ print(") => ");
+ printType(types[types.length - 1], Symbol.NONE);
+ return this;
+ }
+
+ /**
+ * Prints a template type with the given base types.
+ *
+ * @param types
+ */
+ public SymbolTablePrinter printTemplateType(Type[] types) {
+ print("<template: ");
+ printTypes(types, " with ", Symbol.NONE);
+ print(" {...}>");
+ return this;
+ }
+
+ /**
+ * Prints the type and prefix common part of the given type.
+ *
+ * @param type
+ * @param user The symbol using the type <code>type</code>
+ */
+ public SymbolTablePrinter printCommonPart(Type type, Symbol user) {
+ switch (type) {
+ case ErrorType:
+ print("<error>");
+ return this;
+
+ case AnyType:
+ print("<any type>");
+ return this;
+
+ case NoType:
+ print("<notype>");
+ return this;
+
+ case ThisType(Symbol sym):
+ if (sym == Symbol.NONE) print("<local>.this");
+ if ((sym.isAnonymousClass() || sym.isCompoundSym()) && !global.debug)
+ print("this");
+ printUsedSymbolName(sym, user);
+ print(".this"); // vincent
+ return this;
+
+ case TypeRef(Type pre, Symbol sym, Type[] args):
+ if (!global.debug) {
+ if (type.isFunctionType()) {
+ printFunctionType(args);
+ return this;
+ }
+ if (sym.isAnonymousClass() || sym.isCompoundSym())
+ printTemplateType(pre.memberInfo(sym).parents());
+ }
+ printPrefix(pre, sym);
+ printUsedSymbolName(sym, user);
+ if (args.length != 0) {
+ print('[');
+ printTypes(args, ",", user);
+ print(']');
+ }
+ return this;
+
+ case SingleType(Type pre, Symbol sym):
+ printPrefix(pre, sym);
+ printUsedSymbolName(sym, user);
+ return this;
+
+ case CompoundType(Type[] parts, Scope members):
+ printTypes(parts," with ", user);
+ space();
+ return printScope(members, true); // vincent
+
+ case MethodType(_, _):
+ return printType0(type, user);
+
+ case PolyType(_, _):
+ return printType0(type, user);
+
+ case OverloadedType(Symbol[] alts, Type[] alttypes):
+ return printTypes(alttypes, " <and> ", user);
+
+ case TypeVar(Type origin, Constraint constr):
+ printType(origin, user);
+ print("?");
+ return this;
+
+ case UnboxedType(int kind):
+ print(type.unboxedName(kind).toString());
+ return this;
+
+ case UnboxedArrayType(Type elemtp):
+ printType(elemtp, user);
+ print("[]");
+ return this;
+
+ case LazyType():
+ if (!global.debug) print("?");
+ String classname = type.getClass().getName();
+ print("<lazy type ").print(classname).print(">");
+ return this;
+
+ default:
+ String classname = type.getClass().getName();
+ print("<unknown type ").print(classname).print(">");
+ return this;
+ }
+ }
+
+ //########################################################################
+ // Public Methods - Printing prefixes
+
+ /**
+ * Returns the type to print for the given prefix (transitive).
+ *
+ * @param prefix
+ * @param sym
+ */
+ public Type getTypeToPrintForPrefix(Type prefix, Symbol sym) {
+ while (true) {
+ Type result = getTypeToPrintForPrefix0(prefix);
+ if (result == prefix || result == null) {
+ return
+ htmlGenerator.cleanPrefix(result); // vincent
+ // Next lines should replace the previous one in theory.
+ // htmlGenerator.isReferenced(sym) ?
+ // htmlGenerator.cleanPrefix(result) : // vincent
+ // result;
+ }
+ prefix = result;
+ }
+ }
+
+ /**
+ * Prints the given type as a prefix.
+ *
+ * @param prefix
+ */
+ public SymbolTablePrinter printPrefix(Type prefix, Symbol sym) {
+ prefix = getTypeToPrintForPrefix(prefix, sym);
+ return prefix == null ? this : printPrefix0(prefix);
+ }
+
+ /**
+ * Returns the type to print for the given prefix (non-transitive).
+ *
+ * @param prefix
+ */
+ public Type getTypeToPrintForPrefix0(Type prefix) {
+ if (!global.debug) {
+ if (prefix.symbol().kind == Kinds.NONE) return null;
+ if (prefix.symbol().isRoot()) return null;
+ }
+ return getTypeToPrintForType0(prefix);
+ }
+
+ /**
+ * ..
+ */
+ public SymbolTablePrinter printPrefix0(Type prefix) {
+ printCommonPart(prefix, Symbol.NONE);
+ switch (prefix) {
+ case ThisType(_):
+ case SingleType(_,_):
+ print(".");
+ return this;
+ default:
+ print("#");
+ return this;
+ }
+ }
+
+ //########################################################################
+ // Public Methods - Converting
+
+ /**
+ * Returns the string representation of this.
+ */
+ public String toString() {
+ return toString();
+ }
+
+ //########################################################################
+}
diff --git a/sources/scala/tools/scaladoc/Tag.java b/sources/scala/tools/scaladoc/Tag.java
new file mode 100644
index 0000000000..a659707601
--- /dev/null
+++ b/sources/scala/tools/scaladoc/Tag.java
@@ -0,0 +1,150 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+\* */
+
+// $Id$
+
+package scala.tools.scaladoc;
+
+import java.util.regex.*;
+
+import ch.epfl.lamp.util.Pair;
+
+import scalac.ast.Tree;
+import scalac.symtab.Symbol;
+
+import scaladoc.*;
+
+/**
+ * Documentation tag.
+ */
+public class Tag {
+
+ /**
+ * Symbol whose comment contains the tag.
+ */
+ public final Symbol holder;
+
+ /**
+ * Name of the tag.
+ */
+ public final String name;
+
+ /**
+ * Value of the tag.
+ */
+ public final String text;
+
+ /**
+ * Constructor
+ *
+ * @param name
+ * @param text
+ */
+ public Tag(Symbol holder, String name, String text) {
+ this.holder = holder;
+ this.name = name;
+ this.text = text;
+ }
+
+ /** String representation.
+ */
+ public String toString() {
+ return "Tag(" + name + ", " + text + ")";
+ }
+
+ /**
+ * Is this tag a param tag ?
+ */
+ public boolean isParam() {
+ return name.equals("@param");
+ }
+
+ /**
+ * Is this tag an exception tag ?
+ */
+ public boolean isException() {
+ return name.equals("@param") || name.equals("@exception");
+ }
+
+ /**
+ * Is this tag a reference tag ?
+ */
+ public boolean isReference() {
+ return name.equals("@see") || name.equals("@link");
+ }
+
+ /**
+ * Is this tag a text tag ?
+ */
+ public boolean isText() {
+ return name.equals("@text");
+ }
+
+ /**
+ * Splits a string containing spaces into two parts.
+ *
+ * @param text
+ */
+ public static Pair split(String text) {
+ int length = text.length();
+ int endFst = length;
+ int startSnd = length;
+ int current = 0;
+ while (current < length && startSnd >= length) {
+ char c = text.charAt(current);
+ if (Character.isWhitespace(c) && endFst == length)
+ endFst = current;
+ else if (!Character.isWhitespace(c) && endFst != length)
+ startSnd = current;
+ current++;
+ }
+ return new Pair(text.substring(0, endFst),
+ text.substring(startSnd, length));
+ }
+
+ /**
+ * Kind of a reference tag.
+ */
+ public static class RefKind {
+ /** Bad reference. */
+ public case Bad(String ref);
+
+ /** Reference to an URL. */
+ public case Url(String ref);
+
+ /** String literal reference. */
+ public case Literal(String ref);
+
+ /** Reference to a scala entity. */
+ public case Scala(String container, String member, String label);
+ }
+
+ /**
+ * Parses a reference tag.
+ *
+ * @param tag
+ */
+ protected static RefKind parseReference(Tag tag) {
+ if (tag.text.startsWith("\""))
+ return RefKind.Literal(tag.text);
+ else if (tag.text.startsWith("<"))
+ return RefKind.Url(tag.text);
+ else {
+ Pattern p = Pattern.compile("(([^#\\s]*)(#([^\\s]+))?)\\s*(.+)?");
+ Matcher m = p.matcher(tag.text);
+ if (m.find()) {
+ String container = m.group(2) == null ? "" : m.group(2);
+ String member = m.group(3);
+ String label = m.group(4) == null ? "" : m.group(4);
+ return RefKind.Scala(container, member, label);
+ } else {
+ System.err.println("Warning: Fail to parse see tag:" + tag + " in " + tag.holder);
+ return RefKind.Bad(tag.text);
+ }
+ }
+ }
+
+}
diff --git a/sources/scala/tools/scaladoc/resources/script.js b/sources/scala/tools/scaladoc/resources/script.js
new file mode 100644
index 0000000000..94093ecc50
--- /dev/null
+++ b/sources/scala/tools/scaladoc/resources/script.js
@@ -0,0 +1,5 @@
+<!--
+function setWindowTitle(title) {
+ parent.document.title = title;
+}
+-->
diff --git a/sources/scala/tools/scaladoc/resources/style.css b/sources/scala/tools/scaladoc/resources/style.css
new file mode 100644
index 0000000000..5fecb9014e
--- /dev/null
+++ b/sources/scala/tools/scaladoc/resources/style.css
@@ -0,0 +1,124 @@
+/* Scaladoc style sheet */
+
+a:link {
+ color: #0000ee;
+}
+
+a:visited {
+ color: #551a8b;
+}
+
+a:active {
+ color: #0000ee;
+}
+
+body {
+ background-color: #ffffff;
+}
+
+div.entity {
+ margin: 18px 0px 18px 0px;
+ font-size: x-large;
+ font-weight: bold;
+}
+
+div.doctitle {
+ font-weight: bold;
+ font-style: italic;
+}
+
+div.doctitle-larger {
+ margin: 0px 0px 10px 0px;
+ font-size: larger;
+ font-weight: bold;
+}
+
+div.page-title {
+ margin: 15px 0px 15px 0px;
+ font-size: x-large;
+ font-weight: bold;
+ text-align: center;
+}
+
+span.entity {
+ color: #ff6666;
+}
+
+table.member {
+ border-collapse: collapse;
+ border: 2px solid #888888;
+ background-color: #ffffff;
+ width: 100%;
+}
+
+table.member-detail {
+ margin: 10px 0px 0px 0px;
+ border-collapse: collapse;
+ border: 2px solid #888888;
+ background-color: #ffffff;
+ width: 100%;
+}
+
+table.navigation {
+ border-collapse: collapse;
+ width: 100%;
+ font-family: Arial,Helvetica,Sans-serif;
+}
+
+table.list {
+ border-collapse: collapse;
+ border-style: none;
+ width: 100%;
+}
+
+td.inherited-members {
+ border-top: 2px solid #888888;
+ border-right: 0px;
+}
+
+td.inherited-owner {
+ background-color: #eeeeff;
+ font-weight: bold;
+}
+
+td.member-title {
+ border: 2px solid #888888;
+ background-color: #ccccff;
+ font-size: x-large;
+ font-weight: bold;
+}
+
+td.modifiers {
+ border-top: 2px solid #888888;
+ border-right: 2px solid #888888;
+ width: 50px;
+ text-align: right;
+}
+
+td.navigation-enabled {
+ font-weight: bold;
+ color: #000000;
+ background-color: #eeeeff;
+}
+
+td.navigation-links {
+ width: 100%;
+ background-color: #eeeeff;
+}
+
+td.navigation-selected {
+ font-weight: bold;
+ color: #ffffff;
+ background-color: #00008b;
+}
+
+td.signature {
+ border-top: 2px solid #888888;
+ width: 90%;
+}
+
+td.title {
+ background-color: #ccccff;
+ font-size: x-large;
+ font-weight: bold;
+}