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

org.beangle.doc.html.dom.StyleSheets.scala Maven / Gradle / Ivy

There is a newer version: 0.4.5
Show newest version
/*
 * Copyright (C) 2005, The Beangle Software.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see .
 */

package org.beangle.doc.html.dom

import org.beangle.commons.lang.Strings
import org.beangle.doc.html.dom.ClassStyle.*

class StyleSheets(val styles: Seq[ClassStyle]) {

  def matches(node: DomNode): Seq[ClassStyle] = {
    styles.filter(_.matches(node))
  }

}

object ClassStyle {

  def buildChain(name: String): ClassEntry = {
    val n = name.replaceAll("(\\s)+>(\\s)+", ">")
    val parts = Strings.split(n, ' ')
    var ancestor: Option[ClassEntry] = None
    var start: ClassEntry = null

    parts foreach { part =>
      var isParent = false
      var i = 0
      Strings.split(part, '>') foreach { p =>
        isParent = i > 0
        val tagName = Strings.substringBefore(p, ".").trim
        val className = Strings.substringAfter(p, ".").trim
        val entry = ClassEntry(if Strings.isBlank(tagName) then "*" else tagName,
          if Strings.isBlank(className) then "*" else className,
          if ancestor.isEmpty then None else Some(ClassAncestor(isParent, ancestor.get))
        )
        ancestor = Some(entry)
        start = entry
        i += 1
      }
    }
    start
  }
}

class ClassStyle(name: String, properties: Map[String, String]) extends Style(properties) {

  val chain: ClassEntry = buildChain(name)

  override def toString: String = {
    val ps = properties.map(x => s"${x._1}:${x._2}").toSeq.sorted.mkString(";")
    s"${name} {${ps}}"
  }

  def matches(node: DomNode): Boolean = {
    if (chain.ancestor.isEmpty) {
      if (name.charAt(0) == '#') {
        node.attributes.get("id").contains(name.substring(1))
      } else if (name.charAt(0) == '.') {
        node.classNames.contains(name.substring(1))
      } else {
        matchByChain(node)
      }
    } else {
      matchByChain(node)
    }
  }

  private def matchByChain(node: DomNode): Boolean = {
    var curClass = chain
    var curNode = node
    var matched = matches(curClass, curNode)
    while (matched && curClass != null) {
      curClass.ancestor match
        case None => curClass = null
        case Some(ancestor) =>
          curClass = ancestor.ancestor
          val ancestorNode = findAncestorNode(curClass, curNode, ancestor.isParent)
          if (ancestorNode.isEmpty) {
            matched = false
          } else {
            curNode = ancestorNode.get
            matched = matches(curClass, curNode)
          }
    }
    matched
  }

  /** 查找指定clazz的上级节点
   *
   * @param clz
   * @param start
   * @param isParent 是否是直接上级
   * @return
   */
  private def findAncestorNode(clz: ClassEntry, start: DomNode, isParent: Boolean): Option[DomNode] = {
    if (isParent) {
      start.parent
    } else {
      var cur = start.parent.orNull
      var parentNode: DomNode = null
      while (null != cur && null == parentNode) {
        if (matches(clz, cur)) {
          parentNode = cur
        }
        cur = cur.parent.orNull
      }
      Option(parentNode)
    }
  }

  private def matches(curClass: ClassEntry, curNode: DomNode): Boolean = {
    (curClass.tagName == "*" || curClass.tagName == curNode.name) &&
      (curClass.className == "*" || curNode.classNames.contains(curClass.className))
  }

  def toString(indentation: Int): String = {
    val iden1 = " " * (indentation)
    val iden2 = " " * (indentation + 2)
    val ps = properties.map(x => s"${iden2}${x._1}:${x._2}").toSeq.sorted.mkString(";\n")
    s"${iden1}${name} {\n${ps}\n${iden1}}"
  }
}

case class ClassEntry(tagName: String, className: String, ancestor: Option[ClassAncestor])

case class ClassAncestor(isParent: Boolean, ancestor: ClassEntry)




© 2015 - 2024 Weber Informatics LLC | Privacy Policy