summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/doc/DefaultDocDriver.scala
blob: adc48f6943e06f7397f284be2f4720d493452074 (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
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
/* NSC -- new Scala compiler
 * Copyright 2007-2009 LAMP/EPFL
 * @author  Sean McDirmid
 */
// $Id$

package scala.tools.nsc.doc

import scala.collection.mutable
import java.util.zip.ZipFile

import symtab.Flags._
import scala.xml._

/**
 *  @author Sean McDirmid
 */
abstract class DefaultDocDriver extends DocDriver with ModelFrames with ModelToXML {
  import global._
  import definitions.{AnyClass, AnyRefClass}

  lazy val additions = new mutable.LinkedHashSet[Symbol]
  lazy val additions0 = new 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 {}
  }

  /** Add all top-level entities in ModelAdditions to allClasses */
  def addAdditionsToClasses() {
    additions0.init
    for (sym <- additions) {
      val packSym = sym.enclosingPackage
      if (packSym != NoSymbol) {
        val pack = Package(packSym)
        if (!(allClasses contains pack)) {
          // don't emit an addition unless its package
          // is already being scaladoced
        } else {
          val addition: Option[ClassOrObject] =
            if (sym.isClass)
              Some(new TopLevelClass(sym))
            else if (sym.isModule)
              Some(new TopLevelObject(sym))
            else if (sym == definitions.AnyRefClass) {
              // AnyRef is the only top-level type alias, so handle
              // it specially instead of introducing general support for
              // top-level aliases
              Some(new TopLevelClass(sym))
            }
            else
              None

          addition match {
            case None =>
              //println("skipping: " + sym) //DEBUG
            case Some(addition) =>
              allClasses(pack) += addition
          }
        }
      } else {
        //println("no package found for: "+sym) //DEBUG
      }
    }
  }

  def process(units: Iterator[CompilationUnit]) {

    assert(global.definitions != null)

    def g(pkg: Package, clazz: ClassOrObject) {
      if (isAccessible(clazz.sym)) {
        allClasses(pkg) += clazz
        clazz.decls.map(_._2).foreach {
          case clazz : ClassOrObject => g(pkg, clazz)
          case _ =>
        }
      }
    }
    def f(pkg: Package, tree: Tree) {
      if (tree != EmptyTree && tree.hasSymbol) {
        val sym = tree.symbol
        if (sym != NoSymbol && !sym.hasFlag(symtab.Flags.PRIVATE)) tree match {
          case tree : PackageDef =>
            val pkg1 = new Package(sym.asInstanceOf[ModuleSymbol])
            tree.stats.foreach(stat => f(pkg1, stat))
          case tree : ClassDef =>
            assert(pkg != null)
            g(pkg, new TopLevelClass(sym.asInstanceOf[ClassSymbol]))
          case tree : ModuleDef =>
            assert(pkg != null)
            g(pkg, new TopLevelObject(sym.asInstanceOf[ModuleSymbol]))
          case _ =>
        }
      }
    }
    units.foreach(unit => f(null, unit.body))
    addAdditionsToClasses()

    for (p <- allClasses; d <- p._2) {
      symbols += d.sym
      for (pp <- d.sym.tpe.parents) subClasses(pp.typeSymbol) += d
    }
    copyResources
    lazy val packages0 = sort(allClasses.keys)
    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 mutable.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))(NodeSeq.builderFactory)
      }

    }
    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('.'));
        }
      }
    }
    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) = DefaultDocDriver.this.longHeader(entity)(this)
    def shortHeader(entity : Entity) = DefaultDocDriver.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
    }): NodeSeq)++super.classBody(entity))//(NodeSeq.builderFactory)

  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 mutable.LinkedHashMap[String,String];
  roots("classes") = "http://java.sun.com/j2se/1.5.0/docs/api";
  roots("rt") = roots("classes");
  private val SCALA_API_ROOT = "http://www.scala-lang.org/docu/files/api/";
  roots("scala-library") = SCALA_API_ROOT;

  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") name.substring(0, name.length - (".jar").length)
    else null
  }

  // <code>{Text(string + " - ")}</code>;
  override def hasLink0(sym: Symbol): Boolean = {
    if (sym == NoSymbol) return false;
    if (sym == AnyRefClass) {
      // AnyRefClass is a type alias, so the following logic
      // does not work.  AnyClass should have a link in
      // the same cases as AnyRefClass, so test it instead.
      return hasLink(AnyClass)
    }
    if (super.hasLink0(sym) && symbols.contains(sym))
      return true;
    if (SyntheticClasses contains sym)
      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 _ =>
    }
    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 mutable.LinkedHashSet[Symbol]

  object allClasses extends mutable.LinkedHashMap[Package, mutable.LinkedHashSet[ClassOrObject]] {
    override def default(pkg: Package): mutable.LinkedHashSet[ClassOrObject] = {
      object ret extends mutable.LinkedHashSet[ClassOrObject]
      this(pkg) = ret
      ret
    }
  }

  object subClasses extends mutable.LinkedHashMap[Symbol, mutable.LinkedHashSet[ClassOrObject]] {
    override def default(key: Symbol) = {
      val ret = new mutable.LinkedHashSet[ClassOrObject]
      this(key) = ret
      ret
    }
  }

  override def rootFor(sym: Symbol): String = {
    assert(sym != NoSymbol)
    if (sym == definitions.AnyRefClass) {
      // AnyRefClass is a type alias, so the following logic
      // does not work.  AnyClass should have the same root,
      // so use it instead.
      return rootFor(definitions.AnyClass)
    }
    if (sym.toplevelClass == NoSymbol) return super.rootFor(sym)
    if (symbols.contains(sym.toplevelClass)) return super.rootFor(sym)
    if (SyntheticClasses contains sym)
      return SCALA_API_ROOT
    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 _ =>
    }
    super.rootFor(sym)
  }
}