/* NSC -- new Scala compiler * Copyright 2007-2011 LAMP/EPFL * @author David Bernard, Manohar Jonnalagedda */ package scala.tools.nsc package doc package html import model._ import comment._ import xml.{XML, NodeSeq} import xml.dtd.{DocType, PublicID} import scala.collection._ import scala.reflect.NameTransformer import java.nio.channels.Channels import java.io.{FileOutputStream, File} /** An html page that is part of a Scaladoc site. * @author David Bernard * @author Gilles Dubochet */ abstract class HtmlPage { thisPage => /** The path of this page, relative to the API site. `path.tail` is a list of folder names leading to this page (from * closest package to one-above-root package), `path.head` is the file name of this page. Note that `path` has a * length of at least one. */ def path: List[String] /** The title of this page. */ protected def title: String /** Additional header elements (links, scripts, meta tags, etc.) required for this page. */ protected def headers: NodeSeq /** The body of this page. */ protected def body: NodeSeq /** Writes this page as a file. The file's location is relative to the generator's site root, and the encoding is * also defined by the generator. * @param generator The generator that is writing this page. */ def writeFor(site: HtmlFactory): Unit = { val doctype = DocType("html", PublicID("-//W3C//DTD XHTML 1.1//EN", "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"), Nil) val html = { title } \n") w.write( doctype.toString + "\n") w.write(xml.Xhtml.toXhtml(html)) } finally { w.close() fos.close() } //XML.save(pageFile.getPath, html, site.encoding, xmlDecl = false, doctype = doctype) } def templateToPath(tpl: TemplateEntity): List[String] = { def doName(tpl: TemplateEntity): String = NameTransformer.encode(tpl.name) + (if (tpl.isObject) "$" else "") def downPacks(pack: Package): List[String] = if (pack.isRootPackage) Nil else (doName(pack) :: downPacks(pack.inTemplate)) def downInner(nme: String, tpl: TemplateEntity): (String, Package) = { tpl.inTemplate match { case inPkg: Package => (nme + ".html", inPkg) case inTpl => downInner(doName(inTpl) + "$" + nme, inTpl) } } val (file, pack) = tpl match { case p: Package => ("package.html", p) case _ => downInner(doName(tpl), tpl) } file :: downPacks(pack) } /** A relative link from this page to some destination class entity. * @param destEntity The class or object entity that the link will point to. */ def relativeLinkTo(destClass: TemplateEntity): String = relativeLinkTo(templateToPath(destClass)) /** A relative link from this page to some destination page in the Scaladoc site. * @param destPage The page that the link will point to. */ def relativeLinkTo(destPage: HtmlPage): String = { relativeLinkTo(destPage.path) } /** A relative link from this page to some destination path. * @param destPath The path that the link will point to. */ def relativeLinkTo(destPath: List[String]): String = { def relativize(from: List[String], to: List[String]): List[String] = (from, to) match { case (f :: fs, t :: ts) if (f == t) => // both paths are identical to that point relativize(fs, ts) case (fss, tss) => List.fill(fss.length - 1)("..") ::: tss } relativize(thisPage.path.reverse, destPath.reverse).mkString("/") } def absoluteLinkTo(destPath: List[String]): String = { destPath.reverse.mkString("/") } /** Transforms an optional comment into an styled HTML tree representing its body if it is defined, or into an empty * node sequence if it is not. */ def commentToHtml(comment: Option[Comment]): NodeSeq = (comment map (commentToHtml(_))) getOrElse NodeSeq.Empty /** Transforms a comment into an styled HTML tree representing its body. */ def commentToHtml(comment: Comment): NodeSeq = bodyToHtml(comment.body) def bodyToHtml(body: Body): NodeSeq = body.blocks flatMap (blockToHtml(_)) def blockToHtml(block: Block): NodeSeq = block match { case Title(in, 1) =>

{ inlineToHtml(in) }

case Title(in, 2) =>

{ inlineToHtml(in) }

case Title(in, 3) =>
{ inlineToHtml(in) }
case Title(in, _) =>
{ inlineToHtml(in) }
case Paragraph(in) =>

{ inlineToHtml(in) }

case Code(data) =>
{ xml.Text(data) }
case UnorderedList(items) => case OrderedList(items, listStyle) =>
    { listItemsToHtml(items) }
case DefinitionList(items) =>
{items map { case (t, d) =>
{ inlineToHtml(t) }
{ blockToHtml(d) }
} }
case HorizontalRule() =>
} def listItemsToHtml(items: Seq[Block]) = items.foldLeft(xml.NodeSeq.Empty){ (xmlList, item) => item match { case OrderedList(_, _) | UnorderedList(_) => // html requires sub ULs to be put into the last LI xmlList.init ++
  • { xmlList.last.child ++ blockToHtml(item) }
  • case Paragraph(inline) => xmlList :+
  • { inlineToHtml(inline) }
  • // LIs are blocks, no need to use Ps case block => xmlList :+
  • { blockToHtml(block) }
  • } } def inlineToHtml(inl: Inline): NodeSeq = inl match { case Chain(items) => items flatMap (inlineToHtml(_)) case Italic(in) => { inlineToHtml(in) } case Bold(in) => { inlineToHtml(in) } case Underline(in) => { inlineToHtml(in) } case Superscript(in) => { inlineToHtml(in) } case Subscript(in) => { inlineToHtml(in) } case Link(raw, title) => { inlineToHtml(title) } case EntityLink(entity) => templateToHtml(entity) case Monospace(text) => { xml.Text(text) } case Text(text) => xml.Text(text) case Summary(in) => inlineToHtml(in) case HtmlTag(tag) => xml.Unparsed(tag) } def typeToHtml(tpe: model.TypeEntity, hasLinks: Boolean): NodeSeq = { val string = tpe.name def toLinksOut(inPos: Int, starts: List[Int]): NodeSeq = { if (starts.isEmpty && (inPos == string.length)) NodeSeq.Empty else if (starts.isEmpty) xml.Text(string.slice(inPos, string.length)) else if (inPos == starts.head) toLinksIn(inPos, starts) else { xml.Text(string.slice(inPos, starts.head)) ++ toLinksIn(starts.head, starts) } } def toLinksIn(inPos: Int, starts: List[Int]): NodeSeq = { val (tpl, width) = tpe.refEntity(inPos) (tpl match { case dtpl:DocTemplateEntity if hasLinks => { string.slice(inPos, inPos + width) } case tpl => { string.slice(inPos, inPos + width) } }) ++ toLinksOut(inPos + width, starts.tail) } if (hasLinks) toLinksOut(0, tpe.refEntity.keySet.toList) else xml.Text(string) } def typesToHtml(tpess: List[model.TypeEntity], hasLinks: Boolean, sep: NodeSeq): NodeSeq = tpess match { case Nil => NodeSeq.Empty case tpe :: Nil => typeToHtml(tpe, hasLinks) case tpe :: tpes => typeToHtml(tpe, hasLinks) ++ sep ++ typesToHtml(tpes, hasLinks, sep) } /** Returns the HTML code that represents the template in `tpl` as a hyperlinked name. */ def templateToHtml(tpl: TemplateEntity) = tpl match { case dTpl: DocTemplateEntity => { dTpl.name } case ndTpl: NoDocTemplate => xml.Text(ndTpl.name) } /** Returns the HTML code that represents the templates in `tpls` as a list of hyperlinked names. */ def templatesToHtml(tplss: List[TemplateEntity], sep: NodeSeq): NodeSeq = tplss match { case Nil => NodeSeq.Empty case tpl :: Nil => templateToHtml(tpl) case tpl :: tpls => templateToHtml(tpl) ++ sep ++ templatesToHtml(tpls, sep) } def docEntityKindToString(ety: DocTemplateEntity) = if (ety.isTrait) "trait" else if (ety.isCaseClass) "case class" else if (ety.isClass) "class" else if (ety.isObject) "object" else if (ety.isPackage) "package" else "class" // FIXME: an entity *should* fall into one of the above categories, but AnyRef is somehow not /** Returns the _big image name corresponding to the DocTemplate Entity (upper left icon) */ def docEntityKindToBigImage(ety: DocTemplateEntity) = if (ety.isTrait) "trait_big.png" else if (ety.isClass) "class_big.png" else if (ety.isObject) "object_big.png" else if (ety.isPackage) "package_big.png" else "class_big.png" // FIXME: an entity *should* fall into one of the above categories, but AnyRef is somehow not }