5
\$\begingroup\$

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 before getValue 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.
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

Any idea on how to improve lookUp?

Use a concurrent cache. The point is, regardless how good or bad you lookup function is, nothing beats a cache-hit performance wise..

Is there a better way to do simple caching than using withDefault to mutate the underlying map?

getOrElseUpdate(key, value)
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.