All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.fusesource.scalate.wikitext.Snippets.scala Maven / Gradle / Ivy

There is a newer version: 1.10.1
Show newest version
/**
 * Copyright (C) 2009-2011 the original author or authors.
 * See the notice.md file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.fusesource.scalate.wikitext

import collection.mutable.HashMap
import org.eclipse.mylyn.wikitext.core.parser.DocumentBuilder.BlockType
import org.eclipse.mylyn.internal.wikitext.confluence.core.block.ParameterizedBlock
import io.Source
import java.io.File
import java.util.regex.{ Pattern, Matcher }
import org.eclipse.mylyn.wikitext.core.parser.{ DocumentBuilder, Attributes }
import org.fusesource.scalate.util.Log

/**
 * Helper class to access file containing snippets of code:
 * - on the local file system
 * - using a full URL
 * - using a URL that starts with a predefined prefix
 */
object Snippets {

  val log = Log(getClass); import log._
  var errorHandler: (SnippetBlock, Throwable) => Unit = logError

  var failOnError = false

  /**
   * By default lets use Pygmentize if its installed to style snippets
   * unless its explicitly disabled via user
   */
  var usePygmentize: Boolean = Pygmentize.isInstalled

  val prefixes = HashMap[String, String]()

  /**
   * Add a prefix definition to make it easier to access snippets.
   * E.g. if you add 'activemq' -> 'http://svn.apache.org/repos/asf/activemq/trunk'
   *      you can use 'activemq/pom.xml' as a shorthand notation for 'http://svn.apache.org/repos/asf/activemq/trunk/pom.xml"
   *
   *
   * @param prefix the prefix
   * @param location the full URL that should be used for the prefix
   */
  def addPrefix(prefix: String, location: String): Unit = prefixes.put(prefix, location)

  private[wikitext] def handlePrefix(location: String): String = {
    val prefix = location.substring(0, location.indexOf("/"))
    if (prefixes.contains(prefix)) {
      location.replaceFirst(prefix, prefixes(prefix))
    } else {
      location
    }
  }

  /**
   * Get the snippet file contents
   */
  def getSource(location: String): Source = {
    val url = handlePrefix(location)

    def isUrl = url.indexOf(':') > 0

    var file = new File(location)
    if (!file.exists) {
      file = new File(url)
    }
    debug("for location: " + location + " using prefix: " + url)
    if (file.exists || !isUrl) {
      Source.fromFile(file, "UTF-8")
    } else {
      Source.fromURL(url, "UTF-8")
    }
  }

  protected def logError(snippet: SnippetBlock, e: Throwable): Unit = {
    error(e, "Failed to generate snippet: " + snippet.url + ". " + e)
    if (failOnError) {
      throw e
    }
  }
}
import Snippets.log._

/**
 * Represents a {snippet} block in the wiki markup
 */
class SnippetBlock extends ParameterizedBlock {

  var pattern: Pattern = Pattern.compile("\\s*\\{snippet(?::([^\\}]*))?\\}(.*)")
  var matcher: Matcher = null
  var lang: Option[String] = None
  var url: String = _
  var id: Option[String] = None
  var pygmentize: Boolean = Snippets.usePygmentize

  lazy val handler: SnippetHandler = {
    if (pygmentize) {
      val block = new PygmentsBlock
      block.setState(state)
      block.setParser(parser)
      block.setOption("lang", language)
      PygmentizeSnippetHandler(block)
    } else {
      DefaultSnippetHandler(builder, language)
    }
  }

  override def canStart(line: String, lineOffset: Int) = {
    matcher = pattern.matcher(line)
    if (lineOffset > 0) {
      matcher.region(lineOffset, line.length)
    }
    matcher.matches()
  }

  override def processLineContent(line: String, offset: Int) = {
    setOptions(matcher.group(1))
    val end = matcher.start(2)

    handler.begin()
    try {
      for (snippetLine <- getSnippet) {
        handler.addLine(snippetLine)
      }
    } catch {
      case e: Exception => Snippets.errorHandler(this, e)
    }
    handler.done()

    if (end < line.length) {
      state.setLineSegmentEndOffset(end)
    }
    setClosed(true)
    if (end == line.length) { -1 } else { end }
  }

  /**
   * Extract the snippet from the Source file
   */
  def getSnippet: Iterator[String] = {
    val lines = Snippets.getSource(url).getLines()
    id match {
      case None => lines
      case Some(snippet) => lines.dropWhile(!_.contains("START SNIPPET: " + snippet))
        .takeWhile(!_.contains("END SNIPPET: " + snippet))
        .drop(1)
    }
  }

  override def setOption(key: String, value: String) = {
    key match {
      case "id" => id = Some(value)
      case "url" => url = value
      case "lang" => lang = Some(value)
      case "pygmentize" => pygmentize = value.toBoolean
      case n => warn("Ignored snippet attribute %s on %s", n, this)
    }
  }

  /**
   * Get the configured language or determine the language from the file extension
   */
  def language = {
    lang match {
      case None => extension(url)
      case Some(lang) => lang
    }
  }

  /**
   * Extract the file extension from the URL
   */
  def extension(url: String) = {
    if (url.contains(".")) {
      url.split('.').last
    } else {
      ""
    }
  }

  override def toString = "{snippet:url=" + url + "}"
}

/**
 * Trait to define a {snippet} handler
 */
trait SnippetHandler {

  def begin(): Unit
  def addLine(line: String): Unit
  def done(): Unit

}

/**
 * Default handler for the {snippet} code (renders a 
 */
case class DefaultSnippetHandler(
  val builder: DocumentBuilder,
  val language: String) extends SnippetHandler {

  def begin() = {
    builder.beginBlock(BlockType.DIV, cssClass("snippet"))
    builder.beginBlock(BlockType.PREFORMATTED, cssClass(language))
    builder.characters("\n")
  }

  def addLine(line: String) = {
    builder.characters(line + "\n")
  }

  def done() = {
    builder.endBlock(); // 
builder.endBlock(); //
} /** * Create attributes instance containing the CSS class */ def cssClass(cssClass: String) = { val attributes = new Attributes attributes.setCssClass(cssClass) attributes } } /** * Uses pygmentize to handles syntax coloring for the {snippet}'s code */ case class PygmentizeSnippetHandler(val block: PygmentsBlock) extends SnippetHandler { def begin() = block.beginBlock def addLine(line: String) = block.handleBlockContent(line) def done() = block.endBlock }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy