summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSean McDirmid <sean.mcdirmid@gmail.com>2007-03-28 12:45:13 +0000
committerSean McDirmid <sean.mcdirmid@gmail.com>2007-03-28 12:45:13 +0000
commit43185f20f4c71d9dce678f5d2c8253884fd61727 (patch)
treef3b97f235cb3cd29395a0712130ddf05fa58c707 /src
parent806238432588319e91805570ffbfc2f0ce5f409b (diff)
downloadscala-43185f20f4c71d9dce678f5d2c8253884fd61727.tar.gz
scala-43185f20f4c71d9dce678f5d2c8253884fd61727.tar.bz2
scala-43185f20f4c71d9dce678f5d2c8253884fd61727.zip
New scala doc
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/doc/DocDriver.scala242
-rw-r--r--src/compiler/scala/tools/nsc/doc/ModelAdditions.scala195
-rw-r--r--src/compiler/scala/tools/nsc/doc/ModelExtractor.scala407
-rw-r--r--src/compiler/scala/tools/nsc/doc/ModelFrames.scala325
-rw-r--r--src/compiler/scala/tools/nsc/doc/ModelToXML.scala237
5 files changed, 1406 insertions, 0 deletions
diff --git a/src/compiler/scala/tools/nsc/doc/DocDriver.scala b/src/compiler/scala/tools/nsc/doc/DocDriver.scala
new file mode 100644
index 0000000000..a70056f886
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/doc/DocDriver.scala
@@ -0,0 +1,242 @@
+package scala.tools.nsc.doc;
+import scala.collection.jcl;
+import symtab.Flags._;
+import scala.xml._;
+
+abstract class DocDriver extends ModelFrames with ModelToXML {
+ import global._;
+
+ object additions extends jcl.LinkedHashSet[Symbol];
+ object additions0 extends ModelAdditions(global) {
+ override def addition(sym : global.Symbol) = {
+ super.addition(sym);
+ sym match {
+ case sym : global.ClassSymbol => additions += sym.asInstanceOf[Symbol];
+ case sym : global.ModuleSymbol => additions += sym.asInstanceOf[Symbol];
+ case sym : global.TypeSymbol => additions += sym.asInstanceOf[Symbol];
+ case _ =>
+ }
+ }
+ def init : Unit = {}
+ }
+
+ def process(units: Iterator[CompilationUnit]): Unit = {
+ assert(global.definitions != null);
+
+ def g(pkg : Package, clazz : ClassOrObject) : Unit = {
+ allClasses(pkg) += clazz;
+ clazz.decls.map(._2).foreach{
+ case clazz : ClassOrObject =>
+ g(pkg, clazz);
+ case _ =>
+ }
+ }
+ def f(pkg : Package, tree : Tree) : Unit = if (!tree.symbol.hasFlag(symtab.Flags.PRIVATE)) tree match {
+ case tree : PackageDef =>
+ val pkg1 = new Package(tree.symbol.asInstanceOf[ModuleSymbol]);
+ tree.stats.foreach(stat => f(pkg1, stat));
+ case tree : ClassDef =>
+ assert(pkg != null);
+ g(pkg, new TopLevelClass(tree.symbol.asInstanceOf[ClassSymbol]));
+ case tree : ModuleDef =>
+ assert(pkg != null);
+ g(pkg, new TopLevelObject(tree.symbol.asInstanceOf[ModuleSymbol]));
+ case _ =>
+ }
+ units.foreach(unit => f(null, unit.body));
+
+
+ for (val p <- allClasses; val d <- p._2) {
+ symbols += d.sym;
+ for (val pp <- d.sym.tpe.parents) subClasses(pp.symbol) += d;
+ }
+ additions0.init;
+ copyResources;
+ val packages0 = sort(allClasses.keySet);
+ new AllPackagesFrame with Frame { def packages = packages0; };
+ new PackagesContentFrame with Frame { def packages = packages0; };
+ new NavigationFrame with Frame { };
+ new ListClassFrame with Frame {
+ def classes = {
+ for (val p <- allClasses; val d <- p._2) yield d;
+ }
+ object organized extends jcl.LinkedHashMap[(List[String],Boolean),List[ClassOrObject]] {
+ override def default(key : (List[String],Boolean)) = Nil;
+ classes.foreach(cls => {
+ val path = cls.path.map(.name);
+ this((path,cls.isInstanceOf[Clazz])) = cls :: this((path,cls.isInstanceOf[Clazz]));
+ });
+ }
+
+ def title = "List of all classes and objects"
+ def path = "all-classes"
+ def navLabel = null; // "root-page"
+ // override protected def navSuffix = ".html";
+ override def optional(cls : ClassOrObject) : NodeSeq = {
+ val path = cls.path.map(.name);
+ val key = (cls.path.map(.name), cls.isInstanceOf[Clazz]);
+ assert(!organized(key).isEmpty);
+ (if (!organized(key).tail.isEmpty) Text(" (" +{
+ //Console.println("CONFLICT: " + path + " " + organized(key));
+ val str = cls.path(0).sym.owner.fullNameString('.');
+ val idx = str.lastIndexOf('.');
+ if (idx == -1) str;
+ else str.substring(idx + 1);
+ }+ ")");
+ else NodeSeq.Empty) ++ super.optional(cls);
+ }
+
+ }
+ for (val (pkg0,classes0) <- allClasses) {
+ new ListClassFrame with Frame {
+ def title =
+ "List of classes and objects in package " + pkg0.fullName('.')
+ def classes = classes0;
+ def path = pkg0.fullName('/') + NAME_SUFFIX_PACKAGE;
+ def navLabel = pkg0.fullName('.');
+ }
+ new PackageContentFrame with Frame {
+ def classes = classes0;
+ def pkg = pkg0;
+ }
+ for (val clazz0 <- classes0) {
+ new ClassContentFrame with Frame {
+ def clazz = clazz0;
+ def title =
+ clazz0.kind + " " + clazz0.name + " in " + (clazz0.sym.owner.fullNameString('.'));
+ }
+ }
+ }
+ for (val sym <- additions) sym match {
+ case sym : ClassSymbol =>
+ val add = new TopLevelClass(sym);
+ new ClassContentFrame with Frame {
+ def clazz = add;
+ def title =
+ add.kind + " " + add.name + " in package " + add.sym.owner.fullNameString('.')
+ }
+ case sym : TypeSymbol =>
+ val add = new TopLevelClass(sym);
+ new ClassContentFrame with Frame {
+ def clazz = add;
+ def title =
+ add.kind + " " + add.name + " in package " + add.sym.owner.fullNameString('.')
+ }
+ case sym : ModuleSymbol =>
+ val add = new TopLevelObject(sym);
+ new ClassContentFrame with Frame {
+ def clazz = add;
+ def title =
+ add.kind + " " + add.name + " in package " + add.sym.owner.fullNameString('.')
+ }
+ }
+ new RootFrame with Frame;
+ }
+ override def longList(entity : ClassOrObject,category : Category)(implicit from : Frame) : NodeSeq = category match {
+ case (Classes | Objects) => NodeSeq.Empty;
+ case _ => super.longList(entity,category);
+ }
+ trait Frame extends super.Frame {
+ def longHeader(entity : Entity) = DocDriver.this.longHeader(entity)(this);
+ def shortHeader(entity : Entity) = DocDriver.this.shortHeader(entity)(this);
+ }
+ import DocUtil._;
+ override def classBody(entity : ClassOrObject)(implicit from : Frame) : NodeSeq =
+ (subClasses.get(entity.sym) match {
+ case Some(symbols) =>
+ <dl>
+ <dt style="margin:10px 0 0 20px;"><b>Direct Known Subclasses:</b></dt>
+ <dd>{symbols.mkXML("",", ","")(cls => {
+ aref(urlFor(cls.sym), cls.path.map(.name).mkString("",".",""));
+ })}</dd>
+ </dl><hr/>;
+ case None => NodeSeq.Empty;
+ })++super.classBody(entity);
+ protected def urlFor(sym : Symbol)(implicit frame : Frame) = frame.urlFor(sym);
+
+ override protected def decodeTag(tag : String) : String = tag match {
+ case "exception" => "Throws"
+ case "ex" => "Examples"
+ case "param" => "Parameters"
+ case "pre" => "Precondition"
+ case "return" => "Returns"
+ case "note" => "Notes"
+ case "see" => "See Also"
+ case tag => super.decodeTag(tag);
+ }
+ override protected def decodeOption(tag : String, option : String) : NodeSeq = tag match {
+ case "throws" if additions0.exceptions.contains(option) =>
+ val (sym, s) = additions0.exceptions(option);
+ val path = "../" //todo: fix path
+ val href = path + sym.fullNameString('/') +
+ (if (sym.isModule || sym.isModuleClass) NAME_SUFFIX_OBJECT else "") +
+ "#" + s
+ <a href={href}>{option}</a> ++ {Text(" - ")};
+ case _ => super.decodeOption(tag,option);
+ }
+ object roots extends jcl.LinkedHashMap[String,String];
+ roots("classes") = "http://java.sun.com/j2se/1.5.0/docs/api";
+ roots("rt") = roots("classes");
+ roots("scala-library") = "http://www.scala-lang.org/docu/files/api";
+
+ private def keyFor(file : java.util.zip.ZipFile) : String = {
+ var name = file.getName;
+ var idx = name.lastIndexOf(java.io.File.pathSeparator);
+ if (idx == -1) idx = name.lastIndexOf('/');
+ if (idx != -1) name = name.substring(idx + 1);
+ if (name.endsWith(".jar")) return name.substring(0, name.length - (".jar").length);
+ else return null;
+ }
+
+ // <code>{Text(string + " - ")}</code>;
+ override def hasLink0(sym : Symbol) : Boolean = {
+ if (sym == NoSymbol) return false;
+ val ret = super.hasLink0(sym) && (additions.contains(sym) || symbols.contains(sym));
+ if (ret) return true;
+ if (sym.toplevelClass == NoSymbol) return false;
+ val clazz = sym.toplevelClass.asInstanceOf[ClassSymbol];
+ import scala.tools.nsc.io._;
+ clazz.classFile match {
+ case file : ZipArchive#FileEntry =>
+ val key = keyFor(file.archive);
+ if (key != null && roots.contains(key)) return true;
+ case null =>
+ case _ =>
+ }
+ return false;
+ }
+ def aref(href : String, label : String)(implicit frame : Frame) =
+ frame.aref(href, "_self", label);
+ protected def anchor(entity : Symbol)(implicit frame : Frame) : NodeSeq =
+ <a name={Text(frame.docName(entity))}></a>
+
+ object symbols extends jcl.LinkedHashSet[Symbol];
+ object allClasses extends jcl.LinkedHashMap[Package,jcl.LinkedHashSet[ClassOrObject]] {
+ override def default(pkg : Package) : jcl.LinkedHashSet[ClassOrObject] = {
+ object ret extends jcl.LinkedHashSet[ClassOrObject];
+ this(pkg) = ret; ret;
+ }
+ }
+ object subClasses extends jcl.LinkedHashMap[Symbol,jcl.LinkedHashSet[ClassOrObject]] {
+ override def default(key : Symbol) = {
+ val ret = new jcl.LinkedHashSet[ClassOrObject];
+ this(key) = ret; ret;
+ }
+ }
+ override def rootFor(sym : Symbol) : String = {
+ assert(sym != NoSymbol);
+ if (sym.toplevelClass == NoSymbol) return super.rootFor(sym);
+ if (symbols.contains(sym.toplevelClass)) return super.rootFor(sym);
+ val clazz = sym.toplevelClass.asInstanceOf[ClassSymbol];
+ import scala.tools.nsc.io._;
+ clazz.classFile match {
+ case file : ZipArchive#FileEntry =>
+ val key = keyFor(file.archive);
+ if (key != null && roots.contains(key)) {
+ return roots(key) + '/';
+ }
+ case _ => ;
+ }
+ return super.rootFor(sym);
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/doc/ModelAdditions.scala b/src/compiler/scala/tools/nsc/doc/ModelAdditions.scala
new file mode 100644
index 0000000000..cba6de48b8
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/doc/ModelAdditions.scala
@@ -0,0 +1,195 @@
+package scala.tools.nsc.doc;
+
+class ModelAdditions(val global : Global) {
+ import global._;
+ import definitions._;
+ def addition(sym : global.Symbol) : Unit = {}
+
+ addition(AllClass);
+ comments(AllClass) = """
+ /** <p>
+ * Class <code>Nothing</code> (previously named <code>All</code> in
+ * <a href="http://scala-lang.org" target="_top">Scala</a> 2.2.0 and
+ * older versions) is - together with class <a href="Null.html">
+ * <code>Null</code></a> - at the bottom of the
+ * <a href="http://scala-lang.org" target="_top">Scala</a> type
+ * hierarchy.
+ * </p>
+ * <p>
+ * Type <code>Nothing</code> is a subtype of every other type
+ * (including <a href="Null.html"><code>Null</code></a>); there
+ * exist <em>no instances</em> of this type. Even though type
+ * <code>Nothing</code> is empty, it is nevertheless useful as a
+ * type parameter. For instance, the <a href="http://scala-lang.org"
+ * target="_top">Scala</a> library defines a value
+ * <a href="Nil$object.html"><code>Nil</code></a> of type
+ * <code><a href="List.html">List</a>[Nothing]</code>. Because lists
+ * are covariant in <a href="http://scala-lang.org" target="_top">Scala</a>,
+ * this makes <a href="Nil$object.html"><code>Nil</code></a> an
+ * instance of <code><a href="List.html">List</a>[T]</code>, for
+ * any element type <code>T</code>.
+ * </p>
+ */""";
+ addition(AllRefClass);
+ comments(AllRefClass) = """
+ /** <p>
+ * Class <code>Null</code> (previously named <code>AllRef</code> in
+ * <a href="http://scala-lang.org" target="_top">Scala</a> 2.2.0 and
+ * older versions) is - together with class <a href="Nothing.html">
+ * <code>Nothing</code> - at the bottom of the
+ * <a href="http://scala-lang.org" target="_top">Scala</a> type
+ * hierarchy.
+ * </p>
+ * <p>
+ * Type <code>Null</code> is a subtype of all reference types; its
+ * only instance is the <code>null</code> reference.
+ * Since <code>Null</code> is not a subtype of value types,
+ * <code>null</code> is not a member of any such type. For instance,
+ * it is not possible to assign <code>null</code> to a variable of
+ * type <a href="Int.html"><code>Int</code></a>.
+ * </p>
+ */""";
+
+ addition(AnyClass);
+ comments(AnyClass) = """
+ /** <p>
+ * Class <code>Any</code> is the root of the <a
+ * href="http://scala-lang.org/"
+ * target="_top">Scala</a> class hierarchy. Every class in a
+ * <a href="http://scala-lang.org/" target="_top">Scala</a> execution
+ * environment inherits directly or indirectly from this class.
+ * Class <code>Any</code> has two direct subclasses:
+ * <a href="AnyRef.html"><code>AnyRef</code></a> and
+ * <a href="AnyVal.html"><code>AnyVal</code></a>.
+ * </p>
+ */""";
+ /****/
+ addition(Object_isInstanceOf);
+
+ comments(Object_isInstanceOf) = """
+ /** <p>
+ * The method <code>isInstanceOf</code> is the pendant of the Java
+ * operator <code>instanceof</code>.
+ * </p>
+ * @see <ul><li>Java Language Specification (2<sup>nd</sup> Ed.):
+ * <a href="http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html#80289"
+ * target="_top">Operator <code>instanceof</code></a>.</li></ul>
+ */""";
+ /****/
+ addition(Object_synchronized);
+ comments(Object_synchronized) = """
+ /** <p>
+ * To make your programs thread-safe, you must first identify what
+ * data will be shared across threads. If you are writing data that
+ * may be read later by another thread, or reading data that may
+ * have been written by another thread, then that data is shared,
+ * and you must synchronize when accessing it.
+ * </p>
+ * @see <ul><li>The Java Tutorials:
+ * <a href="http://java.sun.com/docs/books/tutorial/essential/concurrency/sync.html"
+ * target="_top">Synchronization</a>.</li>
+ * <li> IBM developerWorks:
+ * <a href="http://www-128.ibm.com/developerworks/java/library/j-threads1.html"
+ * target="_top">Synchronization is not the enemy</a>.</li></ul>
+ */""";
+ addition(AnyRefClass);
+ comments(AnyRefClass) = """
+ /** <p>
+ * Class <code>AnyRef</code> is the root class of all
+ * <em>reference types</em>.
+ * </p>
+ */""";
+ addition(AnyValClass);
+ comments(AnyValClass) = """
+ /** <p>
+ * Class <code>AnyVal</code> is the root class of all
+ * <em>value types</em>.
+ * </p>
+ * <p>
+ * <code>AnyVal</code> has a fixed number subclasses, which
+ * describe values which are not implemented as objects in the
+ * underlying host system.
+ * </p>
+ * <p>
+ * Classes <a href="Double.html"><code>Double</code></a>,
+ * <a href="Float.html"><code>Float</code></a>,
+ * <a href="Long.html"><code>Long</code></a>,
+ * <a href="Int.html"><code>Int</code></a>,
+ * <a href="Char.html"><code>Char</code></a>,
+ * <a href="Short.html"><code>Short</code></a>, and
+ * <a href="Byte.html"><code>Byte</code></a> are together called
+ * <em>numeric value types</em>.
+ * Classes <a href="Byte.html"><code>Byte</code></a>,
+ * <a href="Short.html"><code>Short</code></a>, or
+ * <a href="Char.html"><code>Char</code></a>
+ * are called <em>subrange types</em>. Subrange types, as well as
+ * <a href="Int.html"><code>Int</code></a> and
+ * <a href="Long.html"><code>Long</code></a> are called
+ * <em>integer types</em>, whereas
+ * <a href="Float.html"><code>Float</code></a> and
+ * <a href="Double.html"><code>Double</code></a> are called
+ * <em>floating point types</em>.
+ * </p>
+ */""";
+ addition(BooleanClass);
+ comments(BooleanClass) = {"""
+ /** <p>
+ * Class <code>Boolean</code> has only two values: <code>true</code>
+ * and <code>false</code>.
+ * </p>
+ */"""};
+ def numericValDescr(sym: Symbol) = {
+ val maxValue = "MAX_" + sym.name.toString().toUpperCase()
+ val minValue = "MIN_" + sym.name.toString().toUpperCase();
+ addition(sym);
+ comments(sym) = """
+ /** <p>
+ * Class <code>""" + sym.name + """ </code> belongs to the value
+ * classes whose instances are not represented as objects by the
+ * underlying host system. All value classes inherit from class
+ * <a href="AnyVal.html"><code>AnyVal</code></a>.
+ * </p>
+ * <p>
+ * Values <code>""" + maxValue + """</code> and <code>""" + minValue + """</code>
+ * are in defined in object <a href="compat/Math$object.html">scala.compat.Math</a>.
+ * </p>
+ */""";
+ }
+ (ByteClass :: CharClass :: DoubleClass :: LongClass ::
+ FloatClass :: IntClass :: ShortClass :: Nil).foreach(numericValDescr);
+
+ addition(UnitClass);
+ comments(UnitClass) = {"""
+ /** <p>
+ * Class <code>Unit</code> has only one value: <code>()</code>.
+ * </p>
+ */"""};
+ addition(UnitClass);
+
+ def boxedValDescr(what : String) = {
+ val sym = definitions.getClass("scala.runtime.Boxed" + what);
+ addition(sym);
+ comments(sym) = """
+ /** <p>
+ * Class <code>""" + sym.name + """</code> implements the
+ * boxing/unboxing from/to value types.
+ * </p>
+ * <p>
+ * Boxing and unboxing enable value types to be treated as objects;
+ * they provide a unified view of the type system wherein a value
+ * of any type can ultimately be treated as an object.
+ * </p>
+ */"""
+ };
+ ("Float" :: "Long" :: "Number" :: "Int" :: Nil).foreach(boxedValDescr);
+
+ object exceptions extends collection.jcl.TreeMap[String,(Symbol,String)] {
+ def f(name : String) = {
+ this("Predef." + name) = (definitions.PredefModule, name);
+ }
+ f("IndexOutOfBoundsException");
+ f("NoSuchElementException");
+ f("NullPointerException");
+ f("UnsupportedOperationException");
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/doc/ModelExtractor.scala b/src/compiler/scala/tools/nsc/doc/ModelExtractor.scala
new file mode 100644
index 0000000000..cd98915002
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/doc/ModelExtractor.scala
@@ -0,0 +1,407 @@
+package scala.tools.nsc.doc;
+import scala.collection.jcl;
+import compat.Platform.{EOL => LINE_SEPARATOR}
+
+
+/** This class attempts to reverse engineer source code intent from compiler symbol objects
+ * @author Sean McDirmid
+ */
+trait ModelExtractor {
+ val global : Global;
+ import global._;
+ def assert(b : Boolean) {
+ if (!b)
+ throw new Error;
+ }
+ case class Tag(tag : String, option : String, body : String);
+ case class Comment(body : String, attributes : List[Tag]) {
+ def decodeAttributes = {
+ val map = new jcl.LinkedHashMap[String,List[(String,String)]] {
+ override def default(key : String) = Nil;
+ }
+ attributes.foreach(a => {
+ map(a.tag) = map(a.tag) ::: ((a.option,a.body) :: Nil);
+ });
+ map;
+ }
+ }
+ protected def decode(sym : Symbol) = {
+ if (sym == definitions.ScalaObjectClass || sym == definitions.ObjectClass)
+ definitions.AnyRefClass;
+ else sym match {
+ case sym : ModuleClassSymbol => sym.sourceModule;
+ case sym => sym;
+ }
+ }
+
+ protected def decodeComment(comment0 : String) : Comment = {
+ assert(comment0.startsWith("/**"));
+ assert(comment0.endsWith("*/"));
+ val comment = comment0.substring("/**".length, comment0.length - "*/".length);
+ val tok = new java.util.StringTokenizer(comment, LINE_SEPARATOR);
+ val buf = new StringBuilder;
+ type AttrDescr = (String, String, StringBuilder)
+ val attributes = new collection.mutable.ListBuffer[AttrDescr]
+ var attr: AttrDescr = null
+ while (tok.hasMoreTokens) {
+ val s = tok.nextToken.replaceFirst("\\p{Space}?\\*", "")
+ val mat1 = pat1.matcher(s)
+ if (mat1.matches) {
+ attr = (mat1.group(1), null, new StringBuilder(mat1.group(2)))
+ //if (kind != CONSTRUCTOR)
+ attributes += attr
+ } else {
+ val mat2 = pat2.matcher(s)
+ if (mat2.matches) {
+ attr = (mat2.group(1), mat2.group(2), new StringBuilder(mat2.group(3)))
+ //if (kind != CLASS)
+ attributes += attr
+ } else if (attr ne null)
+ attr._3.append(s + LINE_SEPARATOR)
+ else
+ buf.append(s + LINE_SEPARATOR)
+ }
+ }
+ (Comment(buf.toString,attributes.toList.map({x => Tag(x._1,x._2,x._3.toString)})));
+ }
+
+
+ sealed abstract class Entity(val sym : Symbol) {
+ private[ModelExtractor] def sym0 = sym;
+
+ override def toString = sym.toString;
+ def comment : Option[String] = global.comments.get(sym);
+ // comments decoded, now what?
+ def attributes = sym.attributes;
+ def decodeComment : Option[Comment] = {
+ val comment0 = this.comment;
+ if (comment0.isEmpty) return None;
+ var comment = comment0.get.trim;
+ Some(ModelExtractor.this.decodeComment(comment));
+ }
+ protected def accessQualified(core : String, qual : String) = core match {
+ case "public" => ""; // assert(qual == null); "";
+ case core => core + (if (qual == null) "" else "[" + qual + "]");
+ }
+
+ def flagsString = {
+ import symtab.Flags;
+ val isLocal = sym.hasFlag(Flags.LOCAL);
+ val x = if (sym.hasFlag(Flags.PRIVATE)) "private" else if (sym.hasFlag(Flags.PROTECTED)) "protected" else "public";
+ var string = accessQualified(x, {
+ if (sym.hasFlag(Flags.LOCAL)) "this";
+ else if (sym.privateWithin != null && sym.privateWithin != NoSymbol)
+ sym.privateWithin.nameString;
+ else null;
+ });
+ def f(flag : Int, str : String) =
+ if (sym.hasFlag(flag)) string = string + " " + str;
+
+ f(Flags.IMPLICIT, "implicit");
+ f(Flags.SEALED, "sealed");
+ f(Flags.OVERRIDE, "override");
+ f(Flags.CASE, "case");
+ if (!sym.isTrait) f(Flags.ABSTRACT, "abstract");
+ if (!sym.isModule) f(Flags.FINAL, "final");
+ if (!sym.isTrait) f(Flags.DEFERRED, "abstract");
+ string.trim;
+ }
+ def listName = name;
+ def name = sym.nameString;
+ def fullName(sep : Char) = sym.fullNameString(sep);
+ def kind : String;
+ def header = {
+ }
+ def typeParams : List[TypeParam] = Nil;
+ def params : List[List[Param]] = Nil;
+ def resultType : Option[Type] = None;
+ def parents : Iterable[Type] = Nil;
+ def lo : Option[Type] = sym.info match {
+ case TypeBounds(lo,hi) if decode(lo.symbol) != definitions.AllClass => Some(lo);
+ case _ => None;
+ }
+ def hi : Option[Type] = sym.info match {
+ case TypeBounds(lo,hi) if decode(hi.symbol) != definitions.AnyClass => Some(hi);
+ case _ => None;
+ }
+ def variance = {
+ import symtab.Flags._;
+ if (sym.hasFlag(COVARIANT)) "+";
+ else if (sym.hasFlag(CONTRAVARIANT)) "-";
+ else "";
+ }
+ def overridden : Iterable[Symbol] = {
+ Nil;
+ }
+ }
+ class Param(sym : Symbol) extends Entity(sym) {
+ override def resultType = Some(sym.tpe);
+ //def kind = if (sym.isPublic) "val" else "";
+ def kind = "";
+ }
+ class ConstructorParam(sym : Symbol) extends Param(sym) {
+ override protected def accessQualified(core : String, qual : String) = core match {
+ case "public" => "val";
+ case "protected" => super.accessQualified(core,qual) + " val";
+ case "private" if qual == "this" => "";
+ case core => super.accessQualified(core, qual);
+ }
+ }
+
+ def Param(sym : Symbol) = new Param(sym);
+ class TypeParam(sym : Symbol) extends Entity(sym) {
+ def kind = "";
+ }
+ def TypeParam(sym : Symbol) = new TypeParam(sym);
+
+ trait Clazz extends ClassOrObject {
+ private def csym = sym.asInstanceOf[TypeSymbol];
+ override def typeParams = csym.typeParams.map(TypeParam);
+ override def params = {
+ if (constructorArgs.isEmpty) Nil;
+ else constructorArgs.values.toList :: Nil;
+ }
+ def isTrait = csym.isTrait;
+ override def kind = if (sym.isTrait) "trait" else "class";
+ }
+ trait Object extends ClassOrObject {
+ override def kind = "object";
+ }
+ case class Package(override val sym : ModuleSymbol) extends Entity(sym) {
+ override def kind = "package";
+ override def name = fullName('.');
+ }
+
+
+ trait TopLevel extends ClassOrObject;
+ class TopLevelClass (sym : Symbol) extends Entity(sym) with TopLevel with Clazz;
+ class TopLevelObject(sym : Symbol) extends Entity(sym) with TopLevel with Object;
+
+ def compare(pathA : List[ClassOrObject], pathB : List[ClassOrObject]) : Int = {
+ var pA = pathA;
+ var pB = pathB;
+ while (true) {
+ if (pA.isEmpty) return -1;
+ if (pB.isEmpty) return +1;
+ val diff = pA.head.name compare pB.head.name;
+ if (diff != 0) return diff;
+ pA = pA.tail;
+ pB = pB.tail;
+ }
+ return 0;
+ }
+
+ trait ClassOrObject extends Entity {
+ def path : List[ClassOrObject] = this :: Nil;
+ override def listName = path.map(.name).mkString("",".","");
+
+ object freshParents extends jcl.LinkedHashSet[Type] {
+ this addAll sym.tpe.parents;
+ this.toList.foreach(e => this removeAll e.parents);
+ }
+ object constructorArgs extends jcl.LinkedHashMap[Symbol,Param] {
+ sym.constrParamAccessors.foreach(arg => {
+ val str = symtab.Flags.flagsToString(arg.flags);
+ assert(arg.hasFlag(symtab.Flags.PRIVATE) && arg.hasFlag(symtab.Flags.LOCAL));
+ val argName = arg.name.toString.trim;
+ val actual = sym.tpe.decls.elements.find(e => {
+ val eName = e.name.toString.trim;
+ argName == eName && {
+ val str = symtab.Flags.flagsToString(e.flags);
+ !e.hasFlag(symtab.Flags.LOCAL);
+ }
+ });
+ if (!actual.isEmpty) this(actual.get) = new ConstructorParam(actual.get);
+ else this(arg) = new ConstructorParam(arg);
+ });
+ }
+ object decls extends jcl.LinkedHashMap[Symbol,Member] {
+ sym.tpe.decls.elements.foreach(e => {
+ if (!constructorArgs.contains(e)) {
+ val m = Member(e);
+ if (!m.isEmpty && !this.contains(e)) this.put(e, m.get);
+ }
+ });
+ }
+ def members0(f : Symbol => Boolean) = decls.pfilter(e => f(e)).valueSet;
+ def members(c : Category) : Iterable[Member] = members0(c.f);
+
+ object inherited extends jcl.LinkedHashMap[Symbol,List[Member]]() {
+ override def default(tpe : Symbol) = Nil;
+ {
+ for (val m <- sym.tpe.members; !sym.tpe.decls.elements.contains(m) &&
+ (Values.f(m) || Methods.f(m))) {
+ val o = m.overridingSymbol(sym);
+ if ((o == NoSymbol)) {
+ val parent = decode(m.enclClass);
+ val mo = Member(m);
+ if (!mo.isEmpty) {
+ this(parent) = mo.get :: this(parent);
+ }
+ }
+ }
+ }
+ }
+ override def parents = freshParents;
+ abstract class Member(sym : Symbol) extends Entity(sym) {
+ private def overriding = sym.allOverriddenSymbols;
+ override def comment = super.comment match {
+ case ret @ Some(comment) => ret;
+ case None =>
+ val o = overriding.find(comments.contains);
+ o.map(comments.apply);
+ }
+ }
+ abstract class ValDef(sym : Symbol) extends Member(sym) {
+ override def resultType = Some(resultType0);
+ protected def resultType0 : Type;
+ override def overridden : Iterable[Symbol] = {
+ var ret : jcl.LinkedHashSet[Symbol] = null;
+ for (val parent <- ClassOrObject.this.parents) {
+ val sym0 = sym.overriddenSymbol(parent.symbol);
+ if (sym0 != NoSymbol) {
+ if (ret == null) ret = new jcl.LinkedHashSet[Symbol];
+ ret += sym0;
+ }
+ }
+ if (ret == null) Nil else ret.readOnly;
+ }
+ }
+ case class Def(override val sym : TermSymbol) extends ValDef(sym) {
+ override def resultType0 = sym.tpe.finalResultType;
+ override def typeParams = sym.tpe.typeParams.map(TypeParam);
+ override def params = methodArgumentNames.get(sym) match {
+ case Some(argss) if argss.length > 1 || (!argss.isEmpty && !argss(0).isEmpty) =>
+ argss.map(.map(Param));
+ case _ =>
+ var i = 0;
+ val ret = for (val tpe <- sym.tpe.paramTypes) yield {
+ val ret = sym.newValueParameter(sym.pos, newTermName("arg" + i));
+ ret.setInfo(tpe);
+ i = i + 1;
+ Param(ret);
+ }
+ if (ret.isEmpty) Nil;
+ else ret :: Nil;
+ }
+ override def kind = "def";
+ }
+ case class Val(override val sym : TermSymbol) extends ValDef(sym) {
+ def resultType0 : Type = sym.tpe;
+ override def kind = {
+ import symtab.Flags._;
+ if (sym.hasFlag(ACCESSOR)) {
+ val setterName = nme.getterToSetter(sym.name);
+ val setter = sym.owner.info.decl(setterName);
+ if (setter == NoSymbol) "val" else "var";
+ } else {
+ assert(sym.hasFlag(JAVA));
+ if (sym.hasFlag(FINAL)) "val" else "var";
+ }
+ }
+ }
+ case class AbstractType(override val sym : Symbol) extends Member(sym) {
+ override def kind = "type";
+ }
+
+ abstract class NestedClassOrObject(override val sym : Symbol) extends Member(sym) with ClassOrObject {
+ override def path : List[ClassOrObject] = ClassOrObject.this.path ::: (super.path);
+ }
+
+ case class NestedClass(override val sym : ClassSymbol) extends NestedClassOrObject(sym) with Clazz;
+ case class NestedObject(override val sym : ModuleSymbol) extends NestedClassOrObject(sym) with Object;
+ def isVisible(sym : Symbol) : Boolean = {
+ import symtab.Flags._;
+ if (sym.isLocalClass) return false;
+ if (sym.isLocal) return false;
+ if (sym.isPrivateLocal) return false;
+ if (sym.hasFlag(PRIVATE)) return false;
+ if (sym.hasFlag(SYNTHETIC)) return false;
+ if (sym.hasFlag(BRIDGE)) return false;
+ if (sym.nameString.indexOf("$") != -1) return false;
+ if (sym.hasFlag(CASE) && sym.isMethod) return false;
+ return true;
+ }
+ def Member(sym : Symbol) : Option[Member] = {
+ import global._;
+ import symtab.Flags._;
+ if (!isVisible(sym)) return None;
+ if (sym.hasFlag(ACCESSOR)) {
+ if (sym.isSetter) return None;
+ assert(sym.isGetter);
+ return Some[Member](new Val(sym.asInstanceOf[TermSymbol]));
+ } else if (sym.isValue && !sym.isMethod && !sym.isModule) {
+ if (!sym.hasFlag(JAVA)) {
+ Console.println("SYM: " + sym + " " + sym.fullNameString('.'));
+ Console.println("FLA: " + symtab.Flags.flagsToString(sym.flags));
+ }
+ assert(sym.hasFlag(JAVA));
+ return Some[Member](new Val(sym.asInstanceOf[TermSymbol]));
+ }
+ if (sym.isValue && !sym.isModule) {
+ val str = symtab.Flags.flagsToString(sym.flags);
+ assert(sym.isMethod);
+ return new Some[Member](new Def(sym.asInstanceOf[TermSymbol]));
+ }
+ if (sym.isAliasType || sym.isAbstractType) return Some(new AbstractType(sym));
+ if (sym.isClass) return Some(new NestedClass(sym.asInstanceOf[ClassSymbol]));
+ if (sym.isModule) return Some(new NestedObject(sym.asInstanceOf[ModuleSymbol]));
+ None;
+ }
+
+ }
+ case class Category(label : String)(g : Symbol => Boolean) {
+ val f = g;
+ def plural = label + "s";
+ }
+ val Constructors = new Category("Additional Constructor")(e => e.isConstructor && !e.isPrimaryConstructor) {
+ // override def plural = "Additional Constructors";
+ }
+ val Objects = Category("Object")(.isModule);
+ val Classes = new Category("Class")(.isClass) {
+ override def plural = "Classes";
+ }
+ val Values = new Category("Value")(e => (e.isValue) && e.hasFlag(symtab.Flags.ACCESSOR)) {
+ override def plural = "Values and Variables";
+ }
+ val Methods = Category("Method")(e => e.isValue && e.isMethod && !e.isConstructor && !e.hasFlag(symtab.Flags.ACCESSOR));
+ val Types = Category("Type")(e => e.isAliasType || e.isAbstractType);
+
+ val categories = Constructors :: Types :: Values :: Methods :: Objects :: Classes :: Nil;
+
+
+ import java.util.regex.Pattern
+ // patterns for standard tags with 1 and 2 arguments
+ private val pat1 = Pattern.compile(
+ "[ \t]*@(author|deprecated|pre|return|see|since|todo|version|ex|note)[ \t]*(.*)")
+ private val pat2 = Pattern.compile(
+ "[ \t]*@(exception|param|throws)[ \t]+(\\p{Graph}*)[ \t]*(.*)")
+
+ def sort[E <: Entity](entities : Iterable[E]) : Iterable[E] = {
+ val set = new jcl.TreeSet[E]()({eA : E => new Ordered[E] {
+ def compare(eB : E) : Int = {
+ if (eA eq eB) return 0;
+ (eA,eB) match {
+ case (eA:ClassOrObject,eB:ClassOrObject) =>
+ val diff = ModelExtractor.this.compare(eA.path, eB.path);
+ if (diff!= 0) return diff;
+ case _ =>
+ }
+ if (eA.getClass != eB.getClass) {
+ val diff = eA.getClass.getCanonicalName.compare(eB.getClass.getCanonicalName);
+ assert(diff != 0);
+ return diff;
+ }
+ if (!eA.sym0.isPackage) {
+ val diff = eA.sym0.nameString compare eB.sym0.nameString;
+ if (diff != 0) return diff;
+ }
+ val diff0 = eA.sym0.fullNameString compare eB.sym0.fullNameString;
+ assert(diff0 != 0);
+ return diff0;
+ }
+ }});
+ set addAll entities;
+ set;
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/doc/ModelFrames.scala b/src/compiler/scala/tools/nsc/doc/ModelFrames.scala
new file mode 100644
index 0000000000..917895e1ef
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/doc/ModelFrames.scala
@@ -0,0 +1,325 @@
+package scala.tools.nsc.doc;
+
+import compat.Platform.{EOL => LINE_SEPARATOR};
+import scala.xml._;
+import java.io.{File,FileWriter};
+import scala.collection.jcl;
+
+/** This class provides HTML document framing functionality.
+ *
+ * @author Sean McDirmid, Stephane Micheloud
+ */
+trait ModelFrames extends ModelExtractor {
+ import DocUtil._;
+ def outdir: String;
+ def windowTitle: String;
+ def documentTitle: String;
+ def contentFrame = "contentFrame";
+ def classesFrame = "classesFrame";
+ def modulesFrame = "modulesFrame";
+ protected val FILE_EXTENSION_HTML = ".html";
+ protected val NAME_SUFFIX_OBJECT = "$object";
+ protected val NAME_SUFFIX_PACKAGE = "$package";
+
+
+ def rootTitle = <div class="page-title"> Scala 2<br/>API Specification</div>;
+ def rootDesc = <p>This document is the API specification for Scala 2.</p>;
+
+ final def hasLink(sym : global.Symbol) : Boolean = {
+ if (sym == global.NoSymbol) false;
+ else if (hasLink0(sym)) true;
+ else {
+ hasLink(decode(sym.owner));
+ }
+ }
+ def hasLink0(sym : global.Symbol) : Boolean = true;
+
+ abstract class Frame extends UrlContext {
+ { // just save.
+ save(page(title, body, hasBody));
+ }
+ def path: String // relative to outdir
+ def relative: String = {
+ assert(path ne null)
+ var idx = 0
+ var ct = new StringBuilder
+ while (idx != -1) {
+ idx = path.indexOf('/', idx)
+ //System.err.println(path + " idx=" + idx)
+ ct.append(if (idx != -1) "../" else "")
+ idx = idx + (if (idx == -1) 0 else 1)
+ }
+ ct.toString
+ }
+ def save(nodes: NodeSeq) = {
+ val path0 = outdir + File.separator + path + FILE_EXTENSION_HTML
+ //if (settings.debug.value) inform("Writing XML nodes to " + path0)
+ val file = new File(path0)
+ val parent = file.getParentFile()
+ if (!parent.exists()) parent.mkdirs()
+ val writer = new FileWriter(file)
+ val str = dtype + LINE_SEPARATOR + nodes.toString()
+ writer.write(str, 0, str.length())
+ writer.close()
+ }
+ protected def body: NodeSeq;
+ protected def title: String;
+ protected def hasBody = true;
+
+ //def urlFor(entity : Entity, target : String) : NodeSeq;
+ def urlFor(entity : Entity) : String = {
+ val ret = this.urlFor(entity.sym);
+ assert(ret != null); ret;
+ }
+ def link(entity : Entity, target : String) = aref(urlFor(entity), target, entity.name);
+ protected def shortHeader(entity : Entity) : NodeSeq;
+ protected def longHeader(entity : Entity) : NodeSeq;
+ import global._;
+ import symtab.Flags;
+
+ def urlFor(sym: Symbol): String = sym match {
+ case sym : TypeSymbol if sym == definitions.AnyRefClass => urlFor0(sym,sym) + FILE_EXTENSION_HTML;
+ case psym : ModuleSymbol if psym.isPackage => urlFor0(sym,sym) + FILE_EXTENSION_HTML;
+ case sym if !hasLink(sym) => null;
+ case msym: ModuleSymbol => urlFor0(sym, sym) + FILE_EXTENSION_HTML
+ case csym: ClassSymbol => urlFor0(sym, sym) + FILE_EXTENSION_HTML
+ case _ =>
+ val cnt = urlFor(decode(sym.owner));
+ if (cnt == null) null else cnt + "#" + docName(sym)
+ }
+ def docName(sym: Symbol): String = {
+ def javaParams(paramTypes: List[Type]): String = {
+ def javaName(pt: Type): String = {
+ val s = pt.toString
+ val matVal = patVal.matcher(s)
+ if (matVal.matches) matVal.group(1).toLowerCase
+ else s.replaceAll("\\$", ".")
+ }
+ paramTypes.map(pt => javaName(pt)).mkString("(", ",", ")")
+ }
+ def scalaParams(paramTypes: List[Type]): String = {
+ def scalaName(pt: Type): String = pt.toString.replaceAll(" ", "")
+ paramTypes.map(pt => scalaName(pt)).mkString("(", ",", ")")
+ }
+ java.net.URLEncoder.encode(sym.nameString +
+ (sym.tpe match {
+ case MethodType(paramTypes, _) =>
+ if (sym hasFlag Flags.JAVA) javaParams(paramTypes)
+ else scalaParams(paramTypes)
+ case PolyType(_, MethodType(paramTypes, _)) =>
+ if (sym hasFlag Flags.JAVA) javaParams(paramTypes)
+ else scalaParams(paramTypes)
+ case _ => ""
+ }), encoding)
+ }
+ def urlFor0(sym: Symbol, orig: Symbol): String = {
+ (if (sym == NoSymbol) "XXX"
+ else if (sym.owner.isPackageClass) {
+ rootFor(sym) + sym.fullNameString('/');
+ } else urlFor0(decode(sym.owner), orig) + "." + Utility.escape(sym.nameString)) +
+ (sym match {
+ case msym: ModuleSymbol =>
+ if (msym hasFlag Flags.PACKAGE) NAME_SUFFIX_PACKAGE
+ else NAME_SUFFIX_OBJECT
+ case csym: ClassSymbol =>
+ if (csym.isModuleClass) {
+ if (csym hasFlag Flags.PACKAGE) NAME_SUFFIX_PACKAGE
+ else NAME_SUFFIX_OBJECT
+ }
+ else ""
+ case _ => ""
+ })
+ }
+ }
+ protected def rootFor(sym : global.Symbol) = "";
+
+ private val doctitle: NodeSeq =
+ <div class="doctitle-larger">{load(documentTitle)}</div>;
+
+ abstract class AllPackagesFrame extends Frame {
+ override val path = "modules";
+ override val title = "List of all packages";
+ def packages : Iterable[Package];
+ override def body: NodeSeq =
+ <div>
+ {doctitle}
+ <a href="all-classes.html" target={classesFrame} onclick="resetKind();">{"All objects and classes"}</a>
+ </div>
+ <div class="kinds">Packages</div>
+ <ul class="list">{sort(packages).mkXML("","\n","")(pkg => {
+ <li><a href={urlFor(pkg)} target={classesFrame} onclick="resetKind();">
+ {pkg.fullName('.')}</a></li>
+ })}
+ </ul>;
+ }
+ abstract class PackagesContentFrame extends Frame {
+ val path = "root-content";
+ val title = "All Packages";
+ def packages : Iterable[Package];
+ //def modules: TreeMap[String, ModuleClassSymbol]
+ def body: NodeSeq =
+ {rootTitle} ++ {rootDesc} ++ <hr/> ++
+ <table cellpadding="3" class="member" summary="">
+ <tr><td colspan="2" class="title">Package Summary</td></tr>
+ {sort(packages).mkXML("","\n","")(pkg => <tr><td class="signature">
+ <code>package
+ {aref(pkg.fullName('/') + "$content.html", "_self", pkg.fullName('.'))}
+ </code>
+ </td></tr>)}
+ </table>;
+ }
+
+ val classFrameKinds = Objects :: Classes :: Nil;
+ abstract class ListClassFrame extends Frame {
+ def classes: Iterable[ClassOrObject]
+ def navLabel: String
+ private def navPath = {
+ val p = path;
+ (if (p endsWith NAME_SUFFIX_PACKAGE)
+ p.substring(0, p.length() - NAME_SUFFIX_PACKAGE.length());
+ else p) + navSuffix;
+ }
+ protected def navSuffix = "$content.html";
+
+ def body: NodeSeq = {
+ val nav = if (navLabel == null) NodeSeq.Empty else
+ <table class="navigation" summary="">
+ <tr><td valign="top" class="navigation-links">
+ {aref(navPath, contentFrame, navLabel)}
+ </td></tr>
+ </table>;
+ val ids = new jcl.LinkedHashSet[String];
+ def idFor(kind: Category, t: Entity)(seq : NodeSeq): NodeSeq = {
+ val ch = t.listName.charAt(0);
+ val id = kind.plural + "_" + ch;
+ if (ids contains id) <li>{seq}</li>;
+ else {
+ ids += id;
+ <li id={id}>{seq}</li>
+ };
+ }
+ val body = <div>{classFrameKinds.mkXML("","\n","")(kind => {
+ val classes = sort(this.classes.filter(e => kind.f(e.sym)));
+ if (classes.isEmpty) NodeSeq.Empty; else
+ <div id={kind.plural} class="kinds">{Text(kind.plural)}</div>
+ <ul class="list">
+ {classes.mkXML("","\n","")(cls => {
+ idFor(kind, cls)(
+ aref(urlFor(cls), contentFrame, cls.listName) ++ optional(cls)
+ );
+ })}
+ </ul>;
+ })}</div>;
+ nav ++ body
+ }
+ def optional(cls : ClassOrObject) : NodeSeq = NodeSeq.Empty;
+ }
+ abstract class PackageContentFrame extends Frame {
+ override def path = pkg.fullName('/') + "$content";
+ override def title = "All classes and objects in " + pkg.fullName('.');
+ protected def pkg : Package;
+ protected def classes : Iterable[ClassOrObject];
+ def body: NodeSeq =
+ {rootTitle} ++ {rootDesc} ++ {classFrameKinds.mkXML("","\n","")(kind => {
+ val classes = sort(this.classes.filter(e => kind.f(e.sym) && e.isInstanceOf[TopLevel]));
+ if (classes.isEmpty) NodeSeq.Empty else
+ <table cellpadding="3" class="member" summary="">
+ <tr><td colspan="2" class="title">{kind.label} Summary</td></tr>
+ {classes.mkXML("","\n","")(shortHeader)}
+ </table>
+ })};
+ }
+ abstract class ClassContentFrame extends Frame {
+ def clazz: ClassOrObject;
+ def body: NodeSeq = <span>{navigation}{header0}{longHeader(clazz)}</span>;
+ final def path = urlFor0(clazz.sym, clazz.sym)
+ private def navigation: NodeSeq =
+ <table class="navigation" summary="">
+ <tr>
+ <td valign="top" class="navigation-links">
+ <table><tr>
+ </tr></table>
+ </td>
+ <td align="right" valign="top" style="white-space:nowrap;" rowspan="2">
+ {doctitle}
+ </td>
+ </tr>
+ <tr><td></td></tr>
+ </table>;
+ private def header0: NodeSeq = <span>
+ <div class="entity">
+ {Text(clazz.kind)}
+ <span class="entity">{Text(clazz.name)}</span> in
+ {aref(urlFor(decode(clazz.sym.owner)), "_self", decode(clazz.sym.owner).fullNameString('.'))}
+ </div><hr/>
+ </span>;
+ }
+ def longComment(cmnt : Comment) : NodeSeq;
+
+ val index =
+ <frameset cols="25%, 75%">
+ <frameset rows="50%, 28, 50%">
+ <frame src="modules.html" name={modulesFrame}></frame>
+ <frame src="nav-classes.html" name="navigationFrame"></frame>
+ <frame src="all-classes.html" name={classesFrame}></frame>
+ </frameset>
+ <frame src="root-content.html" name={contentFrame}></frame>
+ </frameset>;
+
+ val root = <b></b>;
+
+ abstract class RootFrame extends Frame {
+ def title = windowTitle
+ def body = index
+ def path = "index"
+ override def hasBody = false
+ }
+
+ val indexChars = 'A' :: 'B' :: 'C' :: 'D' :: 'E' :: 'G' :: 'I' :: 'L' :: 'M' :: 'P' :: 'R' :: 'T' :: 'V' :: 'X' :: Nil;
+
+ abstract class NavigationFrame extends Frame {
+ def title="navigation"
+ def path="nav-classes"
+ override def body0(hasBody: Boolean, nodes: NodeSeq): NodeSeq =
+ if (!hasBody) nodes
+ else <body style="margin:1px 0 0 1px; padding:1px 0 0 1px;">{nodes}</body>;
+ def body =
+ <form>
+ <select id="kinds" onchange="gotoKind()">
+ <option value="#Classes" selected="selected">Classes</option>
+ <option value="#Objects">Objects</option>
+ </select>
+ <span id="alphabet" style="font-family:Courier;word-spacing:-8px;">{
+ indexChars.mkXML("","\n","")(c => {
+ <a href={Unparsed("javascript:gotoName(\'" + c + "\')")}>{c}</a>
+ });
+ }
+ </span>
+ </form>
+ }
+
+ private val loader = getClass().getClassLoader()
+ def copyResources = {
+ import java.io._;
+ val rsrcdir = "scala/tools/nsc/doc/".replace('/', File.separatorChar)
+ for (val base <- List("style.css", "script.js")) {
+ try {
+ val in = loader.getResourceAsStream(rsrcdir + base)
+ val out = new FileOutputStream(new File(outdir + File.separator + base))
+ val buf = new Array[byte](1024)
+ var len = 0
+ while (len != -1) {
+ out.write(buf, 0, len)
+ len = in.read(buf)
+ }
+ in.close()
+ out.close()
+ } catch {
+ case _ =>
+ error("Resource file '" + base + "' not found")
+ }
+ }
+ }
+ private val patVal = java.util.regex.Pattern.compile(
+ "scala\\.(Byte|Boolean|Char|Double|Float|Int|Long|Short)")
+}
diff --git a/src/compiler/scala/tools/nsc/doc/ModelToXML.scala b/src/compiler/scala/tools/nsc/doc/ModelToXML.scala
new file mode 100644
index 0000000000..c43a215a22
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/doc/ModelToXML.scala
@@ -0,0 +1,237 @@
+package scala.tools.nsc.doc;
+import scala.xml._
+
+/** This class has functionality to format source code models as XML blocks.
+ *
+ * @author Sean McDirmid, Stephane Micheloud
+ */
+trait ModelToXML extends ModelExtractor {
+ import global._;
+ import DocUtil._;
+ // decode entity into XML.
+ type Frame;
+
+ protected def urlFor(sym : Symbol)(implicit frame : Frame) : String;
+ protected def anchor(sym : Symbol)(implicit frame : Frame) : NodeSeq;
+ def aref(href : String, label : String)(implicit frame : Frame) : NodeSeq;
+ def link(entity : Symbol)(implicit frame : Frame) : NodeSeq = {
+ val url = urlFor(entity);
+ // nothing to do but be verbose.
+ if (url == null) Text({
+ entity.owner.fullNameString('.') + '.' + entity.nameString;
+ }) else aref(url, entity.nameString);
+ }
+ def link(tpe : Type)(implicit frame : Frame) : NodeSeq = {
+ if (!tpe.typeArgs.isEmpty) {
+ if (definitions.isFunctionType(tpe)) {
+ val (args,r) = tpe.typeArgs.splitAt(tpe.typeArgs.length - 1);
+ args.mkXML("(", ", ", ")")(link) ++ Text(" => ") ++ link(r.head);
+ } else if (tpe.symbol == definitions.RepeatedParamClass) {
+ assert(tpe.typeArgs.length == 1);
+ link(tpe.typeArgs(0)) ++ Text("*");
+ } else if (tpe.symbol == definitions.ByNameParamClass) {
+ assert(tpe.typeArgs.length == 1);
+ Text("=> ") ++ link(tpe.typeArgs(0));
+ } else if (tpe.symbol.name.toString.startsWith("Tuple") &&
+ tpe.symbol.owner.name == nme.scala_.toTypeName) {
+ tpe.typeArgs.mkXML("(", ", ", ")")(link);
+ } else link(decode(tpe.symbol)) ++ tpe.typeArgs.surround("[", "]")(link);
+ } else tpe match {
+ case PolyType(tparams,result) =>
+ link(result) ++ tparams.surround("[", "]")(link);
+ case RefinedType(parents,_) =>
+ val parents1 =
+ if ((parents.length > 1) &&
+ (parents.head.symbol eq definitions.ObjectClass)) parents.tail;
+ else parents;
+ parents1.mkXML(Text(""), <code> with </code>, Text(""))(link);
+ case _ => {
+ link(decode(tpe.symbol));
+ }
+ }
+ }
+ private def printIf[T](what : Option[T], before : String, after : String)(f : T => NodeSeq) : NodeSeq = {
+ if (what.isEmpty) return Text("");
+ var seq : NodeSeq = Text(before);
+ seq = seq ++ f(what.get);
+ seq = seq ++ Text(after);
+ seq;
+ }
+ def bodyFor(entity : Entity)(implicit frame : Frame) : NodeSeq = {
+ var seq = {entity.typeParams.surround("[", "]")(e => {
+ Text(e.variance) ++ <em>{e.name}</em> ++
+ {printIf(e.hi, " <: ", "")(link)} ++
+ {printIf(e.lo, " >: ", "")(link)}
+ })} ++ printIf(entity.hi, " <: ", "")(link) ++
+ printIf(entity.lo, " >: ", "")(link);
+ {entity.params.foreach(xs => {
+ seq = seq ++ xs.mkXML("(", ", ", ")")(arg => {
+ var seq : NodeSeq = {
+ val str = arg.flagsString.trim;
+ if (str.length == 0) NodeSeq.Empty;
+ else <code>{Text(str)} </code>;
+ }
+ seq = seq ++ <em>{arg.name}</em>;
+ seq = seq ++ Text(" : ") ++ link(arg.resultType.get);
+ seq;
+ });
+ seq;
+ })};
+ seq ++ {printIf(entity.resultType, " : ", "")(tpe => link(tpe))}
+ }
+ def extendsFor(entity : Entity)(implicit frame : Frame) : NodeSeq = {
+ if (entity.parents.isEmpty) NodeSeq.Empty;
+ else <code> extends </code>++
+ entity.parents.mkXML(Text(""), <code> with </code>, Text(""))(link);
+ }
+ def parse(str: String): NodeSeq = {
+ new SpecialNode {
+ def label = "#PCDATA"
+ def toString(sb: StringBuilder): StringBuilder = {
+ sb.append(str.trim)
+ sb
+ }
+ }
+ }
+ def longHeader(entity : Entity)(implicit from : Frame) : NodeSeq = Group({
+ anchor(entity.sym) ++ <dl>
+ <dt>
+ {attrsFor(entity)}
+ <code>{Text(entity.flagsString)}</code>
+ <code>{Text(entity.kind)}</code>
+ <em>{entity.sym.nameString}</em>{bodyFor(entity)}
+ </dt>
+ <dd>{extendsFor(entity)}</dd>
+ </dl>;
+ } ++ {
+ val cmnt = entity.decodeComment;
+ if (cmnt.isEmpty) NodeSeq.Empty;
+ else longComment(cmnt.get);
+ } ++ (entity match {
+ case entity : ClassOrObject => {classBody(entity)};
+ case _ => NodeSeq.Empty;
+ }) ++ {
+ val overridden = entity.overridden;
+ if (!overridden.isEmpty) {
+ var seq : NodeSeq = Text("Overrides ");
+ seq = seq ++ overridden.mkXML("",", ", "")(sym => link(decode(sym.owner)) ++ Text(".") ++ link(sym));
+ seq;
+ } else NodeSeq.Empty;
+ } ++ <hr/>);
+
+ def longComment(cmnt : Comment) : NodeSeq = {
+ val attrs = <dl>{
+ var seq : NodeSeq = NodeSeq.Empty;
+ cmnt.decodeAttributes.foreach{
+ case (tag,xs) =>
+ seq = seq ++ <dt style="margin:10px 0 0 20px;">
+ {decodeTag(tag)}</dt> ++ {xs.flatMap{
+ case (option,body) => <dd>{
+ if (option == null) NodeSeq.Empty;
+ else decodeOption(tag, option);
+ }{parse(body)}</dd>
+ }}
+ };
+ seq;
+ }</dl>;
+ <span><dl><dd>{parse(cmnt.body)}</dd></dl>{attrs}</span>
+ }
+
+ def classBody(entity : ClassOrObject)(implicit from : Frame) : NodeSeq = <span>
+ {categories.mkXML("","\n","")(c => shortList(entity, c)) : NodeSeq}
+ {categories.mkXML("","\n","")(c => longList(entity, c)) : NodeSeq}
+ </span>;
+
+ def longList(entity : ClassOrObject,category : Category)(implicit from : Frame) : NodeSeq = {
+ val xs = entity.members(category);
+ if (!xs.elements.hasNext) return NodeSeq.Empty;
+ Group(
+ <table cellpadding="3" class="member-detail" summary="">
+ <tr><td class="title">{Text(category.label)} Details</td></tr>
+ </table>
+ <div>{xs.mkXML("","\n","")(m => longHeader(m))}</div>);
+ }
+
+ def shortList(entity : ClassOrObject, category : Category)(implicit from : Frame) : NodeSeq = {
+ val xs = entity.members(category);
+ var seq : NodeSeq = NodeSeq.Empty;
+ if (xs.elements.hasNext) {
+ // alphabetic
+ val set = new scala.collection.jcl.TreeSet[entity.Member]()(mA => new Ordered[entity.Member] {
+ def compare(mB : entity.Member) : Int = {
+ if (mA eq mB) return 0;
+ val diff = mA.name compare mB.name;
+ if (diff != 0) return diff;
+ val diff0 = mA.hashCode - mB.hashCode;
+ assert(diff0 != 0);
+ return diff0;
+ }
+ });
+ set addAll xs;
+ seq = seq ++ <table cellpadding="3" class="member" summary="">
+ <tr><td colspan="2" class="title">{Text(category.label + " Summary")}</td></tr>
+ {set.mkXML("","\n","")(mmbr => shortHeader(mmbr))}
+ </table>
+ }
+ // list inherited members...if any.
+ for (val (tpe,members) <- entity.inherited) {
+ val members0 = members.filter(m => category.f(m.sym));
+ if (!members0.isEmpty) seq = seq ++ <table cellpadding="3" class="inherited" summary="">
+ <tr><td colspan="2" class="title">
+ {Text(category.plural + " inherited from ") ++ link(tpe)}
+ </td></tr>
+ <tr><td colspan="2" class="signature">
+ {members0.mkXML((""), (", "), (""))(m => {
+ link(decode(m.sym)) ++
+ (if (m.sym.hasFlag(symtab.Flags.ABSTRACT) || m.sym.hasFlag(symtab.Flags.DEFERRED)) {
+ Text(" (abstract)");
+ } else NodeSeq.Empty);
+ })}
+ </td></tr>
+ </table>
+ }
+ seq;
+ }
+
+ protected def decodeOption(tag : String, string : String) : NodeSeq = <code>{Text(string + " - ")}</code>;
+ protected def decodeTag(tag : String) : String =
+ "" + Character.toUpperCase(tag.charAt(0)) + tag.substring(1);
+
+ def shortHeader(entity : Entity)(implicit from : Frame) : NodeSeq = {
+ <tr>
+ <td valign="top" class="modifiers">
+ <code>{Text(entity.flagsString)} {Text(entity.kind)}</code>
+ </td>
+ <td class="signature">
+ <em>{link(decode(entity.sym))}</em>{bodyFor(entity) ++ extendsFor(entity)}
+ {
+ val cmnt = entity.decodeComment;
+ if (cmnt.isEmpty) NodeSeq.Empty;
+ else <br>{parse(cmnt.get.body)}</br>;
+ }
+ </td>
+ </tr>
+ }
+ def attrsFor(entity : Entity)(implicit from : Frame) : NodeSeq = {
+ def attrFor(attr: AnnotationInfo[Constant]): Node = {
+ val buf = new StringBuilder
+ val AnnotationInfo(tpe, args, nvPairs) = attr
+ val name = link(decode(tpe.symbol))
+ if (!args.isEmpty)
+ buf.append(args.map(.escapedStringValue).mkString("(", ",", ")"))
+ if (!nvPairs.isEmpty)
+ for (val ((name, value), index) <- nvPairs.zipWithIndex) {
+ if (index > 0)
+ buf.append(", ")
+ buf.append(name).append(" = ").append(value)
+ }
+ Group(name ++ Text(buf.toString))
+ }
+ if (entity.sym.hasFlag(symtab.Flags.CASE)) NodeSeq.Empty;
+ else {
+ val sep = Text("@")
+ for (val attr <- entity.attributes)
+ yield Group({(sep ++ attrFor(attr) ++ <br/>)})
+ }
+ }
+}