Skip to content

Commit d9229fc

Browse files
committed
Port scala-xml to v3 core.
1 parent 895706f commit d9229fc

File tree

10 files changed

+231
-334
lines changed

10 files changed

+231
-334
lines changed

gradle/check-codecoverage.gradle

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ jacocoTestCoverageVerification {
1818
element = 'CLASS'
1919
includes = ['com.github.simy4.xpath.*']
2020
excludes = [
21-
'com.github.simy4.xpath.scala.XmlBuilder.BuilderPartiallyApplied',
22-
'com.github.simy4.xpath.scala.impl.*',
23-
'com.github.simy4.xpath.scala.implicits.*',
21+
'com.github.simy4.xpath.scala.implicits.',
22+
'com.github.simy4.xpath.scala.navigator.Attribute.',
23+
'com.github.simy4.xpath.scala.navigator.Element.',
24+
'com.github.simy4.xpath.scala.navigator.Root.',
2425
'com.github.simy4.xpath.scala.xpath.*',
2526
]
2627

xpath-to-xml-scala/src/main/scala/com/github/simy4/xpath/scala/XmlBuilder.scala

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,9 @@ import xml.Elem
2929
* @since 2.2
3030
*/
3131
object XmlBuilder extends compat.Converters {
32-
33-
def apply(effects: Effect*) = new BuilderPartiallyApplied(effects)
34-
35-
def apply(effects: Iterable[Effect]) = new BuilderPartiallyApplied(effects)
36-
37-
final class BuilderPartiallyApplied private[XmlBuilder] (private val effects: Iterable[Effect]) extends AnyVal {
38-
def apply(xml: Elem): Either[XmlBuilderException, Elem] =
39-
try Right(new ScalaXmlNavigatorSpi().process(xml, effects.map(_.effect)))
40-
catch {
41-
case xbe: XmlBuilderException => Left(xbe)
42-
}
43-
}
32+
def apply(effect: Effect, rest: Effect*)(xml: Elem): Either[XmlBuilderException, Elem] =
33+
try Right(new ScalaXmlNavigatorSpi().process(xml, effect.effect +: rest.map(_.effect)))
34+
catch {
35+
case xbe: XmlBuilderException => Left(xbe)
36+
}
4437
}

xpath-to-xml-scala/src/main/scala/com/github/simy4/xpath/scala/impl/ToXmlElemOps.scala

Lines changed: 0 additions & 63 deletions
This file was deleted.

xpath-to-xml-scala/src/main/scala/com/github/simy4/xpath/scala/implicits.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
*/
1616
package com.github.simy4.xpath.scala
1717

18-
import impl.ToXmlElemOps
1918
import xpath.ToXPathLiteral
2019

21-
object implicits extends ToXmlElemOps with ToXPathLiteral
20+
object implicits extends ToXPathLiteral

xpath-to-xml-scala/src/main/scala/com/github/simy4/xpath/scala/navigator/ScalaXmlNavigator.scala

Lines changed: 70 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -17,42 +17,80 @@ package com.github.simy4.xpath
1717
package scala.navigator
1818

1919
import navigator.Navigator
20-
import xml.{ Attribute => XmlAttribute, Elem, Null, Text }
20+
import xml.{ Attribute => XmlAttribute, Elem, Null, Text, TopScope }
2121

2222
import javax.xml.namespace.QName
2323

2424
@SuppressWarnings(Array("org.wartremover.warts.Throw"))
2525
class ScalaXmlNavigator(override val root: Root) extends Navigator[ScalaXmlNode] with scala.compat.Converters {
26-
override def parentOf(node: ScalaXmlNode): ScalaXmlNode = node.parent
27-
override def elementsOf(parent: ScalaXmlNode): java.lang.Iterable[? <: ScalaXmlNode] = parent.elements
28-
override def attributesOf(parent: ScalaXmlNode): java.lang.Iterable[? <: ScalaXmlNode] = parent.attributes
29-
@throws[XmlBuilderException]("If unable to create attribute for given node")
30-
override def createAttribute(parent: ScalaXmlNode, attribute: QName): ScalaXmlNode =
31-
parent match {
32-
case e: Element =>
33-
val pre = attribute.getPrefix
34-
val attr = XmlAttribute(if (pre.nonEmpty) Some(pre) else None, attribute.getLocalPart, Text(""), Null)
35-
e.node = e.node % attr
36-
new Attribute(attr, e)
26+
@SuppressWarnings(Array("org.wartremover.warts.Null"))
27+
def parentOf(node: ScalaXmlNode): ScalaXmlNode = node.parent.orNull
28+
def elementsOf(parent: ScalaXmlNode): java.lang.Iterable[? <: ScalaXmlNode] = parent.elements
29+
def attributesOf(parent: ScalaXmlNode): java.lang.Iterable[? <: ScalaXmlNode] = parent.attributes
30+
def createAttribute(parent: ScalaXmlNode, attribute: QName): ScalaXmlNode = {
31+
val pre = attribute.getPrefix
32+
val attr = XmlAttribute(if (pre.nonEmpty) Some(pre) else None, attribute.getLocalPart, Text(""), Null)
33+
Attribute(attr)()
34+
}
35+
def createElement(parent: ScalaXmlNode, element: QName): ScalaXmlNode = {
36+
val scope = parent.node match {
37+
case e: Elem => e.scope
38+
case _ => TopScope
39+
}
40+
val pre = element.getPrefix
41+
val elem = Elem(if (pre.nonEmpty) pre else null, element.getLocalPart, Null, scope, minimizeEmpty = true)
42+
Element(elem)()
43+
}
44+
@throws[XmlBuilderException]("If unable to append prev element for given node")
45+
def appendPrev(node: ScalaXmlNode, prepend: ScalaXmlNode): Unit =
46+
(node, prepend) match {
47+
case (e: Element, p: Element) =>
48+
val parent = e.parent.getOrElse(throw new XmlBuilderException("Unable to prepend to detached node"))
49+
val idx = e.index
50+
val parentNode = parent.node
51+
parent.node = parentNode.copy(child = parentNode.child.patch(idx, Seq(p.node, e.node), 1))
52+
e.index += 1
53+
p.parent = Some(parent)
3754
case _ =>
38-
throw new XmlBuilderException(s"Unable to create attribute for ${parent.toString}")
55+
throw new XmlBuilderException(s"Unable to prepend to ${node.toString}")
3956
}
40-
@throws[XmlBuilderException]("If unable to create element for given node")
41-
override def createElement(parent: ScalaXmlNode, element: QName): ScalaXmlNode =
42-
parent match {
43-
case e: Element =>
44-
val node = e.node
45-
val children = node.child
46-
val pre = element.getPrefix
47-
val elem = Elem(if (pre.nonEmpty) pre else null, element.getLocalPart, Null, node.scope, minimizeEmpty = true)
48-
e.node = node.copy(child = children :+ elem)
49-
new Element(elem, children.size, e)
57+
@throws[XmlBuilderException]("If unable to append child element for given node")
58+
def appendChild(parent: ScalaXmlNode, node: ScalaXmlNode): Unit =
59+
(parent, node) match {
60+
case (parent: Root, elem: Element) =>
61+
val node = parent.node
62+
val child = node.child
63+
parent.node = node.copy(child = child :+ elem.node)
64+
elem.index = child.size
65+
elem.parent = Some(parent)
66+
case (parent: Element, elem: Element) =>
67+
val node = parent.node
68+
val child = node.child
69+
parent.node = node.copy(child = child :+ elem.node)
70+
elem.index = child.size
71+
elem.parent = Some(parent)
72+
case (parent: Element, attr: Attribute) =>
73+
parent.node = parent.node % attr.node
74+
attr.parent = Some(parent)
5075
case _ =>
51-
throw new XmlBuilderException(s"Unable to create element for ${parent.toString}")
76+
throw new XmlBuilderException(s"Unable to append child ${node.toString}")
77+
}
78+
@throws[XmlBuilderException]("If unable to append next element for given node")
79+
def appendNext(node: ScalaXmlNode, append: ScalaXmlNode): Unit =
80+
(node, append) match {
81+
case (e: Element, a: Element) =>
82+
val parent = e.parent.getOrElse(throw new XmlBuilderException("Unable to append to detached node"))
83+
val idx = e.index
84+
val parentNode = parent.node
85+
parent.node = parentNode.copy(child = parentNode.child.patch(idx, Seq(e.node, a.node), 1))
86+
e.index += 1
87+
a.parent = Some(parent)
88+
case _ =>
89+
throw new XmlBuilderException(s"Unable to append to ${node.toString}")
5290
}
5391
@SuppressWarnings(Array("org.wartremover.warts.IsInstanceOf"))
5492
@throws[XmlBuilderException]("If unable to set text to given node")
55-
override def setText(node: ScalaXmlNode, text: String): Unit =
93+
def setText(node: ScalaXmlNode, text: String): Unit =
5694
node match {
5795
case e: Element =>
5896
val elem = e.node
@@ -63,34 +101,23 @@ class ScalaXmlNavigator(override val root: Root) extends Navigator[ScalaXmlNode]
63101
case _ =>
64102
throw new XmlBuilderException(s"Unable to set text to ${node.toString}")
65103
}
66-
@throws[XmlBuilderException]("If unable to prepend copy to given node")
67-
override def prependCopy(node: ScalaXmlNode): Unit =
68-
node match {
69-
case e: Element =>
70-
val toCopy = e.node
71-
val copy = toCopy.copy()
72-
val idx = e.index
73-
val parentNode = e.parent.node
74-
e.parent.node = parentNode.copy(child = parentNode.child.patch(idx, Seq(copy, toCopy), 1))
75-
e.index += 1
76-
case _ =>
77-
throw new XmlBuilderException(s"Unable to prepend copy to ${node.toString}")
78-
}
79104
@throws[XmlBuilderException]("If unable to remove given node")
80-
override def remove(node: ScalaXmlNode): Unit =
105+
def remove(node: ScalaXmlNode): Unit =
81106
node match {
82107
case e: Element =>
83108
val idx = e.index
84-
val parentNode = e.parent.node
85-
e.parent.node = parentNode.copy(child = parentNode.child.patch(idx, Nil, 1))
109+
val parent = e.parent.getOrElse(throw new XmlBuilderException("Unable to remove detached node"))
110+
val parentNode = parent.node
111+
parent.node = parentNode.copy(child = parentNode.child.patch(idx, Nil, 1))
86112
case a: Attribute =>
87113
val toDelete = a.node
88-
val parentNode = a.parent.node
114+
val parent = a.parent.getOrElse(throw new XmlBuilderException("Unable to remove detached node"))
115+
val parentNode = parent.node
89116
val newAttr =
90117
if (toDelete.isPrefixed)
91118
parentNode.attributes.remove(toDelete.getNamespace(parentNode), parentNode, toDelete.key)
92119
else parentNode.attributes.remove(toDelete.key)
93-
a.parent.node = parentNode.copy(attributes = newAttr)
120+
parent.node = parentNode.copy(attributes = newAttr)
94121
case _ =>
95122
throw new XmlBuilderException(s"Unable to remove node ${node.toString}")
96123
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*
2+
* Copyright 2018-2021 Alex Simkin
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.github.simy4.xpath
17+
package scala.navigator
18+
19+
import navigator.{ Node => NavigatorNode }
20+
import xml.{ Attribute => XmlAttribute, Elem, Text }
21+
22+
import javax.xml.namespace.QName
23+
24+
/**
25+
* Scala XML node contract.
26+
*
27+
* @author
28+
* Alex Simkin
29+
* @since 2.0
30+
*/
31+
sealed trait ScalaXmlNode extends NavigatorNode with Equals {
32+
type N
33+
def parent: Option[ScalaXmlNode]
34+
@SuppressWarnings(Array("org.wartremover.warts.Var"))
35+
private[navigator] var node: N
36+
def elements: Iterable[ScalaXmlNode]
37+
def attributes: Iterable[ScalaXmlNode]
38+
override def toString: String = node.toString
39+
}
40+
41+
@SerialVersionUID(1L)
42+
@SuppressWarnings(Array("org.wartremover.warts.Var"))
43+
final case class Root(var node: Elem) extends ScalaXmlNode {
44+
type N = Elem
45+
def getName: QName = new QName(NavigatorNode.DOCUMENT)
46+
def getText: String = ""
47+
def parent: None.type = None
48+
def elements: Iterable[Element] = Element(node)(Some(this)) :: Nil
49+
def attributes: Iterable[Nothing] = Nil
50+
}
51+
52+
@SerialVersionUID(1L)
53+
@SuppressWarnings(Array("org.wartremover.warts.Var", "org.wartremover.warts.DefaultArguments"))
54+
final private[navigator] case class Element(
55+
private var _node: Elem,
56+
var index: Int = 0
57+
)(var parent: Option[ScalaXmlNode { type N = Elem }] = None)
58+
extends ScalaXmlNode {
59+
type N = Elem
60+
def getName: QName = {
61+
val node = _node
62+
if (null != node.prefix) new QName(node.namespace, node.label, node.prefix)
63+
else new QName(node.label)
64+
}
65+
def getText: String = node.child.collect { case Text(t) => t }.mkString
66+
@SuppressWarnings(Array("org.wartremover.warts.Any"))
67+
def elements: Iterable[Element] =
68+
_node.child.view.zipWithIndex.collect { case (e: Elem, i) => Element(e, i)(Some(this)) }
69+
def attributes: Iterable[Attribute] =
70+
_node.attributes.view.collect { case a: XmlAttribute => Attribute(a)(Some(this)) }
71+
private[navigator] def node: Elem = _node
72+
@SuppressWarnings(Array("org.wartremover.warts.Throw"))
73+
@throws[XmlBuilderException]("If setting an element to detached node")
74+
private[navigator] def node_=(elem: Elem): Unit = {
75+
val parent = this.parent.getOrElse(throw new XmlBuilderException("Unable to set element to detached node"))
76+
val parentNode = parent.node
77+
parent.node =
78+
if (parentNode eq _node) elem
79+
else parentNode.copy(child = parentNode.child.updated(index, elem))
80+
_node = elem
81+
}
82+
}
83+
84+
@SerialVersionUID(1L)
85+
@SuppressWarnings(Array("org.wartremover.warts.Var", "org.wartremover.warts.DefaultArguments"))
86+
final private[navigator] case class Attribute(
87+
private var attr: XmlAttribute
88+
)(var parent: Option[ScalaXmlNode { type N = Elem }] = None)
89+
extends ScalaXmlNode {
90+
type N = XmlAttribute
91+
def getName: QName = {
92+
val attr = this.attr
93+
parent match {
94+
case Some(parent) if attr.isPrefixed => new QName(attr.getNamespace(parent.node), attr.key, attr.pre)
95+
case _ => new QName(attr.key)
96+
}
97+
}
98+
def getText: String = attr.value.text
99+
def elements: Iterable[Nothing] = Nil
100+
def attributes: Iterable[Nothing] = Nil
101+
private[navigator] def node: XmlAttribute = attr
102+
@SuppressWarnings(Array("org.wartremover.warts.Throw"))
103+
@throws[XmlBuilderException]("If setting an attribute to detached node")
104+
private[navigator] def node_=(attr: XmlAttribute): Unit = {
105+
val parent = this.parent.getOrElse(throw new XmlBuilderException("Unable to set attribute to detached node"))
106+
parent.node = parent.node % attr
107+
this.attr = attr
108+
}
109+
}

0 commit comments

Comments
 (0)