I'm investigating some feature for the ScalaDoc tool, which would allow library writers to link to documentation created by third party tools like JavaDoc.
My idea is to have some (XML) configuration which specifies which package prefixes belong to which vendor and how to create a valid URL from the the class name, stored in the XML as regular expressions and format strings.
I only need one public method getLink(entity: String): Option[String]
which either returns Some(validURL)
or None
, if the package name is not supported by the given configuration. Everything else can be changed as much as I want if it improves the code.
Consider this code:
object ExternalReferences2 {
import java.util.regex._
import collection.mutable._
private object Mapping {
def fromXml(mapping: scala.xml.NodeSeq) = {
new Mapping(mapping \ "vendor" text, mapping \ "match" text, mapping \ "format" text)
}
}
private case class Mapping(vendor: String, matches: String, format: String) {
private val pattern = Pattern.compile(matches)
private var currentMatcher: Matcher = null
def hasValue(entity: String) = {
currentMatcher = pattern.matcher(entity);
currentMatcher.matches
}
def getValue = {
val range = 0 until currentMatcher.groupCount()
val groups = range
.map (currentMatcher.group(_))
.filterNot (_ == null)
.map (_.replace('.', '/'))
format.format(groups: _*)
}
}
private val config =
<external-links>
<mapping>
<vendor>OpenJDK</vendor>
<match>{ """^(javax?|sunw?|com.sun|org\.(ietf\.jgss|omg|w3c\.dom|xml\.sax))(\.[^.]+)+$""" }</match>
<format>{ "http://download.oracle.com/javase/7/docs/api/%s.html" }</format>
</mapping>
</external-links>
private def lookUp(entity: String) =
(config \ "mapping").view
.map(m => Mapping.fromXml(m))
.find(_.hasValue(entity))
.map(_.getValue)
private val links: Map[String, Option[String]] = Map[String, Option[String]]()
.withDefault(entry => {val result = lookUp(entry); links += ((entry, result)); result})
def getLink(entity: String) = links(entity)
}
How can I improve that code?
- The necessity to call
hasValue
beforegetValue
is pretty bad, but I don't want to create the matcher twice. - Which environment variables do I have to use so that the tool can pick up the configuration from the current directory or from an arbitrary place with a command line switch?
- Is there a better way to do simple caching than using
withDefault
to mutate the underlying map? - Any idea on how to improve
lookUp
? - Are there any ideas for better names? I'm not happy with
Mapping
,Link
,Reference
... things are all over the place.