diff --git a/core/src/main/scala/com/lightbend/paradox/ErrorContext.scala b/core/src/main/scala/com/lightbend/paradox/ErrorContext.scala index 0930f0ad..09ab984b 100644 --- a/core/src/main/scala/com/lightbend/paradox/ErrorContext.scala +++ b/core/src/main/scala/com/lightbend/paradox/ErrorContext.scala @@ -17,11 +17,11 @@ package com.lightbend.paradox import java.io.File - import com.lightbend.paradox.markdown.Page import org.pegdown.ast.Node import scala.collection.mutable.ListBuffer +import scala.io.BufferedSource trait ErrorContext { def apply(msg: String, index: Int): Unit @@ -46,7 +46,7 @@ trait ErrorContext { class ErrorCollector extends ErrorContext { private val errors = new ListBuffer[ParadoxError]() - private def addError(msg: String, page: Option[File], index: Option[Int]) = { + private def addError(msg: String, page: Option[File], index: Option[Int]): Unit = { errors.append(ParadoxError(msg, page, index)) } @@ -62,7 +62,7 @@ class ErrorCollector extends ErrorContext { def errorCount: Int = errors.toList.distinct.size - def logErrors(log: ParadoxLogger) = { + def logErrors(log: ParadoxLogger): Unit = { val totalErrors = errors.toList.distinct // First log general errors totalErrors.foreach { @@ -78,7 +78,13 @@ class ErrorCollector extends ErrorContext { .foreach { case (page, errors) => // Load contents of the page - val lines = scala.io.Source.fromFile(page)("UTF-8").getLines().toList + var source: BufferedSource = null + val lines = try { + source = scala.io.Source.fromFile(page)("UTF-8") + source.getLines().toList + } finally { + source.close() + } errors.sortBy(_.index.getOrElse(0)).foreach { case ParadoxError(error, _, Some(idx)) => val (_, lineNo, colNo, line) = lines.foldLeft((0, 0, 0, None: Option[String])) { (state, line) => diff --git a/core/src/main/scala/com/lightbend/paradox/ParadoxProcessor.scala b/core/src/main/scala/com/lightbend/paradox/ParadoxProcessor.scala index f32a119d..cacebc41 100644 --- a/core/src/main/scala/com/lightbend/paradox/ParadoxProcessor.scala +++ b/core/src/main/scala/com/lightbend/paradox/ParadoxProcessor.scala @@ -27,6 +27,7 @@ import org.jsoup.nodes.Document import org.pegdown.ast._ import java.net.SocketTimeoutException +import java.util import scala.annotation.tailrec import scala.collection.JavaConverters._ import scala.util.control.NonFatal @@ -162,7 +163,7 @@ class ParadoxProcessor(reader: Reader = new Reader, writer: Writer = new Writer, errorCollector.errorCount } - private def validateExternalLink(capturedLink: CapturedLink, retryCount: Int, errorContext: ErrorContext, logger: ParadoxLogger) = { + private def validateExternalLink(capturedLink: CapturedLink, retryCount: Int, errorContext: ErrorContext, logger: ParadoxLogger): Unit = { logger.info(s"Validating external link: ${capturedLink.link}") def reportError = reportErrorOnSources(errorContext, capturedLink.allSources)(_) @@ -181,7 +182,7 @@ class ParadoxProcessor(reader: Reader = new Reader, writer: Writer = new Writer, // but if you've already read the response body, that will throw an exception, and there's no way to check if // you've already tried to read the response body, so we can't do that in a finally block, we have to do it // explicitly every time we don't want to consume the stream. - def close() = response.bodyStream().close() + def close(): Unit = response.bodyStream().close() if (response.statusCode() / 100 == 3) { close() @@ -305,13 +306,17 @@ class ParadoxProcessor(reader: Reader = new Reader, writer: Writer = new Writer, private def createMetadata(outputDirectory: File, properties: Map[String, String]): (File, String) = { val metadataFilename = "paradox.json" val target = new File(outputDirectory, metadataFilename) - val osWriter = new OutputStreamWriter(new FileOutputStream(target), StandardCharsets.UTF_8) - osWriter.write( - s"""{ - | "name" : "${properties("project.name")}", - | "version" : "${properties("project.version")}" - |}""".stripMargin) - osWriter.close() + var osWriter: OutputStreamWriter = null + try { + osWriter = new OutputStreamWriter(new FileOutputStream(target), StandardCharsets.UTF_8) + osWriter.write( + s"""{ + | "name" : "${properties("project.name")}", + | "version" : "${properties("project.version")}" + |}""".stripMargin) + } finally { + osWriter.close() + } (target, metadataFilename) } @@ -323,8 +328,8 @@ class ParadoxProcessor(reader: Reader = new Reader, writer: Writer = new Writer, private val page = loc.tree.label - val getTitle = page.title - val getContent = + val getTitle: String = page.title + val getContent: String = try writer.writeContent(page.markdown, context) catch { case e: Throwable => @@ -333,23 +338,23 @@ class ParadoxProcessor(reader: Reader = new Reader, writer: Writer = new Writer, "" } - lazy val getBase = page.base - lazy val getHome = link(Some(loc.root)) - lazy val getPrev = link(loc.prev) - lazy val getSelf = link(Some(loc)) - lazy val getNext = link(loc.next) - lazy val getBreadcrumbs = writer.writeBreadcrumbs(Breadcrumbs.markdown(leadingBreadcrumbs, loc.path), context) - lazy val getNavigation = writer.writeNavigation(navToc.root(loc), context) - lazy val getGroups = Groups.html(groups) - lazy val hasSubheaders = page.headers.nonEmpty - lazy val getToc = writer.writeToc(pageToc.headers(loc), context) - lazy val getSource_url = githubLink(Some(loc)).getHtml - def getPath = page.path + lazy val getBase: String = page.base + lazy val getHome: PageTemplate.Link = link(Some(loc.root)) + lazy val getPrev: PageTemplate.Link = link(loc.prev) + lazy val getSelf: PageTemplate.Link = link(Some(loc)) + lazy val getNext: PageTemplate.Link = link(loc.next) + lazy val getBreadcrumbs: String = writer.writeBreadcrumbs(Breadcrumbs.markdown(leadingBreadcrumbs, loc.path), context) + lazy val getNavigation: String = writer.writeNavigation(navToc.root(loc), context) + lazy val getGroups: String = Groups.html(groups) + lazy val hasSubheaders: Boolean = page.headers.nonEmpty + lazy val getToc: String = writer.writeToc(pageToc.headers(loc), context) + lazy val getSource_url: String = githubLink(Some(loc)).getHtml + def getPath: String = page.path // So you can $page.properties.("project.name")$ - lazy val getProperties = context.properties.asJava + lazy val getProperties: util.Map[String, String] = context.properties.asJava // So you can $if(page.property_is.("project.license").("Apache-2.0"))$ - lazy val getProperty_is = context.properties.map { + lazy val getProperty_is: util.Map[String, util.Map[String, Boolean]] = context.properties.map { case (key, value) => (key -> Map(value -> true).asJava) }.asJava @@ -393,7 +398,7 @@ class ParadoxProcessor(reader: Reader = new Reader, writer: Writer = new Writer, lazy val getTitle: String = location.map(title).orNull lazy val isActive: Boolean = false - override def variables = context.properties + override def variables: Map[String, String] = context.properties private def href(location: Location[Page]): String = { try { @@ -401,7 +406,7 @@ class ParadoxProcessor(reader: Reader = new Reader, writer: Writer = new Writer, val rootPath = new File(".").getCanonicalFile.toString (treeUrl / Path.relativeLocalPath(rootPath, sourceFilePath)).toString } catch { - case e: Url.Error => null + case _: Url.Error => null } } @@ -442,14 +447,14 @@ class ParadoxProcessor(reader: Reader = new Reader, writer: Writer = new Writer, val labels = include.attributes.values("identifier").asScala val source = include.source match { case direct: DirectiveNode.Source.Direct => direct.value - case other => + case _ => error(s"Only explicit links are supported by the include directive, reference links are not", file, include) "" } val includeFile = SourceDirective.resolveFile("include", source, file, properties) val frontin = Frontin(includeFile) - val filterLabels = Directive.filterLabels("include", include.attributes, labels.toSeq, properties) - val (text, snippetLang) = Snippet(includeFile, labels.toSeq, filterLabels) + val filterLabels = Directive.filterLabels("include", include.attributes, labels, properties) + val (text, snippetLang) = Snippet(includeFile, labels, filterLabels) // I guess we could support multiple markup languages in future... if (snippetLang != "md" && snippetLang != "markdown") { error(s"Don't know how to include '*.$snippetLang' content.", file, include) diff --git a/core/src/main/scala/com/lightbend/paradox/markdown/Breadcrumbs.scala b/core/src/main/scala/com/lightbend/paradox/markdown/Breadcrumbs.scala index 257ebabe..fbc8c278 100644 --- a/core/src/main/scala/com/lightbend/paradox/markdown/Breadcrumbs.scala +++ b/core/src/main/scala/com/lightbend/paradox/markdown/Breadcrumbs.scala @@ -32,8 +32,8 @@ object Breadcrumbs { */ def markdown(leadingBreadcrumbs: List[(String, String)], locations: List[Location[Page]]): BulletListNode = locations match { - case current :: parents => crumbs(current.tree.label.base, current.tree.label.path, leadingBreadcrumbs, locations.reverse) - case _ => list(Nil) + case current :: _ => crumbs(current.tree.label.base, current.tree.label.path, leadingBreadcrumbs, locations.reverse) + case _ => list(Nil) } private def crumbs(base: String, active: String, leadingBreadcrumbs: List[(String, String)], locations: List[Location[Page]]): BulletListNode = { diff --git a/core/src/main/scala/com/lightbend/paradox/markdown/Directive.scala b/core/src/main/scala/com/lightbend/paradox/markdown/Directive.scala index 71d5e3cb..260cef06 100644 --- a/core/src/main/scala/com/lightbend/paradox/markdown/Directive.scala +++ b/core/src/main/scala/com/lightbend/paradox/markdown/Directive.scala @@ -27,12 +27,13 @@ import org.pegdown.plugins.ToHtmlSerializerPlugin import org.pegdown.{ Printer, ToHtmlSerializer } import scala.collection.JavaConverters._ +import scala.util.matching.Regex /** * Serialize directives, checking the name and format against registered directives. */ class DirectiveSerializer(directives: Seq[Directive]) extends ToHtmlSerializerPlugin { - val directiveMap = directives.flatMap(d => d.names.map(n => (n, d))).toMap + val directiveMap: Map[String, Directive] = directives.flatMap(d => d.names.map(n => (n, d))).toMap def visit(node: Node, visitor: Visitor, printer: Printer): Boolean = node match { case dnode: DirectiveNode => @@ -224,7 +225,7 @@ abstract class ExternalLinkDirective(names: String*) val link = super.resolvedSource(node, page) try { val resolvedLink = resolveLink(node: DirectiveNode, link).base.normalize.toString - if (resolvedLink startsWith (".../")) page.base + resolvedLink.drop(4) else resolvedLink + if (resolvedLink startsWith ".../") page.base + resolvedLink.drop(4) else resolvedLink } catch { case e @ Url.Error(reason) => ctx.logger.debug(e) @@ -276,10 +277,10 @@ abstract class ApiDocDirective(name: String) protected def resolveApiLink(link: String): Url - val defaultBaseUrl = PropertyUrl(name + ".base_url", variables.get) - val ApiDocProperty = raw"""$name\.(.*)\.base_url""".r - val baseUrls = variables.collect { - case (property @ ApiDocProperty(pkg), url) => (pkg, PropertyUrl(property, variables.get)) + val defaultBaseUrl: PropertyUrl = PropertyUrl(name + ".base_url", variables.get) + val ApiDocProperty: Regex = raw"""$name\.(.*)\.base_url""".r + val baseUrls: Map[String, PropertyUrl] = variables.collect { + case (property @ ApiDocProperty(pkg), _) => (pkg, PropertyUrl(property, variables.get)) } override protected def linkContents(node: DirectiveNode): Node = new CodeNode(node.contents) @@ -312,7 +313,7 @@ abstract class ApiDocDirective(name: String) object ApiDocDirective { /** This relies on the naming convention of packages being all-ascii-lowercase (which is rarely broken), numbers and underscore. */ - def packageDotsToSlash(s: String) = s.replaceAll("(\\b[a-z][a-z0-9_]*)\\.", "$1/") + def packageDotsToSlash(s: String): String = s.replaceAll("(\\b[a-z][a-z0-9_]*)\\.", "$1/") } case class ScaladocDirective(ctx: Writer.Context) @@ -320,7 +321,7 @@ case class ScaladocDirective(ctx: Writer.Context) protected def resolveApiLink(link: String): Url = { val levels = link.split("[.]") - val packages = (1 to levels.init.size).map(levels.take(_).mkString(".")) + val packages = (1 to levels.init.length).map(levels.take(_).mkString(".")) val baseUrl = packages.reverse.collectFirst(baseUrls).getOrElse(defaultBaseUrl).resolve() url(link, baseUrl) } @@ -338,15 +339,15 @@ case class ScaladocDirective(ctx: Writer.Context) object JavadocDirective { type LinkStyle = String - val LinkStyleFrames = "frames" - val LinkStyleDirect = "direct" + val LinkStyleFrames: LinkStyle = "frames" + val LinkStyleDirect: LinkStyle = "direct" // If Java 9+ we default to linking directly to the file, since it doesn't support frames, otherwise we default // to linking to the frames version with the class in the query parameter. Also, the version of everything up to // and including 8 starts with 1., so that's an easy way to tell if it's 9+ or not. - val jdkDependentLinkStyle = if (sys.props.get("java.specification.version").exists(_.startsWith("1."))) LinkStyleFrames else LinkStyleDirect + val jdkDependentLinkStyle: LinkStyle = if (sys.props.get("java.specification.version").exists(_.startsWith("1."))) LinkStyleFrames else LinkStyleDirect - final val JavadocLinkStyleProperty = raw"""javadoc\.(.*).link_style""".r + final val JavadocLinkStyleProperty: Regex = raw"""javadoc\.(.*).link_style""".r private[markdown] def url(link: String, baseUrl: Url, linkStyle: LinkStyle): Url = { val url = Url(link).base @@ -364,16 +365,16 @@ case class JavadocDirective(ctx: Writer.Context) import JavadocDirective._ - val rootLinkStyle = variables.getOrElse("javadoc.link_style", LinkStyleFrames) - val javaLinkStyle = variables.getOrElse("javadoc.link_style", jdkDependentLinkStyle) + val rootLinkStyle: String = variables.getOrElse("javadoc.link_style", LinkStyleFrames) + val javaLinkStyle: String = variables.getOrElse("javadoc.link_style", jdkDependentLinkStyle) val packageLinkStyle: Map[String, String] = Map("java" -> javaLinkStyle) ++ variables.collect { - case (property @ JavadocLinkStyleProperty(pkg), url) => (pkg, variables(property)) + case (property @ JavadocLinkStyleProperty(pkg), _) => (pkg, variables(property)) } override protected def resolveApiLink(link: String): Url = { val levels = link.split("[.]") - val packages = (1 to levels.init.size).map(levels.take(_).mkString(".")) + val packages = (1 to levels.init.length).map(levels.take(_).mkString(".")) val packagesDeepestFirst = packages.reverse val baseUrl = packagesDeepestFirst.collectFirst(baseUrls).getOrElse(defaultBaseUrl).resolve() val linkStyle = packagesDeepestFirst.collectFirst(packageLinkStyle).getOrElse(rootLinkStyle) @@ -383,21 +384,21 @@ case class JavadocDirective(ctx: Writer.Context) } object GitHubResolver { - val baseUrl = "github.base_url" - val githubDomain = "github.domain" + val baseUrl: String = "github.base_url" + val githubDomain: String = "github.domain" } trait GitHubResolver { def variables: Map[String, String] - lazy val githubDomain = variables.get(GitHubResolver.githubDomain).getOrElse("github.com") - val IssuesLink = """([^/]+/[^/]+)?#([0-9]+)""".r - val CommitLink = """(([^/]+/[^/]+)?@)?(\p{XDigit}{5,40})""".r - lazy val TreeUrl = (s"(.*$githubDomain/[^/]+/[^/]+/tree/[^/]+)").r - lazy val ProjectUrl = (s"(.*$githubDomain/[^/]+/[^/]+).*").r + lazy val githubDomain: String = variables.getOrElse(GitHubResolver.githubDomain, "github.com") + val IssuesLink: Regex = """([^/]+/[^/]+)?#([0-9]+)""".r + val CommitLink: Regex = """(([^/]+/[^/]+)?@)?(\p{XDigit}{5,40})""".r + lazy val TreeUrl: Regex = s"(.*$githubDomain/[^/]+/[^/]+/tree/[^/]+)".r + lazy val ProjectUrl: Regex = s"(.*$githubDomain/[^/]+/[^/]+).*".r - val baseUrl = PropertyUrl(GitHubResolver.baseUrl, variables.get) + val baseUrl: PropertyUrl = PropertyUrl(GitHubResolver.baseUrl, variables.get) protected def resolvePath(page: Page, source: String, labelOpt: Option[String]): Url = { val pathUrl = Url.parse(source, "path is invalid") @@ -409,7 +410,7 @@ trait GitHubResolver { val file = path match { case p if p.startsWith(Path.toUnixStyleRootPath(root.getAbsolutePath)) => new File(p) case p if p.startsWith("/") => new File(root, path.drop(1)) - case p => new File(page.file.getParentFile, path) + case _ => new File(page.file.getParentFile, path) } val labelFragment = for { @@ -427,7 +428,7 @@ trait GitHubResolver { (treeUrl / treePath) withFragment fragment } - protected def resolveProject(project: String) = { + protected def resolveProject(project: String): Url = { Option(project) match { case Some(path) => Url(s"https://$githubDomain") / path case None => projectUrl @@ -479,9 +480,9 @@ case class SnipDirective(ctx: Writer.Context) try { val labels = node.attributes.values("identifier").asScala val source = resolvedSource(node, page) - val filterLabels = Directive.filterLabels("snip", node.attributes, labels.toSeq, variables) + val filterLabels = Directive.filterLabels("snip", node.attributes, labels, variables) val file = resolveFile("snip", source, page, variables) - val (text, snippetLang) = Snippet(file, labels.toSeq, filterLabels) + val (text, snippetLang) = Snippet(file, labels, filterLabels) val lang = Option(node.attributes.value("type")).getOrElse(snippetLang) val group = Option(node.attributes.value("group")).getOrElse("") val sourceUrl = if (variables.contains(GitHubResolver.baseUrl) && variables.getOrElse(SnipDirective.showGithubLinks, "false") == "true") { @@ -502,8 +503,8 @@ case class SnipDirective(ctx: Writer.Context) object SnipDirective { - val showGithubLinks = "snip.github_link" - val buildBaseDir = "snip.build.base_dir" + val showGithubLinks: String = "snip.github_link" + val buildBaseDir: String = "snip.build.base_dir" } @@ -536,8 +537,8 @@ case class FiddleDirective(ctx: Writer.Context) val source = resolvedSource(node, page) val file = resolveFile("fiddle", source, page, variables) - val filterLabels = Directive.filterLabels("fiddle", node.attributes, labels.toSeq, variables) - val (code, _) = Snippet(file, labels.toSeq, filterLabels) + val filterLabels = Directive.filterLabels("fiddle", node.attributes, labels, variables) + val (code, _) = Snippet(file, labels, filterLabels) printer.println.print( s""" @@ -594,7 +595,7 @@ case class TocDirective(location: Location[Page], includeIndexes: List[Int]) ext */ case class VarDirective(variables: Map[String, String]) extends InlineDirective("var", "var:") { def render(node: DirectiveNode, visitor: Visitor, printer: Printer): Unit = { - new SpecialTextNode(variables.get(node.label).getOrElse(s"<${node.label}>")).accept(visitor) + new SpecialTextNode(variables.getOrElse(node.label, s"<${node.label}>")).accept(visitor) } } @@ -696,14 +697,14 @@ case class InlineGroupDirective(groups: Seq[String]) extends InlineDirective(gro * Dependency directive. */ case class DependencyDirective(ctx: Writer.Context) extends LeafBlockDirective("dependency") { - val BomVersionSymbols = "bomVersionSymbols" - val VersionSymbol = "symbol" - val VersionValue = "value" - val ScalaBinaryVersionVar = "scala.binary.version" - - val variables = ctx.properties - val ScalaVersion = variables.get("scala.version") - val ScalaBinaryVersion = variables.get(ScalaBinaryVersionVar) + val BomVersionSymbols: String = "bomVersionSymbols" + val VersionSymbol: String = "symbol" + val VersionValue: String = "value" + val ScalaBinaryVersionVar: String = "scala.binary.version" + + val variables: Map[String, String] = ctx.properties + val ScalaVersion: Option[String] = variables.get("scala.version") + val ScalaBinaryVersion: Option[String] = variables.get(ScalaBinaryVersionVar) def render(node: DirectiveNode, visitor: Visitor, printer: Printer): Unit = { node.contentsNode.getChildren.asScala.headOption match { diff --git a/core/src/main/scala/com/lightbend/paradox/markdown/Frontin.scala b/core/src/main/scala/com/lightbend/paradox/markdown/Frontin.scala index 23dc5a3f..43aa6773 100644 --- a/core/src/main/scala/com/lightbend/paradox/markdown/Frontin.scala +++ b/core/src/main/scala/com/lightbend/paradox/markdown/Frontin.scala @@ -18,6 +18,7 @@ package com.lightbend.paradox.markdown import java.io.{ File, StringReader } import collection.JavaConverters._ +import scala.io.BufferedSource case class Frontin(header: Map[String, String], body: String) @@ -28,9 +29,13 @@ object Frontin { (str.trim == separator) && (str startsWith separator) def apply(file: File): Frontin = { - val source = scala.io.Source.fromFile(file)("UTF-8") - val lines = source.getLines.mkString("\n") - source.close() + var source: BufferedSource = null + val lines = try { + source = scala.io.Source.fromFile(file)("UTF-8") + source.getLines.mkString("\n") + } finally { + source.close() + } apply(lines) } diff --git a/core/src/main/scala/com/lightbend/paradox/markdown/Groups.scala b/core/src/main/scala/com/lightbend/paradox/markdown/Groups.scala index a4ccaa9c..0565a13c 100644 --- a/core/src/main/scala/com/lightbend/paradox/markdown/Groups.scala +++ b/core/src/main/scala/com/lightbend/paradox/markdown/Groups.scala @@ -17,7 +17,7 @@ package com.lightbend.paradox.markdown object Groups { - def html(supergroups: Map[String, Seq[String]]) = { + def html(supergroups: Map[String, Seq[String]]): String = { supergroups.map { case (supergroup, groups) => s"""