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

.circumflex-xml.2.3.source-code.holder.scala Maven / Gradle / Ivy

package ru.circumflex
package xml

import core._, cache._
import java.lang.reflect.Modifier
import org.apache.commons.io.{IOUtils, FileUtils}
import java.io._
import collection.mutable.ListBuffer

trait Holder {

  def elemName: String

  var parent: Option[ElemHolder] = None

  def accept(it: TagIterator): Boolean = it.current.name == elemName

  def toXml: String = writeXml(0)

  def writeXml(indent: Int): String

  def readXml(parent: Option[ElemHolder], it: TagIterator): this.type = {
    this.parent = parent
    readXml(it)
  }

  def readXml(it: TagIterator): this.type

  def loadFrom(file: File): this.type = {
    xml.parse(file) { it =>
      XML_LOG.trace("Loading " + getClass.getSimpleName + " instance from " + file.getCanonicalPath)
      it.next()
      readXml(it)
    }
    this
  }

  def loadFrom(is: InputStream): this.type = {
    xml.parseStream(is) { it =>
      XML_LOG.trace("Loading " + getClass.getSimpleName + " instance from stream.")
      it.next()
      readXml(it)
    }
    this
  }

  def loadString(string: String): this.type = {
    xml.parseString(string) { it =>
      XML_LOG.trace("Loading " + getClass.getSimpleName + " instance from string.")
      it.next()
      readXml(it)
    }
    this
  }

  def saveTo(file: File): this.type = {
    XML_LOG.trace("Saving " + getClass.getSimpleName +
        " instance to " + file.getCanonicalPath)
    FileUtils.writeStringToFile(file, "\n" + toXml, "UTF-8")
    this
  }

  def saveTo(out: OutputStream): this.type = {
    XML_LOG.trace("Saving " + getClass.getSimpleName + " instance to stream.")
    IOUtils.write("\n" + toXml, out, "UTF-8")
    this
  }

}

trait ValHolder[T]
    extends Holder
    with Container[T]
    with Equals {

  def canEqual(that: Any) = that match {
    case holder: ValHolder[T] =>
      holder.elemName == this.elemName && holder.parent == this.parent
    case _ => false
  }

  override def equals(that: Any) = that match {
    case holder: ValHolder[T] =>
      this.canEqual(holder) && holder.value == this.value
    case _ => false
  }

  override def hashCode = elemName.hashCode * 31 + value.hashCode

  override def toString = value.map(_.toString).getOrElse("")
}

class AttrHolder(val elemName: String)
    extends ValHolder[String] {

  addSetter(s => escapeHtml(s))

  def writeXml(indent: Int): String = value.map(s =>
    " " + elemName + "=\"" + s + "\"").getOrElse("")

  def readXml(it: TagIterator): this.type = {
    it.attr(elemName).map(a => set(a))
    this
  }
}

trait ElemHolder extends Holder {

  def findHolders[H <: Holder](holderClass: Class[H]): Seq[H] =
    findHolders(this.getClass, holderClass)

  def attr(elemName: String): AttrHolder = new AttrHolder(elemName)

  private def findHolders[H <: Holder](containerClass: Class[_], holderClass: Class[H]): Seq[H] = {
    val parentHolders: Seq[H] =
      if (containerClass != classOf[Any])
        findHolders(containerClass.getSuperclass, holderClass) else Nil
    val thisHolders: Seq[H] = containerClass.getDeclaredFields
        .filter(f => !Modifier.isTransient(f.getModifiers))
        .flatMap(f => try {
      Some(containerClass.getMethod(f.getName))
    } catch {
      case e: Exception => None
    })
        .filter(m => holderClass.isAssignableFrom(m.getReturnType))
        .map(m => m.invoke(this).asInstanceOf[H])
    parentHolders ++ thisHolders
  }

  def findAttrs: Seq[AttrHolder] = findHolders(classOf[AttrHolder])

  def writeAttrs(indent: Int) =
    findAttrs.map(_.writeXml(indent)).filter(_ != "")
        .mkString("\n " + (" " * elemName.length) + ("  " * indent))

}

trait TextHolder
    extends ValHolder[String]
    with ElemHolder {

  addSetter(s => escapeHtml(s))

  def readXml(it: TagIterator): this.type = {
    if (accept(it)) {
      findAttrs.foreach(a => a.readXml(it))
      set(it.text)
    }
    this
  }

  def writeXml(indent: Int): String = ("  " * indent) + "<" + elemName +
      writeAttrs(indent) + ">"
}

trait ListHolder[T <: ElemHolder]
    extends ElemHolder {

  val children = new ListBuffer[T]

  def add(child: T) {
    child.parent = Some(this)
    children += child
  }

  def setChildren(children: Seq[T]) {
    this.children.clear()
    this.children ++= children
  }

  def read: String => T

  def readXml(it: TagIterator): this.type = {
    if (accept(it)) {
      children.clear()
      findAttrs.foreach(a => a.readXml(it))
      it.takeWhile(_ != EndTag(elemName)) foreach {
        case t: StartTag =>
          try {
            val child = read(t.name)
            child.readXml(it)
            add(child)
          } catch {
            case e: MatchError =>
              XML_LOG.warn("Unexpected element <" + t.toString + ">. " +
                  "Please check `readChild` or XML input.")
          }
        case _ =>
      }
    }
    this
  }

  def writeXml(indent: Int): String = {
    var result = ("  " * indent) + "<" + elemName + writeAttrs(indent)
    val chldrn = children
    if (children.size == 0) result += "/>"
    else result += ">\n" + children.map(_.writeXml(indent + 1)).mkString("\n") +
        "\n" + ("  " * indent) + ""
    result
  }

  def delete(child: T) {
    children -= child
  }

}

trait StructHolder extends ElemHolder {

  def findElems: Seq[ElemHolder] = findHolders(classOf[ElemHolder])

  def text(n: String): TextHolder = new TextHolder {
    def elemName = n
  }

  def writeXml(indent: Int): String = {
    var result = ("  " * indent) + "<" + elemName + writeAttrs(indent)
    val children = findElems
    if (children.size == 0) result += "/>"
    else result += ">\n" + children.map(_.writeXml(indent + 1)).mkString("\n") +
        "\n" + ("  " * indent) + ""
    result
  }

  def readXml(it: TagIterator): this.type = {
    if (accept(it)) {
      findAttrs.foreach(a => a.readXml(it))
      val elems = findElems
      it.takeWhile(_ != EndTag(elemName)) foreach {
        case StartTag(n) =>
          elems.find(_.elemName == n).map(_.readXml(Some(this), it))
        case _ => // should only occur if XML contains redundant tags
      }
    }
    this
  }

}

trait XmlFile extends ElemHolder with Cached {

  def descriptorFile: File

  def exists = descriptorFile.isFile

  def expired = exists && (descriptorFile.lastModified > createdAt.getTime)

  def save() {
    saveTo(descriptorFile)
    invalidate()
  }

  def load(): this.type = {
    reclaim()
    loadFrom(descriptorFile)
    this
  }

}

trait XmlSingleton extends XmlFile {

  invalidate()

  def touch() {
    if (!isValid) load()
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy