summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/doc/DocDriver.scala
blob: f20afd405e0dd15df59f25d9491d9e1674b32e47 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
/* NSC -- new Scala compiler
 * Copyright 2007-2008 LAMP/EPFL
 * @author  Sean McDirmid
 */
// $Id$

package scala.tools.nsc.doc

import java.util.zip.ZipFile

import scala.collection.jcl
import symtab.Flags._
import scala.xml._

/**
 *  @author Sean McDirmid
 */
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]) {
    assert(global.definitions != null);

    def g(pkg: Package, clazz: ClassOrObject) {
      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 (p <- allClasses; d <- p._2) {
      symbols += d.sym
      for (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 (p <- allClasses; 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 ((pkg0,classes0) <- allClasses) {
      new ListClassFrame with Frame {
        def title =
          "List of classes and objects in package " + pkg0.fullName('.')
        def classes = classes0;
        def path = pkgPath(pkg0.sym) + NAME_SUFFIX_PACKAGE;
        def navLabel = pkg0.fullName('.');
      }
      new PackageContentFrame with Frame {
        def classes = classes0;
        def pkg = pkg0;
      }
      for (clazz0 <- classes0) {
        new ClassContentFrame with Frame {
          def clazz = clazz0;
          def title =
            clazz0.kind + " " + clazz0.name + " in " + (clazz0.sym.owner.fullNameString('.'));
        }
      }
    }
    for (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: 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);
  }
}