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

scalariform.lexer.XmlLexer.scala Maven / Gradle / Ivy

The newest version!
package scalariform.lexer

import scala.annotation._
import scalariform.lexer.CharConstants.SU
import scalariform.lexer.ScalaLexer._
import scalariform.lexer.Tokens._
import scalariform.utils.Utils
import scala.PartialFunction.cond

/**
 * Lexer implementation for XML literals and patterns
 */
trait XmlLexer { self: ScalaLexer ⇒

  private def tagMode = xmlMode.isTagMode

  private def tagMode_=(isTagMode: Boolean) {
    xmlMode.isTagMode = isTagMode
  }

  private def moreXmlToCome: Boolean = {
    // Amount of scanning ahead required is unlimited, so we can't use the circular buffer:
    val newReader = reader.copy.buffered
    while (newReader.head != SU && isSpace(newReader.head))
      newReader.next()
    cond(newReader.take(2).toList) {
      case List(c1, c2) ⇒ c1 == '<' && isNameStart(c2)
    }
  }

  protected def fetchXmlToken() {
    (ch: @switch) match {
      case '<' ⇒ {
        if (ch(1) == '/') {
          nextChar()
          nextChar()
          token(XML_END_OPEN)
          xmlMode.isTagMode = true
          xmlMode.tagState = InEndTag
        } else if (ch(1) == '!') {
          if (ch(2) == '-') {
            getXmlComment()
            if (xmlMode.nestingLevel == 0 && !moreXmlToCome)
              popMode()
          } else if (ch(2) == '[') {
            getXmlCDATA()
            if (xmlMode.nestingLevel == 0 && !moreXmlToCome)
              popMode()
          } else {
            if (forgiveErrors) {
              munch("') {
            nextChar()
            nextChar()
            token(XML_EMPTY_CLOSE)
            xmlMode.isTagMode = false
            xmlMode.tagState = Normal
            if (xmlMode.nestingLevel == 0 && !moreXmlToCome)
              popMode()
          } else
            getXmlCharData()
        } else
          getXmlCharData()
      }
      case '>' ⇒
        if (tagMode) {
          nextChar()
          token(XML_TAG_CLOSE)
          xmlMode.isTagMode = false
          xmlMode.tagState match {
            case InStartTag ⇒ xmlMode.nestTag()
            case InEndTag ⇒ {
              val nestingLevel = xmlMode.unnestTag()
              if (nestingLevel == 0 && !moreXmlToCome)
                popMode()
            }
            case Normal ⇒ throw new AssertionError("shouldn't reach here")
          }
        } else
          getXmlCharData()

      case '=' ⇒
        if (tagMode) {
          nextChar()
          token(XML_ATTR_EQ)
        } else {
          getXmlCharData()
        }
      case '\'' ⇒
        if (tagMode) {
          getXmlAttributeValue('\'')
        } else {
          getXmlCharData()
        }
      case '"' ⇒
        if (tagMode) {
          getXmlAttributeValue('"')
        } else {
          getXmlCharData()
        }
      case '{' ⇒
        if (ch(1) != '{')
          switchToScalaModeAndFetchToken
        else
          getXmlCharData() // TODO: tagMode?
      case SU ⇒ token(EOF)
      case _ ⇒
        if (tagMode && isNameStart(ch)) {
          getXmlName()
        } else if (tagMode && isSpace(ch)) {
          getXmlSpace()
        } else {
          getXmlCharData()
          // throw new ScalaLexerException("illegal character in xml: " + Character.valueOf(ch))
          // TODO
        }

    }
  }

  private def getXmlCDATA() {
    munch("")) {
        munch("]]>")
        continue = false
      } else if (ch == SU)
        if (forgiveErrors)
          continue = false
        else
          throw new ScalaLexerException("Malformed XML CDATA")
      else
        nextChar()
    }
    token(XML_CDATA)
  }

  private def getXmlComment() {
    munch("