diff --git a/rudder-core/src/main/scala/com/normation/rudder/repository/xml/GitArchiverImpl.scala b/rudder-core/src/main/scala/com/normation/rudder/repository/xml/GitArchiverImpl.scala index 390389f0c02..9a096cc5b9e 100644 --- a/rudder-core/src/main/scala/com/normation/rudder/repository/xml/GitArchiverImpl.scala +++ b/rudder-core/src/main/scala/com/normation/rudder/repository/xml/GitArchiverImpl.scala @@ -63,7 +63,6 @@ import org.eclipse.jgit.lib.PersonIdent import net.liftweb.common._ import net.liftweb.util.Helpers.tryo import scala.xml.Elem -import scala.xml.PrettyPrinter import scala.collection.mutable.Buffer import scala.collection.JavaConversions._ import com.normation.cfclerk.domain.TechniqueId @@ -81,7 +80,7 @@ class GitRuleArchiverImpl( , override val gitRootDirectory : File , ruleSerialisation : RuleSerialisation , ruleRootDir : String //relative path ! - , override val xmlPrettyPrinter : PrettyPrinter + , override val xmlPrettyPrinter : RudderPrettyPrinter , override val gitModificationRepository : GitModificationRepository , override val encoding : String = "UTF-8" ) extends @@ -189,7 +188,7 @@ class GitActiveTechniqueCategoryArchiverImpl( , override val gitRootDirectory : File , activeTechniqueCategorySerialisation: ActiveTechniqueCategorySerialisation , techniqueLibraryRootDir : String //relative path ! - , override val xmlPrettyPrinter : PrettyPrinter + , override val xmlPrettyPrinter : RudderPrettyPrinter , override val gitModificationRepository : GitModificationRepository , override val encoding : String = "UTF-8" , serializedCategoryName : String = "category.xml" @@ -316,7 +315,7 @@ trait ActiveTechniqueModificationCallback { class UpdatePiOnActiveTechniqueEvent( gitDirectiveArchiver: GitDirectiveArchiver , techniqeRepository : TechniqueRepository - , directiveRepository : RoDirectiveRepository + , directiveRepository : RoDirectiveRepository ) extends ActiveTechniqueModificationCallback with Loggable { override val uptModificationCallbackName = "Update PI on UPT events" @@ -367,7 +366,7 @@ class GitActiveTechniqueArchiverImpl( , override val gitRootDirectory : File , activeTechniqueSerialisation : ActiveTechniqueSerialisation , techniqueLibraryRootDir : String //relative path ! - , override val xmlPrettyPrinter : PrettyPrinter + , override val xmlPrettyPrinter : RudderPrettyPrinter , override val gitModificationRepository : GitModificationRepository , override val encoding : String = "UTF-8" , val uptModificationCallback : Buffer[ActiveTechniqueModificationCallback] = Buffer() @@ -479,7 +478,7 @@ class GitDirectiveArchiverImpl( , override val gitRootDirectory : File , directiveSerialisation : DirectiveSerialisation , techniqueLibraryRootDir : String //relative path ! - , override val xmlPrettyPrinter : PrettyPrinter + , override val xmlPrettyPrinter : RudderPrettyPrinter , override val gitModificationRepository : GitModificationRepository , override val encoding : String = "UTF-8" ) extends GitDirectiveArchiver with Loggable with GitArchiverUtils with BuildCategoryPathName[ActiveTechniqueCategoryId] { @@ -565,24 +564,24 @@ class GitDirectiveArchiverImpl( * * Basically, we directly map the category tree to file-system directories, * with the root category being the file denoted by "nodeGroupLibrary - */ + */ class GitNodeGroupArchiverImpl( override val gitRepo : GitRepositoryProvider , override val gitRootDirectory : File , nodeGroupSerialisation : NodeGroupSerialisation , nodeGroupCategorySerialisation: NodeGroupCategorySerialisation , groupLibraryRootDir : String //relative path ! - , override val xmlPrettyPrinter : PrettyPrinter + , override val xmlPrettyPrinter : RudderPrettyPrinter , override val gitModificationRepository : GitModificationRepository , override val encoding : String = "UTF-8" , serializedCategoryName : String = "category.xml" ) extends - GitNodeGroupArchiver with + GitNodeGroupArchiver with Loggable with GitArchiverUtils with BuildCategoryPathName[NodeGroupCategoryId] with GitArchiverFullCommitUtils { - + override lazy val relativePath = groupLibraryRootDir override def getCategoryName(categoryId:NodeGroupCategoryId) = categoryId.value diff --git a/rudder-core/src/main/scala/com/normation/rudder/repository/xml/GitArchiverUtils.scala b/rudder-core/src/main/scala/com/normation/rudder/repository/xml/GitArchiverUtils.scala index 72e20e2fb01..21cddf4883e 100644 --- a/rudder-core/src/main/scala/com/normation/rudder/repository/xml/GitArchiverUtils.scala +++ b/rudder-core/src/main/scala/com/normation/rudder/repository/xml/GitArchiverUtils.scala @@ -37,7 +37,6 @@ package com.normation.rudder.repository.xml import java.io.File import scala.collection.JavaConversions.asScalaSet import scala.xml.Elem -import scala.xml.PrettyPrinter import org.apache.commons.io.FileUtils import org.eclipse.jgit.api.Git import org.joda.time.format.ISODateTimeFormat @@ -64,7 +63,7 @@ trait GitArchiverUtils extends Loggable { def gitRepo : GitRepositoryProvider def gitRootDirectory : File def relativePath : String - def xmlPrettyPrinter : PrettyPrinter + def xmlPrettyPrinter : RudderPrettyPrinter def encoding : String def gitModificationRepository : GitModificationRepository diff --git a/rudder-core/src/main/scala/com/normation/rudder/repository/xml/RudderPrettyPrinter.scala b/rudder-core/src/main/scala/com/normation/rudder/repository/xml/RudderPrettyPrinter.scala new file mode 100644 index 00000000000..30d329fb494 --- /dev/null +++ b/rudder-core/src/main/scala/com/normation/rudder/repository/xml/RudderPrettyPrinter.scala @@ -0,0 +1,276 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +/* + * License notice: + * This class is a copy of scala.xml.PrettyPrinter class of Scala 2.10.3. + * + * The only changes in it are: + * - the method doPreserve always returning true + * to workaround https://issues.scala-lang.org/browse/SI-4303 + * and correct http://www.rudder-project.org/redmine/issues/4476 + * - the copy&paste of scala.xml.Utility.sbToString because of its + * visibility modifier. + * + * + * The class name is altered so that the good dependency is used + * every where in Rudder project. + */ + +package com.normation.rudder.repository.xml + + + + +class RudderPrettyPrinter(width: Int, step: Int) { + + import scala.xml._ + + def sbToString(f: (StringBuilder) => Unit): String = { + val sb = new StringBuilder + f(sb) + sb.toString + } + + class BrokenException() extends java.lang.Exception + + class Item + case object Break extends Item { + override def toString() = "\\" + } + case class Box(col: Int, s: String) extends Item + case class Para(s: String) extends Item + + protected var items: List[Item] = Nil + + protected var cur = 0 + + protected def reset() = { + cur = 0 + items = Nil + } + + /** Try to cut at whitespace. + */ + protected def cut(s: String, ind: Int): List[Item] = { + val tmp = width - cur + if (s.length <= tmp) + return List(Box(ind, s)) + val sb = new StringBuilder() + var i = s indexOf ' ' + if (i > tmp || i == -1) throw new BrokenException() // cannot break + + var last: List[Int] = Nil + while (i != -1 && i < tmp) { + last = i::last + i = s.indexOf(' ', i+1) + } + var res: List[Item] = Nil + while (Nil != last) try { + val b = Box(ind, s.substring(0, last.head)) + cur = ind + res = b :: Break :: cut(s.substring(last.head, s.length), ind) + // backtrack + last = last.tail + } catch { + case _:BrokenException => last = last.tail + } + throw new BrokenException() + } + + /** Try to make indented box, if possible, else para. + */ + protected def makeBox(ind: Int, s: String) = + if (cur + s.length > width) { // fits in this line + items ::= Box(ind, s) + cur += s.length + } + else try cut(s, ind) foreach (items ::= _) // break it up + catch { case _: BrokenException => makePara(ind, s) } // give up, para + + // dont respect indent in para, but afterwards + protected def makePara(ind: Int, s: String) = { + items = Break::Para(s)::Break::items + cur = ind + } + + // respect indent + protected def makeBreak() = { // using wrapping here... + items = Break :: items + cur = 0 + } + + protected def leafTag(n: Node) = { + def mkLeaf(sb: StringBuilder) { + sb append '<' + n nameToString sb + n.attributes buildString sb + sb append "/>" + } + sbToString(mkLeaf) + } + + protected def startTag(n: Node, pscope: NamespaceBinding): (String, Int) = { + var i = 0 + def mkStart(sb: StringBuilder) { + sb append '<' + n nameToString sb + i = sb.length + 1 + n.attributes buildString sb + n.scope.buildString(sb, pscope) + sb append '>' + } + (sbToString(mkStart), i) + } + + protected def endTag(n: Node) = { + def mkEnd(sb: StringBuilder) { + sb append "' + } + sbToString(mkEnd) + } + + protected def childrenAreLeaves(n: Node): Boolean = { + def isLeaf(l: Node) = l match { + case _:Atom[_] | _:Comment | _:EntityRef | _:ProcInstr => true + case _ => false + } + n.child forall isLeaf + } + + protected def fits(test: String) = + test.length < width - cur + + private def doPreserve(node: Node) = true + + protected def traverse(node: Node, pscope: NamespaceBinding, ind: Int): Unit = node match { + + case Text(s) if s.trim() == "" => + ; + case _:Atom[_] | _:Comment | _:EntityRef | _:ProcInstr => + makeBox( ind, node.toString.trim() ) + case g @ Group(xs) => + traverse(xs.iterator, pscope, ind) + case _ => + val test = { + val sb = new StringBuilder() + Utility.serialize(node, pscope, sb, false) + if (doPreserve(node)) sb.toString + else TextBuffer.fromString(sb.toString).toText(0).data + } + if (childrenAreLeaves(node) && fits(test)) { + makeBox(ind, test) + } else { + val (stg, len2) = startTag(node, pscope) + val etg = endTag(node) + if (stg.length < width - cur) { // start tag fits + makeBox(ind, stg) + makeBreak() + traverse(node.child.iterator, node.scope, ind + step) + makeBox(ind, etg) + } else if (len2 < width - cur) { + // + if (!lastwasbreak) sb.append('\n') // on windows: \r\n ? + lastwasbreak = true + cur = 0 +// while (cur < last) { +// sb append ' ' +// cur += 1 +// } + + case Box(i, s) => + lastwasbreak = false + while (cur < i) { + sb append ' ' + cur += 1 + } + sb.append(s) + case Para( s ) => + lastwasbreak = false + sb append s + } + } + + // public convenience methods + + /** Returns a formatted string containing well-formed XML with + * given namespace to prefix mapping. + * + * @param n the node to be serialized + * @param pscope the namespace to prefix mapping + * @return the formatted string + */ + def format(n: Node, pscope: NamespaceBinding = null): String = + sbToString(format(n, pscope, _)) + + /** Returns a formatted string containing well-formed XML. + * + * @param nodes the sequence of nodes to be serialized + * @param pscope the namespace to prefix mapping + */ + def formatNodes(nodes: Seq[Node], pscope: NamespaceBinding = null): String = + sbToString(formatNodes(nodes, pscope, _)) + + /** Appends a formatted string containing well-formed XML with + * the given namespace to prefix mapping to the given stringbuffer. + * + * @param nodes the nodes to be serialized + * @param pscope the namespace to prefix mapping + * @param sb the string buffer to which to append to + */ + def formatNodes(nodes: Seq[Node], pscope: NamespaceBinding, sb: StringBuilder): Unit = + nodes foreach (n => sb append format(n, pscope)) +} \ No newline at end of file diff --git a/rudder-web/src/main/scala/bootstrap/liftweb/AppConfig.scala b/rudder-web/src/main/scala/bootstrap/liftweb/AppConfig.scala index a15ebb83745..243fc8652e6 100644 --- a/rudder-web/src/main/scala/bootstrap/liftweb/AppConfig.scala +++ b/rudder-web/src/main/scala/bootstrap/liftweb/AppConfig.scala @@ -80,7 +80,6 @@ import com.normation.rudder.services.servers.NodeConfigurationChangeDetectServic import org.apache.commons.dbcp.BasicDataSource import com.normation.rudder.services.eventlog.HistorizationServiceImpl import com.normation.rudder.services.policies.DeployOnTechniqueCallback -import scala.xml.PrettyPrinter import com.normation.rudder.services.marshalling._ import com.normation.utils.ScalaLock import com.normation.rudder.web.rest._ @@ -241,7 +240,7 @@ object RudderConfig extends Loggable { val licensesConfiguration = "licenses.xml" val logentries = "logentries.xml" - val prettyPrinter = new PrettyPrinter(120, 2) + val prettyPrinter = new RudderPrettyPrinter(120, 2) val userLibraryDirectoryName = "directives" val groupLibraryDirectoryName = "groups" val rulesDirectoryName = "rules"