├── .jvmopts
├── project
├── build.properties
└── plugins.sbt
├── jvm
└── src
│ └── test
│ ├── resources
│ └── scala
│ │ └── xml
│ │ ├── utf8.xml
│ │ ├── archive
│ │ ├── books
│ │ │ └── book
│ │ │ │ ├── author
│ │ │ │ └── volume
│ │ │ │ │ └── 1.xml
│ │ │ │ └── author.xml
│ │ └── books.xml
│ │ ├── includee.xml
│ │ ├── utf16.xml
│ │ ├── site.xml
│ │ └── includer.xml
│ ├── scala
│ └── scala
│ │ └── xml
│ │ ├── JavaByteSerialization.scala
│ │ ├── XMLSyntaxTest.scala
│ │ ├── SerializationTest.scala
│ │ ├── AttributeTest.scala
│ │ ├── parsing
│ │ ├── Ticket0632Test.scala
│ │ ├── PiParsingTest.scala
│ │ ├── XhtmlParserTest.scala
│ │ └── ConstructingParserTest.scala
│ │ ├── BillionLaughsAttackTest.scala
│ │ └── ReuseNodesTest.scala
│ └── scala-2.x
│ └── scala
│ └── xml
│ └── XMLTestJVM2x.scala
├── .github
├── dependabot.yml
└── workflows
│ ├── cla.yml
│ ├── ci.yml
│ └── release.yml
├── CONTRIBUTING.md
├── shared
└── src
│ ├── test
│ ├── scala
│ │ └── scala
│ │ │ └── xml
│ │ │ ├── JUnitAssertsForXML.scala
│ │ │ ├── NodeBufferTest.scala
│ │ │ ├── XMLEmbeddingTest.scala
│ │ │ ├── parsing
│ │ │ ├── PiParsingTest.scala
│ │ │ └── Ticket0632Test.scala
│ │ │ ├── Properties.scala
│ │ │ ├── CommentTest.scala
│ │ │ ├── PCDataTest.scala
│ │ │ ├── dtd
│ │ │ └── DeclTest.scala
│ │ │ ├── PrintEmptyElementsTest.scala
│ │ │ ├── MetaDataTest.scala
│ │ │ ├── XMLSyntaxTest.scala
│ │ │ ├── ShouldCompile.scala
│ │ │ ├── NodeSeqTest.scala
│ │ │ └── PatternMatchingTest.scala
│ └── scala-2.x
│ │ └── scala
│ │ └── xml
│ │ ├── ShouldCompile.scala
│ │ ├── XMLTest2x.scala
│ │ └── TransformersTest.scala
│ └── main
│ ├── scala
│ └── scala
│ │ └── xml
│ │ ├── TypeSymbol.scala
│ │ ├── MalformedAttributeException.scala
│ │ ├── parsing
│ │ ├── FatalError.scala
│ │ ├── ExternalSources.scala
│ │ ├── DefaultMarkupHandler.scala
│ │ ├── XhtmlParser.scala
│ │ ├── ConstructingHandler.scala
│ │ ├── ConstructingParser.scala
│ │ ├── NoBindingFactoryAdapter.scala
│ │ ├── TokenTests.scala
│ │ ├── MarkupHandler.scala
│ │ └── XhtmlEntities.scala
│ │ ├── transform
│ │ ├── NestingTransformer.scala
│ │ ├── RuleTransformer.scala
│ │ ├── RewriteRule.scala
│ │ └── BasicTransformer.scala
│ │ ├── include
│ │ ├── UnavailableResourceException.scala
│ │ ├── CircularIncludeException.scala
│ │ ├── XIncludeException.scala
│ │ └── sax
│ │ │ └── EncodingHeuristics.scala
│ │ ├── dtd
│ │ ├── impl
│ │ │ ├── SyntaxError.scala
│ │ │ ├── DetWordAutom.scala
│ │ │ ├── WordExp.scala
│ │ │ ├── Base.scala
│ │ │ ├── Inclusion.scala
│ │ │ ├── NondetWordAutom.scala
│ │ │ ├── BaseBerrySethi.scala
│ │ │ └── SubsetConstruction.scala
│ │ ├── Tokens.scala
│ │ ├── DTD.scala
│ │ ├── DocType.scala
│ │ ├── ValidationException.scala
│ │ ├── ExternalID.scala
│ │ └── ContentModel.scala
│ │ ├── QNode.scala
│ │ ├── TopScope.scala
│ │ ├── SpecialNode.scala
│ │ ├── Unparsed.scala
│ │ ├── Text.scala
│ │ ├── EntityRef.scala
│ │ ├── ProcInstr.scala
│ │ ├── Comment.scala
│ │ ├── TextBuffer.scala
│ │ ├── Atom.scala
│ │ ├── PCData.scala
│ │ ├── NodeBuffer.scala
│ │ ├── Group.scala
│ │ ├── factory
│ │ ├── NodeFactory.scala
│ │ └── XMLLoader.scala
│ │ ├── PrefixedAttribute.scala
│ │ ├── UnprefixedAttribute.scala
│ │ ├── Null.scala
│ │ ├── package.scala
│ │ ├── NamespaceBinding.scala
│ │ ├── Xhtml.scala
│ │ ├── Document.scala
│ │ ├── Attribute.scala
│ │ ├── Elem.scala
│ │ ├── Equality.scala
│ │ └── XML.scala
│ ├── scala-2
│ └── scala
│ │ └── xml
│ │ └── ScalaVersionSpecificReturnTypes.scala
│ ├── scala-2.12
│ └── scala
│ │ └── xml
│ │ └── ScalaVersionSpecific.scala
│ ├── scala-3
│ └── scala
│ │ └── xml
│ │ └── ScalaVersionSpecificReturnTypes.scala
│ └── scala-2.13+
│ └── scala
│ └── xml
│ └── ScalaVersionSpecific.scala
├── CODE_OF_CONDUCT.md
├── NOTICE
├── .gitignore
├── .mailmap
├── CHANGELOG.md
├── README.md
└── .circleci
└── config.yml
/.jvmopts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=1.11.7
2 |
--------------------------------------------------------------------------------
/jvm/src/test/resources/scala/xml/utf8.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/jvm/src/test/resources/scala/xml/archive/books/book/author/volume/1.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/jvm/src/test/resources/scala/xml/includee.xml:
--------------------------------------------------------------------------------
1 |
2 | Blah!
3 |
4 |
--------------------------------------------------------------------------------
/jvm/src/test/resources/scala/xml/utf16.xml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scala/scala-xml/HEAD/jvm/src/test/resources/scala/xml/utf16.xml
--------------------------------------------------------------------------------
/jvm/src/test/resources/scala/xml/site.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/jvm/src/test/resources/scala/xml/includer.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "github-actions"
4 | directory: "/"
5 | schedule:
6 | interval: "weekly"
7 |
--------------------------------------------------------------------------------
/jvm/src/test/resources/scala/xml/archive/books.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/jvm/src/test/resources/scala/xml/archive/books/book/author.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | Contributing
2 | ============
3 |
4 | Please see the wiki for the scala-xml contributor guide:
5 |
6 | https://github.com/scala/scala-xml/wiki/Contributor-guide
7 |
8 | Thank you,
9 |
10 | The Scala XML maintainers
11 |
--------------------------------------------------------------------------------
/shared/src/test/scala/scala/xml/JUnitAssertsForXML.scala:
--------------------------------------------------------------------------------
1 | package scala.xml
2 |
3 | object JUnitAssertsForXML {
4 |
5 | private[xml] def assertEquals(expected: String, actual: NodeSeq): Unit =
6 | org.junit.Assert.assertEquals(expected, actual.toString)
7 | }
8 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | all repositories in these organizations:
2 |
3 | * [scala](https://github.com/scala)
4 | * [scalacenter](https://github.com/scalacenter)
5 | * [lampepfl](https://github.com/lampepfl)
6 |
7 | are covered by the Scala Code of Conduct: https://scala-lang.org/conduct/
8 |
--------------------------------------------------------------------------------
/shared/src/test/scala-2.x/scala/xml/ShouldCompile.scala:
--------------------------------------------------------------------------------
1 | package scala.xml
2 |
3 | // these tests depend on xml, so they ended up here,
4 | // though really they are compiler tests
5 |
6 | // t1626
7 | object o {
8 | val n: Elem =
9 | n.namespace == null
10 | }
11 |
--------------------------------------------------------------------------------
/.github/workflows/cla.yml:
--------------------------------------------------------------------------------
1 | name: "Check Scala CLA"
2 | on:
3 | pull_request:
4 | jobs:
5 | cla-check:
6 | runs-on: ubuntu-latest
7 | steps:
8 | - name: Verify CLA
9 | uses: scala/cla-checker@v1
10 | with:
11 | author: ${{ github.event.pull_request.user.login }}
12 |
--------------------------------------------------------------------------------
/shared/src/test/scala/scala/xml/NodeBufferTest.scala:
--------------------------------------------------------------------------------
1 | package scala.xml
2 |
3 | import org.junit.Test
4 | import org.junit.Assert.assertEquals
5 |
6 | class NodeBufferTest {
7 |
8 | @Test
9 | def testToString(): Unit = {
10 | val nodeBuffer: NodeBuffer = new NodeBuffer
11 | assertEquals("NodeBuffer()", nodeBuffer.toString)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("org.scala-lang.modules" % "sbt-scala-module" % "3.4.0")
2 | addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.3.2")
3 | addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % "1.3.2")
4 | addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.20.1")
5 | addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.9")
6 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/TypeSymbol.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 |
16 | abstract class TypeSymbol
17 |
--------------------------------------------------------------------------------
/shared/src/test/scala/scala/xml/XMLEmbeddingTest.scala:
--------------------------------------------------------------------------------
1 | package scala.xml
2 |
3 | import org.junit.Test
4 | import org.junit.Assert.assertEquals
5 |
6 | class XMLEmbeddingTest {
7 |
8 | @Test
9 | def basic(): Unit = {
10 | val ya: Elem = {{
11 | assertEquals("{", ya.text)
12 | val ua: Elem = }}
13 | assertEquals("}", ua.text)
14 | val za: Elem = {{}}{{}}{{}}
15 | assertEquals("{}{}{}", za.text)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/MalformedAttributeException.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 |
16 | case class MalformedAttributeException(msg: String) extends RuntimeException(msg)
17 |
--------------------------------------------------------------------------------
/shared/src/test/scala/scala/xml/parsing/PiParsingTest.scala:
--------------------------------------------------------------------------------
1 | package scala.xml.parsing
2 |
3 | import org.junit.Test
4 | import scala.xml.JUnitAssertsForXML.assertEquals
5 |
6 | class PiParsingTest {
7 |
8 | @Test
9 | def piNoWSLiteral(): Unit = {
10 | val expected: String = "ab"
11 | assertEquals(expected, ab)
12 | }
13 |
14 | @Test
15 | def piLiteral(): Unit = {
16 | val expected: String = " a b "
17 | assertEquals(expected, a b )
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/jvm/src/test/scala/scala/xml/JavaByteSerialization.scala:
--------------------------------------------------------------------------------
1 | package scala.xml
2 |
3 | import java.io.Serializable
4 | import org.apache.commons.lang3.SerializationUtils
5 |
6 | object JavaByteSerialization {
7 | def roundTrip[T <: Serializable](obj: T): T = {
8 | SerializationUtils.roundtrip(obj)
9 | }
10 |
11 | def serialize[T <: Serializable](in: T): Array[Byte] = {
12 | SerializationUtils.serialize(in)
13 | }
14 |
15 | def deserialize[T <: Serializable](in: Array[Byte]): T = {
16 | SerializationUtils.deserialize(in)
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/parsing/FatalError.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 | package parsing
16 |
17 | /**
18 | * !!! This is poorly named, but I guess it's in the API.
19 | */
20 | case class FatalError(msg: String) extends java.lang.RuntimeException(msg)
21 |
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | scala-xml
2 | Copyright (c) 2002-2025 EPFL
3 | Copyright (c) 2011-2025 Lightbend, Inc. dba Akka
4 |
5 | scala-xml includes software developed at
6 | LAMP/EPFL (https://lamp.epfl.ch/) and
7 | Akka (https://akka.io/).
8 |
9 | Licensed under the Apache License, Version 2.0 (the "License").
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 |
--------------------------------------------------------------------------------
/shared/src/test/scala/scala/xml/Properties.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 |
16 | object Properties extends util.PropertiesTrait {
17 | override protected def propCategory: String = "scala-xml"
18 | override protected def pickJarBasedOn: Class[Node] = classOf[Node]
19 | }
20 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/transform/NestingTransformer.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 | package transform
16 |
17 | import scala.collection.Seq
18 |
19 | class NestingTransformer(rule: RewriteRule) extends BasicTransformer {
20 | override def transform(n: Node): Seq[Node] =
21 | rule.transform(super.transform(n))
22 | }
23 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: test
2 | on:
3 | push:
4 | branches:
5 | - main
6 | pull_request:
7 | jobs:
8 | test:
9 | strategy:
10 | fail-fast: false
11 | matrix:
12 | java: [8, 11, 17, 21]
13 | scala: [2.12.x, 2.13.x, 3.x]
14 | runs-on: ubuntu-latest
15 | steps:
16 | - uses: actions/checkout@v6
17 | with:
18 | fetch-depth: 0
19 | - uses: actions/setup-java@v5
20 | with:
21 | distribution: temurin
22 | java-version: ${{matrix.java}}
23 | cache: sbt
24 | - uses: sbt/setup-sbt@v1
25 | - name: Test
26 | run: sbt ++${{matrix.scala}} test headerCheck versionPolicyCheck package
27 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 | on:
3 | push:
4 | tags: ["*"]
5 | jobs:
6 | publish:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - uses: actions/checkout@v6
10 | with:
11 | fetch-depth: 0
12 | - uses: actions/setup-java@v5
13 | with:
14 | distribution: temurin
15 | java-version: 8
16 | - uses: sbt/setup-sbt@v1
17 | - run: sbt versionCheck ci-release
18 | env:
19 | PGP_PASSPHRASE: ${{secrets.PGP_PASSPHRASE}}
20 | PGP_SECRET: ${{secrets.PGP_SECRET}}
21 | SONATYPE_PASSWORD: ${{secrets.SONATYPE_PASSWORD}}
22 | SONATYPE_USERNAME: ${{secrets.SONATYPE_USERNAME}}
23 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/include/UnavailableResourceException.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 | package include
16 |
17 | /**
18 | * An `UnavailableResourceException` is thrown when an included document
19 | * cannot be found or loaded.
20 | */
21 | class UnavailableResourceException(message: String)
22 | extends XIncludeException(message) {
23 | def this() = this(null)
24 | }
25 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/dtd/impl/SyntaxError.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml.dtd.impl
15 |
16 | /**
17 | * This runtime exception is thrown if an attempt to instantiate a
18 | * syntactically incorrect expression is detected.
19 | *
20 | * @author Burak Emir
21 | */
22 | @deprecated("This class will be removed", "2.10.0")
23 | private[dtd] class SyntaxError(e: String) extends RuntimeException(e)
24 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/QNode.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 |
16 | /**
17 | * This object provides an extractor method to match a qualified node with
18 | * its namespace URI
19 | *
20 | * @author Burak Emir
21 | */
22 | object QNode {
23 | def unapplySeq(n: Node): Some[(String, String, MetaData, ScalaVersionSpecific.SeqOfNode)] =
24 | Some((n.scope.getURI(n.prefix), n.label, n.attributes, n.child))
25 | }
26 |
--------------------------------------------------------------------------------
/shared/src/test/scala/scala/xml/CommentTest.scala:
--------------------------------------------------------------------------------
1 | package scala.xml
2 |
3 | import org.junit.Assert.assertEquals
4 | import org.junit.Test
5 |
6 | final class CommentTest {
7 |
8 | @Test(expected=classOf[IllegalArgumentException])
9 | def invalidCommentWithTwoDashes(): Unit = {
10 | Comment("invalid--comment")
11 | }
12 |
13 | @Test(expected=classOf[IllegalArgumentException])
14 | def invalidCommentWithFinalDash(): Unit = {
15 | Comment("invalid comment-")
16 | }
17 |
18 | @Test
19 | def validCommentWithDash(): Unit = {
20 | val valid: String = "valid-comment"
21 | assertEquals(s"", Comment(valid).toString)
22 | }
23 |
24 | @Test
25 | def validEmptyComment(): Unit = {
26 | val valid: String = ""
27 | assertEquals(s"", Comment(valid).toString)
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/shared/src/test/scala/scala/xml/PCDataTest.scala:
--------------------------------------------------------------------------------
1 | package scala.xml
2 |
3 | import org.junit.Test
4 | import org.junit.Assert.assertEquals
5 |
6 | class PCDataTest {
7 |
8 | def check(pcdata: String, expected: String): Unit = {
9 | val actual: PCData = new PCData(pcdata)
10 | assertEquals(expected, actual.toString)
11 | }
12 |
13 | @Test
14 | def emptyTest(): Unit = check("", "")
15 |
16 | @Test
17 | def bracketTest(): Unit = check("[]", "")
18 |
19 | @Test
20 | def hellaBracketingTest(): Unit = check("[[[[[[[[]]]]]]]]", "")
21 |
22 | @Test
23 | def simpleNestingTest(): Unit = check("]]>", "]]>")
24 |
25 | @Test
26 | def recursiveNestingTest(): Unit = check("", "]]>")
27 | }
28 |
--------------------------------------------------------------------------------
/shared/src/test/scala/scala/xml/parsing/Ticket0632Test.scala:
--------------------------------------------------------------------------------
1 | package scala.xml.parsing
2 |
3 | import org.junit.Test
4 | import scala.xml.JUnitAssertsForXML.assertEquals
5 |
6 | class Ticket0632Test {
7 |
8 | @Test
9 | def singleAmp(): Unit = {
10 | val expected: String = ""
11 | assertEquals(expected, )
12 | assertEquals(expected, )
13 | }
14 |
15 | @Test
16 | def oneAndHalfAmp(): Unit = {
17 | val expected: String = ""
18 | assertEquals(expected, )
19 | assertEquals(expected, )
20 | }
21 |
22 | @Test
23 | def doubleAmp(): Unit = {
24 | val expected: String = ""
25 | assertEquals(expected, )
26 | assertEquals(expected, )
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/include/CircularIncludeException.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 | package include
16 |
17 | /**
18 | * A `CircularIncludeException` is thrown when an included document attempts
19 | * to include itself or one of its ancestor documents.
20 | */
21 | class CircularIncludeException(message: String) extends XIncludeException {
22 |
23 | /**
24 | * Constructs a `CircularIncludeException` with `'''null'''`.
25 | * as its error detail message.
26 | */
27 | def this() = this(null)
28 | }
29 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | #
2 | # Are you tempted to edit this file?
3 | #
4 | # First consider if the changes make sense for all,
5 | # or if they are specific to your workflow/system.
6 | # If it is the latter, you can augment this list with
7 | # entries in .git/info/excludes
8 | #
9 | # see also test/files/.gitignore
10 | #
11 |
12 | *.jar
13 | *~
14 |
15 | # target directories for ant build
16 | /build/
17 | /dists/
18 |
19 | # other
20 | /out/
21 | /bin/
22 | /sandbox/
23 |
24 | # eclipse, intellij
25 | /.classpath
26 | /.project
27 | /src/intellij/*.iml
28 | /src/intellij/*.ipr
29 | /src/intellij/*.iws
30 | /.cache
31 | /.idea
32 | /.settings
33 |
34 | # bak files produced by ./cleanup-commit
35 | *.bak
36 |
37 | # Standard symbolic link to build/quick/bin
38 | qbin
39 |
40 | # Mac specific, but that is common enough a dev platform to warrant inclusion.
41 | .DS_Store
42 |
43 | target/
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/transform/RuleTransformer.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 | package transform
16 |
17 | import scala.collection.Seq
18 |
19 | class RuleTransformer(rules: RewriteRule*) extends BasicTransformer {
20 | private val transformers: Seq[NestingTransformer] = rules.map(new NestingTransformer(_))
21 |
22 | override def transform(n: Node): Seq[Node] =
23 | if (transformers.isEmpty) n
24 | else transformers.tail.foldLeft(transformers.head.transform(n)) { (res, transformer) => transformer.transform(res) }
25 | }
26 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/transform/RewriteRule.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 | package transform
16 |
17 | import scala.collection.Seq
18 |
19 | /**
20 | * A RewriteRule, when applied to a term, yields either
21 | * the result of rewriting the term or the term itself if the rule
22 | * is not applied.
23 | *
24 | * @author Burak Emir
25 | */
26 | abstract class RewriteRule extends BasicTransformer {
27 | override def transform(ns: Seq[Node]): Seq[Node] = super.transform(ns)
28 | override def transform(n: Node): Seq[Node] = n
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/jvm/src/test/scala/scala/xml/XMLSyntaxTest.scala:
--------------------------------------------------------------------------------
1 | package scala.xml
2 |
3 | import org.junit.Test
4 | import org.junit.Assert.assertEquals
5 |
6 | class XMLSyntaxTestJVM {
7 |
8 | @Test
9 | def test3(): Unit = {
10 | // this demonstrates how to handle entities
11 | val s: io.Source = io.Source.fromString(" ")
12 | object parser extends xml.parsing.ConstructingParser(s, preserveWS = false /*ignore ws*/) {
13 | override def replacementText(entityName: String): io.Source = {
14 | entityName match {
15 | case "nbsp" => io.Source.fromString("\u0160")
16 | case _ => super.replacementText(entityName)
17 | }
18 | }
19 | nextch() // !!important, to initialize the parser
20 | }
21 | val parsed: NodeSeq = parser.element(TopScope) // parse the source as element
22 | // alternatively, we could call document()
23 | assertEquals("Š", parsed.toString)
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/jvm/src/test/scala/scala/xml/SerializationTest.scala:
--------------------------------------------------------------------------------
1 | package scala.xml
2 |
3 | import scala.collection.Seq
4 | import org.junit.Assert.assertEquals
5 | import org.junit.Test
6 |
7 | class SerializationTest {
8 | @Test
9 | def xmlLiteral(): Unit = {
10 | val n: Elem =
11 | assertEquals(n, JavaByteSerialization.roundTrip(n))
12 | }
13 |
14 | @Test
15 | def empty(): Unit = {
16 | assertEquals(NodeSeq.Empty, JavaByteSerialization.roundTrip(NodeSeq.Empty))
17 | }
18 |
19 | @Test
20 | def unmatched(): Unit = {
21 | assertEquals(NodeSeq.Empty, JavaByteSerialization.roundTrip( \ "HTML"))
22 | }
23 |
24 | @Test
25 | def implicitConversion(): Unit = {
26 | val parent: Elem =
27 | val children: Seq[Node] = parent.child
28 | val asNodeSeq: NodeSeq = children // implicit seqToNodeSeq
29 | assertEquals(asNodeSeq, JavaByteSerialization.roundTrip(asNodeSeq))
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/shared/src/test/scala-2.x/scala/xml/XMLTest2x.scala:
--------------------------------------------------------------------------------
1 | package scala.xml
2 |
3 | import org.junit.{Test => UnitTest}
4 | import org.junit.Assert.assertTrue
5 | import org.junit.Assert.assertEquals
6 |
7 | class XMLTest2x {
8 | // t-486
9 | def wsdlTemplate3(serviceName: String): Node =
10 |
11 |
12 |
13 | @UnitTest
14 | def wsdl(): Unit = {
15 | assertEquals("""
16 | """, wsdlTemplate3("service3").toString)
17 | }
18 |
19 | @UnitTest
20 | def t5154(): Unit = {
21 |
22 | // extra space made the pattern OK
23 | def f: Boolean = {{3}} match { case {{3}} => true }
24 |
25 | // lack of space used to error: illegal start of simple pattern
26 | def g: Boolean = {{3}} match { case {{3}} => true }
27 |
28 | assertTrue(f)
29 | assertTrue(g)
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/shared/src/test/scala/scala/xml/dtd/DeclTest.scala:
--------------------------------------------------------------------------------
1 | package scala.xml
2 | package dtd
3 |
4 | import org.junit.Test
5 | import org.junit.Assert.assertEquals
6 |
7 | class DeclTest {
8 |
9 | @Test
10 | def elemDeclToString(): Unit = {
11 | assertEquals(
12 | "",
13 | ElemDecl("x", PCDATA).toString
14 | )
15 | }
16 |
17 | @Test
18 | def attListDeclToString(): Unit = {
19 |
20 | val expected: String =
21 | """|""".stripMargin
24 |
25 | val actual: String = AttListDecl("x",
26 | List(
27 | AttrDecl("y", "CDATA", REQUIRED),
28 | AttrDecl("z", "CDATA", REQUIRED)
29 | )
30 | ).toString
31 |
32 | assertEquals(expected, actual)
33 | }
34 |
35 | @Test
36 | def parsedEntityDeclToString(): Unit = {
37 | assertEquals(
38 | """""",
39 | ParsedEntityDecl("foo", ExtDef(SystemID("bar"))).toString
40 | )
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/parsing/ExternalSources.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 | package parsing
16 |
17 | import java.net.URL
18 | import java.io.File.separator
19 |
20 | import scala.io.Source
21 |
22 | /**
23 | * @author Burak Emir
24 | */
25 | trait ExternalSources {
26 | self: ExternalSources with MarkupParser with MarkupHandler =>
27 |
28 | def externalSource(systemId: String): Source = {
29 | if (systemId.startsWith("http:"))
30 | return Source.fromURL(new URL(systemId))
31 |
32 | val fileStr: String = input.descr match {
33 | case x if x.startsWith("file:") => x.drop(5)
34 | case x => x.take(x.lastIndexOf(separator) + 1)
35 | }
36 |
37 | Source.fromFile(s"$fileStr$systemId")
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/TopScope.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 |
16 | /**
17 | * top level namespace scope. only contains the predefined binding
18 | * for the "xml" prefix which is bound to
19 | * "http://www.w3.org/XML/1998/namespace"
20 | */
21 | object TopScope extends NamespaceBinding(null, null, null) {
22 |
23 | override def getURI(prefix1: String): String =
24 | if (prefix1 == XML.xml) XML.namespace else null
25 |
26 | override def getPrefix(uri1: String): String =
27 | if (uri1 == XML.namespace) XML.xml else null
28 |
29 | override def toString: String = ""
30 |
31 | override def buildString(stop: NamespaceBinding): String = ""
32 | override def buildString(sb: StringBuilder, ignore: NamespaceBinding): Unit = ()
33 | }
34 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/parsing/DefaultMarkupHandler.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 | package parsing
16 |
17 | /** Default implementation of markup handler always returns `NodeSeq.Empty` */
18 | abstract class DefaultMarkupHandler extends MarkupHandler {
19 |
20 | override def elem(pos: Int, pre: String, label: String, attrs: MetaData,
21 | scope: NamespaceBinding, empty: Boolean, args: NodeSeq): NodeSeq = NodeSeq.Empty
22 |
23 | override def procInstr(pos: Int, target: String, txt: String): NodeSeq = NodeSeq.Empty
24 |
25 | override def comment(pos: Int, comment: String): NodeSeq = NodeSeq.Empty
26 |
27 | override def entityRef(pos: Int, n: String): NodeSeq = NodeSeq.Empty
28 |
29 | override def text(pos: Int, txt: String): NodeSeq = NodeSeq.Empty
30 | }
31 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/parsing/XhtmlParser.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 | package parsing
16 |
17 | import scala.io.Source
18 |
19 | /**
20 | * An XML Parser that preserves `CDATA` blocks and knows about
21 | * [[scala.xml.parsing.XhtmlEntities]].
22 | *
23 | * @author (c) David Pollak, 2007 WorldWide Conferencing, LLC.
24 | */
25 | class XhtmlParser(override val input: Source) extends ConstructingHandler with MarkupParser with ExternalSources {
26 | override val preserveWS: Boolean = true
27 | ent ++= XhtmlEntities()
28 | }
29 |
30 | /**
31 | * Convenience method that instantiates, initializes and runs an `XhtmlParser`.
32 | *
33 | * @author Burak Emir
34 | */
35 | object XhtmlParser {
36 | def apply(source: Source): NodeSeq = new XhtmlParser(source).initialize.document()
37 | }
38 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/SpecialNode.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 |
16 | /**
17 | * `SpecialNode` is a special XML node which represents either text
18 | * `(PCDATA)`, a comment, a `PI`, or an entity ref.
19 | *
20 | * @author Burak Emir
21 | */
22 | abstract class SpecialNode extends Node {
23 |
24 | /** always empty */
25 | final override def attributes: Null.type = Null
26 |
27 | /** always Node.EmptyNamespace - TODO not really: Node.EmptyNamespace is "", but this is null. */
28 | final override def namespace: scala.Null = null
29 |
30 | /** always empty */
31 | final override def child: ScalaVersionSpecificReturnTypes.SpecialNodeChild = Nil
32 |
33 | /** Append string representation to the given string buffer argument. */
34 | def buildString(sb: StringBuilder): StringBuilder
35 | }
36 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/dtd/Tokens.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 | package dtd
16 |
17 | class Tokens {
18 |
19 | // Tokens
20 |
21 | final val TOKEN_PCDATA: Int = 0
22 | final val NAME: Int = 1
23 | final val LPAREN: Int = 3
24 | final val RPAREN: Int = 4
25 | final val COMMA: Int = 5
26 | final val STAR: Int = 6
27 | final val PLUS: Int = 7
28 | final val OPT: Int = 8
29 | final val CHOICE: Int = 9
30 | final val END: Int = 10
31 | final val S: Int = 13
32 |
33 | final def token2string(i: Int): String = i match {
34 | case 0 => "#PCDATA"
35 | case 1 => "NAME"
36 | case 3 => "("
37 | case 4 => ")"
38 | case 5 => ","
39 | case 6 => "*"
40 | case 7 => "+"
41 | case 8 => "?"
42 | case 9 => "|"
43 | case 10 => "END"
44 | case 13 => " "
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/dtd/DTD.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 | package dtd
16 |
17 | import scala.collection.mutable
18 | import scala.collection.Seq
19 |
20 | /**
21 | * A document type declaration.
22 | *
23 | * @author Burak Emir
24 | */
25 | abstract class DTD {
26 | var externalID: ExternalID = _
27 | var decls: List[Decl] = Nil
28 | def notations: Seq[NotationDecl] = Nil
29 | def unparsedEntities: Seq[EntityDecl] = Nil
30 |
31 | var elem: mutable.Map[String, ElemDecl] = new mutable.HashMap[String, ElemDecl]()
32 | var attr: mutable.Map[String, AttListDecl] = new mutable.HashMap[String, AttListDecl]()
33 | var ent: mutable.Map[String, EntityDecl] = new mutable.HashMap[String, EntityDecl]()
34 |
35 | override def toString: String =
36 | s"DTD ${Option(externalID).getOrElse("")} [\n${decls.mkString("\n")}\n]"
37 | }
38 |
--------------------------------------------------------------------------------
/jvm/src/test/scala/scala/xml/AttributeTest.scala:
--------------------------------------------------------------------------------
1 | package scala.xml
2 |
3 | import org.junit.Test
4 | import org.junit.Assert.assertEquals
5 | import org.junit.Assert.assertNotEquals
6 |
7 | class AttributeTestJVM {
8 |
9 | @Test
10 | def attributeOrder(): Unit = {
11 | val x: Elem =
12 | assertEquals("""""", x.toString)
13 | }
14 |
15 | @Test
16 | def attributesFromString(): Unit = {
17 | val str: String = """"""
18 | val doc: Elem = XML.loadString(str)
19 | assertEquals(str, doc.toString)
20 | }
21 |
22 | @Test
23 | def attributesAndNamespaceFromString(): Unit = {
24 | val str: String = """"""
25 | val doc: Elem = XML.loadString(str)
26 | assertNotEquals(str, doc.toString)
27 | val str2: String = """"""
28 | val doc2: Elem = XML.loadString(str2)
29 | assertEquals(str2, doc2.toString)
30 | }
31 |
32 | @Test(expected=classOf[SAXParseException])
33 | def attributesFromStringWithDuplicate(): Unit = {
34 | val str: String = """"""
35 | XML.loadString(str)
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/parsing/ConstructingHandler.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 | package parsing
16 |
17 | /**
18 | * Implementation of MarkupHandler that constructs nodes.
19 | *
20 | * @author Burak Emir
21 | */
22 | abstract class ConstructingHandler extends MarkupHandler {
23 | val preserveWS: Boolean
24 |
25 | override def elem(pos: Int, pre: String, label: String, attrs: MetaData,
26 | pscope: NamespaceBinding, empty: Boolean, nodes: NodeSeq): NodeSeq =
27 | Elem(pre, label, attrs, pscope, empty, nodes: _*)
28 |
29 | override def procInstr(pos: Int, target: String, txt: String): ProcInstr = ProcInstr(target, txt)
30 | override def comment(pos: Int, txt: String): Comment = Comment(txt)
31 | override def entityRef(pos: Int, n: String): EntityRef = EntityRef(n)
32 | override def text(pos: Int, txt: String): Text = Text(txt)
33 | }
34 |
--------------------------------------------------------------------------------
/jvm/src/test/scala/scala/xml/parsing/Ticket0632Test.scala:
--------------------------------------------------------------------------------
1 | package scala.xml.parsing
2 |
3 | import org.junit.Test
4 | import scala.xml.JUnitAssertsForXML.assertEquals
5 | import scala.xml.NodeSeq
6 |
7 | class Ticket0632TestJVM {
8 |
9 | import scala.io.Source.fromString
10 | import scala.xml.parsing.ConstructingParser.fromSource
11 | import scala.xml.TopScope
12 | private def parse(s:String): NodeSeq = fromSource(fromString(s), preserveWS = false).element(TopScope)
13 |
14 | @Test
15 | def singleAmp(): Unit = {
16 | val expected: String = ""
17 | assertEquals(expected, parse(""))
18 | assertEquals(expected, xml.XML.loadString(""))
19 | }
20 |
21 | @Test
22 | def oneAndHalfAmp(): Unit = {
23 | val expected: String = ""
24 | assertEquals(expected, xml.XML.loadString(""))
25 | assertEquals(expected, parse(""))
26 | }
27 |
28 | @Test
29 | def doubleAmp(): Unit = {
30 | val expected: String = ""
31 | assertEquals(expected, xml.XML.loadString(""))
32 | assertEquals(expected, parse(""))
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/Unparsed.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 |
16 | /**
17 | * An XML node for unparsed content. It will be output verbatim, all bets
18 | * are off regarding wellformedness etc.
19 | *
20 | * @author Burak Emir
21 | * @param data content in this node, may not be null.
22 | */
23 | // Note: used by the Scala compiler.
24 | class Unparsed(data: String) extends Atom[String](data) {
25 |
26 | /**
27 | * Returns text, with some characters escaped according to XML
28 | * specification.
29 | */
30 | override def buildString(sb: StringBuilder): StringBuilder =
31 | sb.append(data)
32 | }
33 |
34 | /**
35 | * This singleton object contains the `apply`and `unapply` methods for
36 | * convenient construction and deconstruction.
37 | *
38 | * @author Burak Emir
39 | */
40 | object Unparsed {
41 | def apply(data: String): Unparsed = new Unparsed(data)
42 | def unapply(x: Unparsed): Some[String] = Some(x.data)
43 | }
44 |
--------------------------------------------------------------------------------
/jvm/src/test/scala/scala/xml/parsing/PiParsingTest.scala:
--------------------------------------------------------------------------------
1 | package scala.xml.parsing
2 |
3 | import org.junit.Test
4 | import scala.xml.JUnitAssertsForXML.assertEquals
5 | import scala.xml.NodeSeq
6 |
7 | class PiParsingTestJVM {
8 |
9 | import scala.io.Source.fromString
10 | import scala.xml.parsing.ConstructingParser.fromSource
11 | import scala.xml.TopScope
12 | private def parse(s: String): NodeSeq = fromSource(fromString(s), preserveWS = true).element(TopScope)
13 | private def parseNoWS(s: String): NodeSeq = fromSource(fromString(s), preserveWS = false).element(TopScope)
14 |
15 | @Test
16 | def piNoWSparse(): Unit = {
17 | val expected: String = "ab"
18 | assertEquals(expected, parseNoWS("ab"))
19 | }
20 |
21 | @Test
22 | def piNoWSloadString(): Unit = {
23 | val expected: String = "ab"
24 | assertEquals(expected, xml.XML.loadString("ab"))
25 | }
26 |
27 | @Test
28 | def piParse(): Unit = {
29 | val expected: String = " a b "
30 | assertEquals(expected, parse(" a b "))
31 | }
32 |
33 | @Test
34 | def piLoadString(): Unit = {
35 | val expected: String = " a b "
36 | assertEquals(expected, xml.XML.loadString(" a b "))
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/Text.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 |
16 | /**
17 | * The class `Text` implements an XML node for text (PCDATA).
18 | * It is used in both non-bound and bound XML representations.
19 | *
20 | * @author Burak Emir
21 | * @param data the text contained in this node, may not be null.
22 | */
23 | // Note: used by the Scala compiler.
24 | class Text(data: String) extends Atom[String](data) {
25 |
26 | /**
27 | * Returns text, with some characters escaped according to the XML
28 | * specification.
29 | */
30 | override def buildString(sb: StringBuilder): StringBuilder =
31 | Utility.escape(data, sb)
32 | }
33 |
34 | /**
35 | * This singleton object contains the `apply`and `unapply` methods for
36 | * convenient construction and deconstruction.
37 | *
38 | * @author Burak Emir
39 | */
40 | // Note: used by the Scala compiler.
41 | object Text {
42 | def apply(data: String): Text = new Text(data)
43 | def unapply(other: Any): Option[String] = other match {
44 | case x: Text => Some(x.data)
45 | case _ => None
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/EntityRef.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 |
16 | /**
17 | * The class `EntityRef` implements an XML node for entity references.
18 | *
19 | * @author Burak Emir
20 | * @param entityName the name of the entity reference, for example `amp`.
21 | */
22 | // Note: used by the Scala compiler.
23 | case class EntityRef(entityName: String) extends SpecialNode {
24 | final override def doCollectNamespaces: Boolean = false
25 | final override def doTransform: Boolean = false
26 | override def label: String = "#ENTITY"
27 |
28 | override def text: String = entityName match {
29 | case "lt" => "<"
30 | case "gt" => ">"
31 | case "amp" => "&"
32 | case "apos" => "'"
33 | case "quot" => "\""
34 | case _ => Utility.sbToString(buildString)
35 | }
36 |
37 | /**
38 | * Appends `"& entityName;"` to this string buffer.
39 | *
40 | * @param sb the string buffer.
41 | * @return the modified string buffer `sb`.
42 | */
43 | override def buildString(sb: StringBuilder): StringBuilder =
44 | sb.append(s"&$entityName;")
45 | }
46 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/dtd/DocType.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 | package dtd
16 |
17 | import scala.collection.Seq
18 |
19 | /**
20 | * An XML node for document type declaration.
21 | *
22 | * @author Burak Emir
23 | *
24 | * @param name name of this DOCTYPE
25 | * @param extID NoExternalID or the external ID of this doctype
26 | * @param intSubset sequence of internal subset declarations
27 | */
28 | case class DocType(name: String, extID: ExternalID, intSubset: Seq[dtd.Decl]) {
29 | if (!Utility.isName(name))
30 | throw new IllegalArgumentException(s"$name must be an XML Name")
31 |
32 | /** returns "<!DOCTYPE + name + extID? + ("["+intSubSet+"]")? >" */
33 | final override def toString: String = {
34 | def intString: String =
35 | if (intSubset.isEmpty) ""
36 | else intSubset.mkString("[", "", "]")
37 |
38 | s""
39 | }
40 | }
41 |
42 | object DocType {
43 | /** Creates a doctype with no external id, nor internal subset declarations. */
44 | def apply(name: String): DocType = apply(name, NoExternalID, Nil)
45 | }
46 |
--------------------------------------------------------------------------------
/shared/src/main/scala-2/scala/xml/ScalaVersionSpecificReturnTypes.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala.xml
14 |
15 | /*
16 | Unlike other Scala-version-specific things, this class is not filling any gaps in capabilities
17 | between different versions of Scala; instead, it mostly documents the types that different versions of the
18 | Scala compiler inferred in the unfortunate absence of the explicit type annotations.
19 | What should have been specified explicitly is given in the comments;
20 | next time we break binary compatibility the types should be changed in the code and this class removed.
21 | */
22 | private[xml] object ScalaVersionSpecificReturnTypes { // should be
23 | type ExternalIDAttribute = MetaData // Null.type
24 | type NoExternalIDId = scala.Null
25 | type NodeNoAttributes = MetaData // Null.type
26 | type NullFilter = MetaData // Null.type
27 | type NullGetNamespace = scala.Null
28 | type NullNext = scala.Null
29 | type NullKey = scala.Null
30 | type NullValue = scala.Null
31 | type NullApply3 = scala.Null
32 | type NullRemove = Null.type
33 | type SpecialNodeChild = Nil.type
34 | type GroupChild = Nothing
35 | }
36 |
--------------------------------------------------------------------------------
/jvm/src/test/scala/scala/xml/BillionLaughsAttackTest.scala:
--------------------------------------------------------------------------------
1 | package scala.xml
2 |
3 | import org.junit.Test
4 |
5 | class BillionLaughsAttackTest {
6 |
7 | /**
8 | * org.xml.sax.SAXParseException: JAXP00010001: The parser has
9 | * encountered more than "64000" entity expansions in this document;
10 | * this is the limit imposed by the JDK.
11 | */
12 | @Test(expected=classOf[org.xml.sax.SAXParseException])
13 | def lolzTest(): Unit = {
14 | XML.loadString(lolz)
15 | }
16 |
17 | // Copied from https://msdn.microsoft.com/en-us/magazine/ee335713.aspx
18 | val lolz: String =
19 | """
20 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | |
32 | |]>
33 | |&lol9;
34 | |""".stripMargin
35 | }
36 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/ProcInstr.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 |
16 | /**
17 | * an XML node for processing instructions (PI)
18 | *
19 | * @author Burak Emir
20 | * @param target target name of this PI
21 | * @param proctext text contained in this node, may not contain "?>"
22 | */
23 | // Note: used by the Scala compiler.
24 | case class ProcInstr(target: String, proctext: String) extends SpecialNode {
25 | if (!Utility.isName(target))
26 | throw new IllegalArgumentException(s"$target must be an XML Name")
27 | if (proctext.contains("?>"))
28 | throw new IllegalArgumentException(s"""$proctext may not contain "?>"""")
29 | if (target.toLowerCase == "xml")
30 | throw new IllegalArgumentException(s"$target is reserved")
31 |
32 | final override def doCollectNamespaces: Boolean = false
33 | final override def doTransform: Boolean = false
34 |
35 | final override def label: String = "#PI"
36 | override def text: String = ""
37 |
38 | /**
39 | * appends "<?" target (" "+text)?+"?>"
40 | * to this stringbuffer.
41 | */
42 | override def buildString(sb: StringBuilder): StringBuilder =
43 | sb.append(s"$target${if (proctext.isEmpty) "" else s" $proctext"}?>")
44 | }
45 |
--------------------------------------------------------------------------------
/jvm/src/test/scala/scala/xml/parsing/XhtmlParserTest.scala:
--------------------------------------------------------------------------------
1 | package scala.xml
2 | package parsing
3 |
4 | import scala.io.Source
5 |
6 | import org.junit.Test
7 | import org.junit.Assert.assertEquals
8 |
9 | class XhtmlParserTest {
10 |
11 | @Test
12 | def issue259(): Unit = {
13 | val xml: String =
14 | """|
15 | |
16 | |
17 | |
18 | |
19 | |
20 | | Text
21 | |
22 | |""".stripMargin
23 |
24 | val expected: Elem =
25 |
26 |
27 |
28 |
29 | Text
30 |
31 |
32 |
33 | assertEquals(expected, XhtmlParser(Source.fromString(xml)).theSeq)
34 | }
35 |
36 | @Test
37 | def html4Strict(): Unit = {
38 | val xml: String =
39 | """|
41 | |
42 | |
43 | | Title
44 | |
45 | |
46 | | Text
47 | |
48 | |""".stripMargin
49 |
50 | val expected: Elem =
51 |
52 | Title
53 |
54 |
55 | Text
56 |
57 |
58 |
59 | assertEquals(expected, XhtmlParser(Source.fromString(xml)).theSeq)
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/Comment.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 |
16 | /**
17 | * The class `Comment` implements an XML node for comments.
18 | *
19 | * @author Burak Emir
20 | * @param commentText the text contained in this node, may not contain "--"
21 | * and the final character may not be `-` to prevent a closing span of `-->`
22 | * which is invalid. [[https://www.w3.org/TR/xml11//#IDA5CES]]
23 | */
24 | // Note: used by the Scala compiler.
25 | case class Comment(commentText: String) extends SpecialNode {
26 |
27 | override def label: String = "#REM"
28 | override def text: String = ""
29 | final override def doCollectNamespaces: Boolean = false
30 | final override def doTransform: Boolean = false
31 |
32 | if (commentText.contains("--"))
33 | throw new IllegalArgumentException(s"""text contains "--"""")
34 |
35 | if (commentText.nonEmpty && commentText.charAt(commentText.length - 1) == '-')
36 | throw new IllegalArgumentException("The final character of a XML comment may not be '-'. See https://www.w3.org/TR/xml11//#IDA5CES")
37 |
38 | /**
39 | * Appends "" to this string buffer.
40 | */
41 | override def buildString(sb: StringBuilder): StringBuilder =
42 | sb.append(s"")
43 | }
44 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/TextBuffer.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 |
16 | import scala.collection.Seq
17 | import scala.collection.immutable.{Seq => ISeq}
18 | import Utility.isSpace
19 |
20 | object TextBuffer {
21 | def fromString(str: String): TextBuffer = new TextBuffer().append(str)
22 | }
23 |
24 | /**
25 | * The class `TextBuffer` is for creating text nodes without surplus
26 | * whitespace. All occurrences of one or more whitespace in strings
27 | * appended with the `append` method will be replaced by a single space
28 | * character, and leading and trailing space will be removed completely.
29 | */
30 | class TextBuffer extends ScalaVersionSpecificTextBuffer {
31 | val sb: StringBuilder = new StringBuilder()
32 |
33 | /**
34 | * Appends this string to the text buffer, trimming whitespaces as needed.
35 | */
36 | def append(cs: Seq[Char]): this.type = {
37 | cs.foreach { c =>
38 | if (!isSpace(c)) sb.append(c)
39 | else if (sb.isEmpty || !isSpace(sb.last)) sb.append(' ')
40 | }
41 | this
42 | }
43 |
44 | /**
45 | * Returns an empty sequence if text is only whitespace.
46 | *
47 | * @return the text without whitespaces.
48 | */
49 | def toText: ScalaVersionSpecific.SeqOfText = sb.toString.trim match {
50 | case "" => Nil
51 | case s => ISeq(Text(s))
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/dtd/ValidationException.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 | package dtd
16 |
17 | case class ValidationException(e: String) extends Exception(e)
18 |
19 | /**
20 | * @author Burak Emir
21 | */
22 | object MakeValidationException {
23 | def fromFixedAttribute(k: String, value: String, actual: String): ValidationException =
24 | ValidationException(s"""value of attribute $k FIXED to "$value", but document tries "$actual"""")
25 |
26 | def fromNonEmptyElement(): ValidationException =
27 | ValidationException("element should be *empty*")
28 |
29 | def fromUndefinedElement(label: String): ValidationException =
30 | ValidationException(s"""element "$label" not allowed here""")
31 |
32 | def fromUndefinedAttribute(key: String): ValidationException =
33 | ValidationException(s"attribute $key not allowed here")
34 |
35 | def fromMissingAttribute(allKeys: Set[String]): ValidationException = {
36 | val sb: StringBuilder = new StringBuilder("missing value for REQUIRED attribute")
37 | if (allKeys.size > 1) sb.append('s')
38 | allKeys.foreach(k => sb.append(s"'$k'"))
39 | ValidationException(sb.toString)
40 | }
41 |
42 | def fromMissingAttribute(key: String, tpe: String): ValidationException =
43 | ValidationException(s"missing value for REQUIRED attribute $key of type $tpe")
44 | }
45 |
--------------------------------------------------------------------------------
/shared/src/main/scala-2.12/scala/xml/ScalaVersionSpecific.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala.xml
14 |
15 | import scala.collection.{SeqLike, mutable}
16 | import scala.collection.generic.CanBuildFrom
17 |
18 | private[xml] object ScalaVersionSpecific {
19 | import NodeSeq.Coll
20 | type CBF[-From, -A, +C] = CanBuildFrom[From, A, C]
21 | object NodeSeqCBF extends CanBuildFrom[Coll, Node, NodeSeq] {
22 | override def apply(from: Coll): mutable.Builder[Node, NodeSeq] = NodeSeq.newBuilder
23 | override def apply(): mutable.Builder[Node, NodeSeq] = NodeSeq.newBuilder
24 | }
25 | type SeqOfNode = scala.collection.Seq[Node]
26 | type SeqOfText = scala.collection.Seq[Text]
27 | }
28 |
29 | private[xml] trait ScalaVersionSpecificNodeSeq extends SeqLike[Node, NodeSeq] { self: NodeSeq =>
30 | /** Creates a list buffer as builder for this class */
31 | override protected[this] def newBuilder: mutable.Builder[Node, NodeSeq] = NodeSeq.newBuilder
32 | }
33 |
34 | private[xml] trait ScalaVersionSpecificNodeBuffer { self: NodeBuffer =>
35 | override def stringPrefix: String = "NodeBuffer"
36 | }
37 |
38 | private[xml] trait ScalaVersionSpecificNode { self: Node => }
39 |
40 | private[xml] trait ScalaVersionSpecificMetaData { self: MetaData => }
41 |
42 | private[xml] trait ScalaVersionSpecificTextBuffer { self: TextBuffer => }
43 |
44 | private[xml] trait ScalaVersionSpecificUtility { self: Utility.type => }
45 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/Atom.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 |
16 | import scala.collection.Seq
17 |
18 | /**
19 | * The class `Atom` provides an XML node for text (`PCDATA`).
20 | * It is used in both non-bound and bound XML representations.
21 | *
22 | * @author Burak Emir
23 | * @param data the text contained in this node, may not be `'''null'''`.
24 | */
25 | class Atom[+A](val data: A) extends SpecialNode with Serializable {
26 | if (data == null)
27 | throw new IllegalArgumentException(s"cannot construct ${getClass.getSimpleName} with null")
28 |
29 | override protected def basisForHashCode: Seq[Any] = Seq(data)
30 |
31 | override def strict_==(other: Equality): Boolean = other match {
32 | case x: Atom[?] => data == x.data
33 | case _ => false
34 | }
35 |
36 | override def canEqual(other: Any): Boolean = other match {
37 | case _: Atom[?] => true
38 | case _ => false
39 | }
40 |
41 | final override def doCollectNamespaces: Boolean = false
42 | final override def doTransform: Boolean = false
43 |
44 | override def label: String = "#PCDATA"
45 |
46 | /**
47 | * Returns text, with some characters escaped according to the XML
48 | * specification.
49 | */
50 | override def buildString(sb: StringBuilder): StringBuilder =
51 | Utility.escape(data.toString, sb)
52 |
53 | override def text: String = data.toString
54 | }
55 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/PCData.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 |
16 | /**
17 | * This class (which is not used by all XML parsers, but always used by the
18 | * XHTML one) represents parseable character data, which appeared as CDATA
19 | * sections in the input and is to be preserved as CDATA section in the output.
20 | *
21 | * @author Burak Emir
22 | */
23 | // Note: used by the Scala compiler (before Scala 3).
24 | class PCData(data: String) extends Atom[String](data) {
25 |
26 | /**
27 | * Returns text, with some characters escaped according to the XML
28 | * specification.
29 | *
30 | * @param sb the input string buffer associated to some XML element
31 | * @return the input string buffer with the formatted CDATA section
32 | */
33 | override def buildString(sb: StringBuilder): StringBuilder = {
34 | val dataStr: String = data.replaceAll("]]>", "]]]]>")
35 | sb.append(s"")
36 | }
37 | }
38 |
39 | /**
40 | * This singleton object contains the `apply`and `unapply` methods for
41 | * convenient construction and deconstruction.
42 | *
43 | * @author Burak Emir
44 | */
45 | // Note: used by the Scala compiler (before Scala 3).
46 | object PCData {
47 | def apply(data: String): PCData = new PCData(data)
48 | def unapply(other: Any): Option[String] = other match {
49 | case x: PCData => Some(x.data)
50 | case _ => None
51 | }
52 | }
53 |
54 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/dtd/impl/DetWordAutom.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml.dtd.impl
15 |
16 | /**
17 | * A deterministic automaton. States are integers, where
18 | * 0 is always the only initial state. Transitions are represented
19 | * in the delta function. A default transitions is one that
20 | * is taken when no other transition can be taken.
21 | * All states are reachable. Accepting states are those for which
22 | * the partial function 'finals' is defined.
23 | *
24 | * @author Burak Emir
25 | */
26 | // TODO: still used in ContentModel -- @deprecated("This class will be removed", "2.10.0")
27 | private[dtd] abstract class DetWordAutom[T <: AnyRef] {
28 | val nstates: Int
29 | val finals: Array[Int]
30 | val delta: Array[scala.collection.mutable.Map[T, Int]]
31 | val default: Array[Int]
32 |
33 | def isFinal(q: Int): Boolean = finals(q) != 0
34 | def isSink(q: Int): Boolean = delta(q).isEmpty && default(q) == q
35 | def next(q: Int, label: T): Int = delta(q).getOrElse(label, default(q))
36 |
37 | override def toString: String = {
38 | val map: Map[Int, Int] = finals.zipWithIndex.map(_.swap).toMap
39 | val sb: StringBuilder = new StringBuilder(s"[DetWordAutom nstates=$nstates finals=$map delta=\n")
40 |
41 | for (i <- 0.until(nstates)) {
42 | sb.append(s"$i->${delta(i)}\n")
43 | if (i < default.length)
44 | sb.append(s"_>${default(i)}\n")
45 | }
46 | sb.toString
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/dtd/impl/WordExp.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml.dtd.impl
15 |
16 | /**
17 | * The class `WordExp` provides regular word expressions.
18 | *
19 | * Users have to instantiate type member `_regexpT <;: RegExp`
20 | * (from class `Base`) and a type member `_labelT <;: Label`.
21 | *
22 | * Here is a short example:
23 | * {{{
24 | * import scala.util.regexp._
25 | * import scala.util.automata._
26 | * object MyLang extends WordExp {
27 | * type _regexpT = RegExp
28 | * type _labelT = MyChar
29 | *
30 | * case class MyChar(c:Char) extends Label
31 | * }
32 | * import MyLang._
33 | * // (a* | b)*
34 | * val rex = Star(Alt(Star(Letter(MyChar('a'))),Letter(MyChar('b'))))
35 | * object MyBerriSethi extends WordBerrySethi {
36 | * override val lang = MyLang
37 | * }
38 | * val nfa = MyBerriSethi.automatonFrom(Sequ(rex), 1)
39 | * }}}
40 | *
41 | * @author Burak Emir
42 | */
43 | // TODO: still used in ContentModel -- @deprecated("This class will be removed", "2.10.0")
44 | private[dtd] abstract class WordExp extends Base {
45 |
46 | abstract class Label
47 |
48 | override type _regexpT <: RegExp
49 | type _labelT <: Label
50 |
51 | case class Letter(a: _labelT) extends RegExp {
52 | final override lazy val isNullable: Boolean = false
53 | var pos: Int = -1
54 | }
55 |
56 | case class Wildcard() extends RegExp {
57 | final override lazy val isNullable: Boolean = false
58 | var pos: Int = -1
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/parsing/ConstructingParser.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 | package parsing
16 |
17 | import java.io.File
18 | import scala.io.Source
19 |
20 | object ConstructingParser {
21 | def fromFile(inp: File, preserveWS: Boolean): ConstructingParser =
22 | new ConstructingParser(Source.fromFile(inp), preserveWS).initialize
23 |
24 | def fromSource(inp: Source, preserveWS: Boolean): ConstructingParser =
25 | new ConstructingParser(inp, preserveWS).initialize
26 | }
27 |
28 | /**
29 | * An xml parser. parses XML and invokes callback methods of a MarkupHandler.
30 | * Don't forget to call next.ch on a freshly instantiated parser in order to
31 | * initialize it. If you get the parser from the object method, initialization
32 | * is already done for you.
33 | *
34 | * {{{
35 | * object parseFromURL {
36 | * def main(args: Array[String]) {
37 | * val url = args(0)
38 | * val src = scala.io.Source.fromURL(url)
39 | * val cpa = scala.xml.parsing.ConstructingParser.fromSource(src, false) // fromSource initializes automatically
40 | * val doc = cpa.document()
41 | *
42 | * // let's see what it is
43 | * val ppr = new scala.xml.PrettyPrinter(80, 5)
44 | * val ele = doc.docElem
45 | * println("finished parsing")
46 | * val out = ppr.format(ele)
47 | * println(out)
48 | * }
49 | * }
50 | * }}}
51 | */
52 | class ConstructingParser(override val input: Source, override val preserveWS: Boolean)
53 | extends ConstructingHandler
54 | with ExternalSources
55 | with MarkupParser
56 |
--------------------------------------------------------------------------------
/shared/src/main/scala-3/scala/xml/ScalaVersionSpecificReturnTypes.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala.xml
14 |
15 | /*
16 | Unlike other Scala-version-specific things, this class is not filling any gaps in capabilities
17 | between different versions of Scala; instead, it mostly documents the types that different versions of the
18 | Scala compiler inferred in the unfortunate absence of the explicit type annotations.
19 | What should have been specified explicitly is given in the comments;
20 | next time we break binary compatibility the types should be changed in the code and this class removed.
21 | */
22 | private[xml] object ScalaVersionSpecificReturnTypes { // should be
23 | type ExternalIDAttribute = MetaData // Null.type
24 | type NoExternalIDId = String // scala.Null
25 | type NodeNoAttributes = MetaData // Null.type
26 | type NullFilter = MetaData // Null.type
27 | type NullGetNamespace = String // scala.Null
28 | type NullNext = MetaData // scala.Null
29 | type NullKey = String // scala.Null
30 | type NullValue = scala.collection.immutable.Seq[Node] // scala.Null
31 | type NullApply3 = scala.collection.immutable.Seq[Node] // scala.Null
32 | type NullRemove = MetaData // Null.type
33 | type SpecialNodeChild = scala.collection.immutable.Seq[Node] // Nil.type
34 | type GroupChild = scala.collection.immutable.Seq[Node] // Nothing
35 | }
36 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/NodeBuffer.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 |
16 | /**
17 | * This class acts as a Buffer for nodes. If it is used as a sequence of
18 | * nodes `Seq[Node]`, it must be ensured that no updates occur after that
19 | * point, because `scala.xml.Node` is assumed to be immutable.
20 | *
21 | * Despite this being a sequence, don't use it as key in a hashtable.
22 | * Calling the hashcode function will result in a runtime error.
23 | *
24 | * @author Burak Emir
25 | */
26 | // Note: used by the Scala compiler.
27 | class NodeBuffer extends scala.collection.mutable.ArrayBuffer[Node] with ScalaVersionSpecificNodeBuffer {
28 | /**
29 | * Append given object to this buffer, returns reference on this
30 | * `NodeBuffer` for convenience. Some rules apply:
31 | * - If argument `o` is `'''null'''`, it is ignored.
32 | * - If it is an `Iterator` or `Iterable`, its elements will be added.
33 | * - If `o` is a node, it is added as it is.
34 | * - If it is anything else, it gets wrapped in an [[scala.xml.Atom]].
35 | *
36 | * @param o converts to an xml node and adds to this node buffer
37 | * @return this nodebuffer
38 | */
39 | def &+(o: Any): this.type = {
40 | o match {
41 | case null | _: Unit | Text("") => // ignore
42 | case it: Iterator[?] => it.foreach(&+)
43 | case n: Node => super.+=(n)
44 | case ns: Iterable[?] => this &+ ns.iterator
45 | case ns: Array[?] => this &+ ns.iterator
46 | case d => super.+=(new Atom(d))
47 | }
48 | this
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/transform/BasicTransformer.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 | package transform
16 |
17 | import scala.collection.Seq
18 |
19 | /**
20 | * A class for XML transformations.
21 | *
22 | * @author Burak Emir
23 | */
24 | abstract class BasicTransformer extends (Node => Node) {
25 | protected def unchanged(n: Node, ns: Seq[Node]): Boolean =
26 | ns.length == 1 && (ns.head == n)
27 |
28 | /**
29 | * Call transform(Node) for each node in ns, append results
30 | * to NodeBuffer.
31 | */
32 | def transform(it: Iterator[Node], nb: NodeBuffer): Seq[Node] =
33 | it.foldLeft(nb)(_ ++= transform(_))
34 |
35 | /**
36 | * Call transform(Node) to each node in ns, yield ns if nothing changes,
37 | * otherwise a new sequence of concatenated results.
38 | */
39 | def transform(ns: Seq[Node]): Seq[Node] = {
40 | val changed: Seq[Node] = ns.flatMap(transform)
41 | if (changed.length != ns.length || changed.zip(ns).exists(p => p._1 != p._2)) changed
42 | else ns
43 | }
44 |
45 | def transform(n: Node): Seq[Node] =
46 | if (n.doTransform) n match {
47 | case Group(xs) => Group(transform(xs)) // un-group the hack Group tag
48 | case _ =>
49 | val ch = n.child
50 | val nch = transform(ch)
51 |
52 | if (ch.eq(nch)) n
53 | else Elem(n.prefix, n.label, n.attributes, n.scope, nch.isEmpty, nch.toSeq: _*)
54 | }
55 | else n
56 |
57 | override def apply(n: Node): Node = {
58 | val seq: Seq[Node] = transform(n)
59 | if (seq.length > 1)
60 | throw new UnsupportedOperationException("transform must return single node for root")
61 | else seq.head
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/Group.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 |
16 | import scala.collection.Seq
17 |
18 | /**
19 | * A hack to group XML nodes in one node for output.
20 | *
21 | * @author Burak Emir
22 | */
23 | // Note: used by the Scala compiler.
24 | final case class Group(nodes: Seq[Node]) extends Node {
25 | // Ideally, the `immutable.Seq` would be stored as a field.
26 | // But evolving the case class and remaining binary compatible is very difficult
27 | // Since `Group` is used rarely, call `toSeq` on the field.
28 | // In practice, it should not matter - the `nodes` field anyway contains an `immutable.Seq`.
29 | override def theSeq: ScalaVersionSpecific.SeqOfNode = nodes.toSeq
30 |
31 | override def canEqual(other: Any): Boolean = other match {
32 | case _: Group => true
33 | case _ => false
34 | }
35 |
36 | override def strict_==(other: Equality): Boolean = other match {
37 | case Group(xs) => nodes.sameElements(xs)
38 | case _ => false
39 | }
40 |
41 | override protected def basisForHashCode: Seq[Node] = nodes
42 |
43 | /**
44 | * Since Group is very much a hack it throws an exception if you
45 | * try to do anything with it.
46 | */
47 | private def fail(msg: String): Nothing = throw new UnsupportedOperationException(s"class Group does not support method '$msg'")
48 |
49 | override def label: Nothing = fail("label")
50 | override def attributes: Nothing = fail("attributes")
51 | override def namespace: Nothing = fail("namespace")
52 | override def child: ScalaVersionSpecificReturnTypes.GroupChild = fail("child")
53 | def buildString(sb: StringBuilder): Nothing = fail("toString(StringBuilder)")
54 | }
55 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/parsing/NoBindingFactoryAdapter.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 | package parsing
16 |
17 | import scala.collection.Seq
18 | import factory.NodeFactory
19 |
20 | /**
21 | * nobinding adaptor providing callbacks to parser to create elements.
22 | * implements hash-consing
23 | */
24 | class NoBindingFactoryAdapter extends FactoryAdapter with NodeFactory[Elem] {
25 | /** True. Every XML node may contain text that the application needs */
26 | override def nodeContainsText(label: String): Boolean = true
27 |
28 | /** From NodeFactory. Constructs an instance of scala.xml.Elem -- TODO: deprecate as in Elem */
29 | override protected def create(pre: String, label: String, attrs: MetaData, scope: NamespaceBinding, children: Seq[Node]): Elem =
30 | Elem(pre, label, attrs, scope, children.isEmpty, children.toSeq: _*)
31 |
32 | /** From FactoryAdapter. Creates a node. never creates the same node twice, using hash-consing.
33 | TODO: deprecate as in Elem, or forward to create?? */
34 | override def createNode(pre: String, label: String, attrs: MetaData, scope: NamespaceBinding, children: List[Node]): Elem =
35 | Elem(pre, label, attrs, scope, children.isEmpty, children: _*)
36 |
37 | /** Creates a text node. */
38 | override def createText(text: String): Text = makeText(text)
39 |
40 | /** Creates a processing instruction. */
41 | override def createProcInstr(target: String, data: String): Seq[ProcInstr] = makeProcInstr(target, data)
42 |
43 | /** Creates a comment. */
44 | override def createComment(characters: String): Seq[Comment] = makeComment(characters)
45 |
46 | /** Creates a cdata. */
47 | override def createPCData(characters: String): PCData = makePCData(characters)
48 | }
49 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/dtd/impl/Base.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml.dtd.impl
15 |
16 | /**
17 | * Basic regular expressions.
18 | *
19 | * @author Burak Emir
20 | */
21 |
22 | @deprecated("This class will be removed", "2.10.0")
23 | private[dtd] abstract class Base {
24 | type _regexpT <: RegExp
25 |
26 | abstract class RegExp {
27 | def isNullable: Boolean
28 | }
29 |
30 | object Alt {
31 | /** `Alt( R,R,R* )`. */
32 | def apply(rs: _regexpT*): Alt =
33 | if (rs.size < 2) throw new SyntaxError("need at least 2 branches in Alt")
34 | else new Alt(rs: _*)
35 | // Can't enforce that statically without changing the interface
36 | // def apply(r1: _regexpT, r2: _regexpT, rs: _regexpT*) = new Alt(Seq(r1, r2) ++ rs: _*)
37 | def unapplySeq(x: Alt): Some[Seq[_regexpT]] = Some(x.rs)
38 | }
39 |
40 | class Alt private (val rs: _regexpT*) extends RegExp {
41 | final override val isNullable: Boolean = rs.exists(_.isNullable)
42 | }
43 |
44 | object Sequ {
45 | /** Sequ( R,R* ) */
46 | def apply(rs: _regexpT*): RegExp = if (rs.isEmpty) Eps else new Sequ(rs: _*)
47 | def unapplySeq(x: Sequ): Some[Seq[_regexpT]] = Some(x.rs)
48 | }
49 |
50 | class Sequ private (val rs: _regexpT*) extends RegExp {
51 | final override val isNullable: Boolean = rs.forall(_.isNullable)
52 | }
53 |
54 | case class Star(r: _regexpT) extends RegExp {
55 | final override lazy val isNullable: Boolean = true
56 | }
57 |
58 | // The empty Sequ.
59 | case object Eps extends RegExp {
60 | final override lazy val isNullable: Boolean = true
61 | override def toString: String = "Eps"
62 | }
63 |
64 | /** this class can be used to add meta information to regexps. */
65 | class Meta(r1: _regexpT) extends RegExp {
66 | final override val isNullable: Boolean = r1.isNullable
67 | def r: _regexpT = r1
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/include/XIncludeException.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 | package include
16 |
17 | /**
18 | * `XIncludeException` is the generic superclass for all checked exceptions
19 | * that may be thrown as a result of a violation of XInclude's rules.
20 | *
21 | * Constructs an `XIncludeException` with the specified detail message.
22 | * The error message string `message` can later be retrieved by the
23 | * `{@link java.lang.Throwable#getMessage}`
24 | * method of class `java.lang.Throwable`.
25 | *
26 | * @param message the detail message.
27 | */
28 | class XIncludeException(message: String) extends Exception(message) {
29 |
30 | /**
31 | * uses `'''null'''` as its error detail message.
32 | */
33 | def this() = this(null)
34 |
35 | private var rootCause: Throwable = _
36 |
37 | /**
38 | * When an `IOException`, `MalformedURLException` or other generic
39 | * exception is thrown while processing an XML document for XIncludes,
40 | * it is customarily replaced by some form of `XIncludeException`.
41 | * This method allows you to store the original exception.
42 | *
43 | * @param nestedException the underlying exception which
44 | * caused the XIncludeException to be thrown
45 | */
46 | def setRootCause(nestedException: Throwable): Unit = {
47 | this.rootCause = nestedException
48 | }
49 |
50 | /**
51 | * When an `IOException`, `MalformedURLException` or other generic
52 | * exception is thrown while processing an XML document for XIncludes,
53 | * it is customarily replaced by some form of `XIncludeException`.
54 | * This method allows you to retrieve the original exception.
55 | * It returns null if no such exception caused this `XIncludeException`.
56 | *
57 | * @return Throwable the underlying exception which caused the
58 | * `XIncludeException` to be thrown
59 | */
60 | def getRootCause: Throwable = this.rootCause
61 | }
62 |
--------------------------------------------------------------------------------
/shared/src/test/scala/scala/xml/PrintEmptyElementsTest.scala:
--------------------------------------------------------------------------------
1 | package scala.xml
2 |
3 | import org.junit.Test
4 | import JUnitAssertsForXML.assertEquals
5 |
6 | class PrintEmptyElementsTest {
7 |
8 | @Test
9 | def representEmptyXMLElementsInShortForm(): Unit = {
10 | val expected: String =
11 | """|
12 | |
13 | |
14 | |
15 | |
16 | |is pretty cool
17 | |""".stripMargin
18 | // the xml snippet is not indented because indentation affects pretty printing
19 | // results
20 | val actual: NodeSeq =
21 |
22 |
23 |
24 |
25 |
26 | is pretty cool
27 |
28 | assertEquals(expected, actual)
29 | }
30 |
31 | @Test
32 | def programmaticLong(): Unit = {
33 | assertEquals(" ",
34 | Elem(null, "emptiness", Null, TopScope, minimizeEmpty = false) ++ Text(" ") ++ Comment("programmatic long"))
35 | }
36 |
37 | @Test
38 | def programmaticShort(): Unit = {
39 | assertEquals(" ",
40 | Elem(null, "vide", Null, TopScope, minimizeEmpty = true) ++ Text(" ") ++ Comment("programmatic short"))
41 | }
42 |
43 | @Test
44 | def programmaticShortWithAttribute(): Unit = {
45 | assertEquals(""" """,
46 | Elem(null, "elem", Attribute("attr", Text("value"), Null), TopScope, minimizeEmpty = true) ++ Text(" ") ++ Comment ("programmatic short with attribute"))
47 | }
48 |
49 | @Test
50 | def programmaticLongWithAttribute(): Unit = {
51 | assertEquals(""" """,
52 | Elem(null, "elem2", Attribute("attr2", Text("value2"), Null), TopScope, minimizeEmpty = false) ++ Text(" ") ++ Comment ("programmatic long with attribute"))
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/shared/src/test/scala/scala/xml/MetaDataTest.scala:
--------------------------------------------------------------------------------
1 | package scala.xml
2 |
3 | import org.junit.Test
4 | import org.junit.Assert.assertEquals
5 |
6 | class MetaDataTest {
7 |
8 | @Test
9 | def absentElementPrefixed1(): Unit = {
10 | // type ascription to help overload resolution pick the right variant
11 | assertEquals(null: Object, Null("za://foo.com", TopScope, "bar"))
12 | assertEquals(null, Null("bar"))
13 | }
14 |
15 | @Test
16 | def absentElementPrefixed2(): Unit = {
17 | assertEquals(Option.empty, Null.get("za://foo.com", TopScope, "bar" ))
18 | assertEquals(Option.empty, Null.get("bar"))
19 | }
20 |
21 | @Test
22 | def presentElement1(): Unit = {
23 | val x: PrefixedAttribute = new PrefixedAttribute("zo","bar", new Atom(42), Null)
24 | val s: NamespaceBinding = NamespaceBinding("zo","za://foo.com", TopScope)
25 | assertEquals(new Atom(42), x("za://foo.com", s, "bar" ))
26 | assertEquals(null, x("bar"))
27 | assertEquals(Some(new Atom(42)), x.get("za://foo.com", s, "bar"))
28 | assertEquals(Option.empty, x.get("bar"))
29 | }
30 |
31 | @Test
32 | def presentElement2(): Unit = {
33 | val s: NamespaceBinding = NamespaceBinding("zo","za://foo.com", TopScope)
34 | val x1: PrefixedAttribute = new PrefixedAttribute("zo","bar", new Atom(42), Null)
35 | val x: UnprefixedAttribute = new UnprefixedAttribute("bar","meaning", x1)
36 | assertEquals(null, x(null, s, "bar"))
37 | assertEquals(Text("meaning"), x("bar"))
38 | assertEquals(None, x.get(null, s, "bar" ))
39 | assertEquals(Some(Text("meaning")), x.get("bar"))
40 | }
41 |
42 | @Test
43 | def attributeExtractor(): Unit = {
44 | def domatch(x: Node): Node = {
45 | x match {
46 | case Node("foo", md @ UnprefixedAttribute(_, value, _), _*) if value.nonEmpty =>
47 | md("bar")(0)
48 | case _ => new Atom(3)
49 | }
50 | }
51 | val z: Elem =
52 | val z2: Elem =
53 | assertEquals(Text("gar"), domatch(z))
54 | assertEquals(new Atom(3), domatch(z2))
55 | }
56 |
57 | @Test
58 | def reverseTest(): Unit = {
59 | assertEquals("", Null.reverse.toString)
60 | assertEquals(""" b="c"""", .attributes.reverse.toString)
61 | assertEquals(""" d="e" b="c"""", .attributes.reverse.toString)
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/dtd/impl/Inclusion.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml.dtd.impl
15 |
16 | import scala.collection.Seq
17 |
18 | /**
19 | * A fast test of language inclusion between minimal automata.
20 | * inspired by the ''AMoRE automata library''.
21 | *
22 | * @author Burak Emir
23 | */
24 | @deprecated("This class will be removed", "2.10.0")
25 | private[dtd] trait Inclusion[A <: AnyRef] {
26 |
27 | val labels: Seq[A]
28 |
29 | /**
30 | * Returns true if `dfa1` is included in `dfa2`.
31 | */
32 | def inclusion(dfa1: DetWordAutom[A], dfa2: DetWordAutom[A]): Boolean = {
33 |
34 | def encode(q1: Int, q2: Int): Int = 1 + q1 + q2 * dfa1.nstates
35 | def decode2(c: Int): Int = (c - 1) / dfa1.nstates //integer division
36 | def decode1(c: Int): Int = (c - 1) % dfa1.nstates
37 |
38 | var q1: Int = 0 //dfa1.initstate; // == 0
39 | var q2: Int = 0 //dfa2.initstate; // == 0
40 |
41 | val max: Int = 1 + dfa1.nstates * dfa2.nstates
42 | val mark: Array[Int] = new Array[Int](max)
43 |
44 | var result: Boolean = true
45 | var current: Int = encode(q1, q2)
46 | var last: Int = current
47 | mark(last) = max // mark (q1,q2)
48 | while (current != 0 && result) {
49 | //Console.println("current = [["+q1+" "+q2+"]] = "+current);
50 | for (letter <- labels) {
51 | val r1: Int = dfa1.next(q1, letter)
52 | val r2: Int = dfa2.next(q2, letter)
53 | if (dfa1.isFinal(r1) && !dfa2.isFinal(r2))
54 | result = false
55 | val test: Int = encode(r1, r2)
56 | //Console.println("test = [["+r1+" "+r2+"]] = "+test);
57 | if (mark(test) == 0) {
58 | mark(last) = test
59 | mark(test) = max
60 | last = test
61 | }
62 | }
63 | val ncurrent: Int = mark(current)
64 | if (ncurrent != max) {
65 | q1 = decode1(ncurrent)
66 | q2 = decode2(ncurrent)
67 | current = ncurrent
68 | } else {
69 | current = 0
70 | }
71 | }
72 | result
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/shared/src/test/scala/scala/xml/XMLSyntaxTest.scala:
--------------------------------------------------------------------------------
1 | package scala.xml
2 |
3 | import scala.collection.Seq
4 | import org.junit.Test
5 | import org.junit.Assert.assertTrue
6 | import org.junit.Assert.assertFalse
7 | import org.junit.Assert.assertEquals
8 |
9 | class XMLSyntaxTest {
10 |
11 | private def handle[A](x: Node): A = {
12 | x.child(0).asInstanceOf[Atom[A]].data
13 | }
14 |
15 | @Test
16 | def test1(): Unit = {
17 | val xNull: Elem = {null} // these used to be Atom(unit), changed to empty children
18 | assertTrue(xNull.child.sameElements(Nil))
19 |
20 | val x0: Elem = {} // these used to be Atom(unit), changed to empty children
21 | val x00: Elem = { } // dto.
22 | val xa: Elem = { "world" }
23 |
24 | assertTrue(x0.child.sameElements(Nil))
25 | assertTrue(x00.child.sameElements(Nil))
26 | assertEquals("world", handle[String](xa))
27 |
28 | val xb: Elem = { 1.5 }
29 | assertEquals(1.5, handle[Double](xb), 0.0)
30 |
31 | val xc: Elem = { 5 }
32 | assertEquals(5, handle[Int](xc).toLong)
33 |
34 | val xd: Elem = { true }
35 | assertEquals(true, handle[Boolean](xd))
36 |
37 | val xe: Elem = { 5:Short }
38 | assertEquals((5:Short).toLong, handle[Short](xe).toLong)
39 |
40 | val xf: Elem = { val x = 27; x }
41 | assertEquals(27, handle[Int](xf).toLong)
42 |
43 | val xg: Elem = { List(1,2,3,4) }
44 | assertEquals("1 2 3 4", xg.toString)
45 | assertFalse(xg.child.map(_.isInstanceOf[Text]).exists(identity))
46 |
47 | val xh: Elem = { for(x <- List(1,2,3,4) if x % 2 == 0) yield x }
48 | assertEquals("2 4", xh.toString)
49 | assertFalse(xh.child.map(_.isInstanceOf[Text]).exists(identity))
50 | }
51 |
52 | /** see SVN r13821 (emir): support for ,
53 | * so that Options can be used for optional attributes.
54 | */
55 | @Test
56 | def test2(): Unit = {
57 | val x1: Option[Seq[Node]] = Some(hello)
58 | val n1: Elem =
59 | assertEquals(x1, n1.attribute("key"))
60 |
61 | val x2: Option[Seq[Node]] = None
62 | val n2: Elem =
63 | assertEquals(x2, n2.attribute("key"))
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/factory/NodeFactory.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 | package factory
16 |
17 | import scala.collection.Seq
18 |
19 | trait NodeFactory[A <: Node] {
20 | val ignoreComments: Boolean = false
21 | val ignoreProcInstr: Boolean = false
22 |
23 | /* default behaviour is to use hash-consing */
24 | val cache: scala.collection.mutable.HashMap[Int, List[A]] = new scala.collection.mutable.HashMap[Int, List[A]]
25 |
26 | protected def create(pre: String, name: String, attrs: MetaData, scope: NamespaceBinding, children: Seq[Node]): A
27 |
28 | protected def construct(hash: Int, old: List[A], pre: String, name: String, attrSeq: MetaData, scope: NamespaceBinding, children: Seq[Node]): A = {
29 | val el: A = create(pre, name, attrSeq, scope, children)
30 | cache.update(hash, el :: old)
31 | el
32 | }
33 |
34 | def eqElements(ch1: Seq[Node], ch2: Seq[Node]): Boolean =
35 | ch1.view.zipAll(ch2.view, null, null).forall { case (x, y) => x.eq(y) }
36 |
37 | def nodeEquals(n: Node, pre: String, name: String, attrSeq: MetaData, scope: NamespaceBinding, children: Seq[Node]): Boolean =
38 | n.prefix == pre &&
39 | n.label == name &&
40 | n.attributes == attrSeq &&
41 | // scope?
42 | eqElements(n.child, children)
43 |
44 | def makeNode(pre: String, name: String, attrSeq: MetaData, scope: NamespaceBinding, children: Seq[Node]): A = {
45 | val hash: Int = Utility.hashCode(pre, name, attrSeq.##, scope.##, children)
46 | def cons(old: List[A]): A = construct(hash, old, pre, name, attrSeq, scope, children)
47 |
48 | cache.get(hash) match {
49 | case Some(list) => // find structurally equal
50 | list.find(nodeEquals(_, pre, name, attrSeq, scope, children)) match {
51 | case Some(x) => x
52 | case _ => cons(list)
53 | }
54 | case None => cons(Nil)
55 | }
56 | }
57 |
58 | def makeText(s: String): Text = Text(s)
59 | def makePCData(s: String): PCData =
60 | PCData(s)
61 | def makeComment(s: String): Seq[Comment] =
62 | if (ignoreComments) Nil else List(Comment(s))
63 | def makeProcInstr(t: String, s: String): Seq[ProcInstr] =
64 | if (ignoreProcInstr) Nil else List(ProcInstr(t, s))
65 | }
66 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/PrefixedAttribute.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 |
16 | import scala.collection.Seq
17 |
18 | /**
19 | * prefixed attributes always have a non-null namespace.
20 | *
21 | * @param pre
22 | * @param key
23 | * @param value the attribute value
24 | * @param next1
25 | */
26 | // Note: used by the Scala compiler.
27 | class PrefixedAttribute(
28 | override val pre: String,
29 | override val key: String,
30 | _value: Seq[Node],
31 | val next1: MetaData
32 | )
33 | extends Attribute
34 | {
35 | override val value: ScalaVersionSpecific.SeqOfNode = if (_value == null) null else _value match {
36 | case ns: ScalaVersionSpecific.SeqOfNode => ns
37 | case _ => _value.toVector
38 | }
39 |
40 | override val next: MetaData = if (value != null) next1 else next1.remove(key)
41 |
42 | /** same as this(pre, key, Text(value), next), or no attribute if value is null */
43 | def this(pre: String, key: String, value: String, next: MetaData) =
44 | this(pre, key, if (value != null) Text(value) else null: NodeSeq, next)
45 |
46 | /** same as this(pre, key, value.get, next), or no attribute if value is None */
47 | def this(pre: String, key: String, value: Option[Seq[Node]], next: MetaData) =
48 | this(pre, key, value.orNull, next)
49 |
50 | /**
51 | * Returns a copy of this unprefixed attribute with the given
52 | * next field.
53 | */
54 | override def copy(next: MetaData): PrefixedAttribute =
55 | new PrefixedAttribute(pre, key, value, next)
56 |
57 | override def getNamespace(owner: Node): String =
58 | owner.getNamespace(pre)
59 |
60 | /** forwards the call to next (because caller looks for unprefixed attribute */
61 | override def apply(key: String): ScalaVersionSpecific.SeqOfNode = next(key)
62 |
63 | /**
64 | * gets attribute value of qualified (prefixed) attribute with given key
65 | */
66 | override def apply(namespace: String, scope: NamespaceBinding, key: String): ScalaVersionSpecific.SeqOfNode =
67 | if (key == this.key && scope.getURI(pre) == namespace)
68 | value
69 | else
70 | next(namespace, scope, key)
71 | }
72 |
73 | object PrefixedAttribute {
74 | def unapply(x: PrefixedAttribute): Some[(String, String, Seq[Node], MetaData)] = Some((x.pre, x.key, x.value, x.next))
75 | }
76 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/UnprefixedAttribute.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 |
16 | import scala.collection.Seq
17 |
18 | /**
19 | * Unprefixed attributes have the null namespace, and no prefix field
20 | *
21 | * @author Burak Emir
22 | */
23 | // Note: used by the Scala compiler.
24 | class UnprefixedAttribute(
25 | override val key: String,
26 | _value: Seq[Node],
27 | next1: MetaData
28 | )
29 | extends Attribute
30 | {
31 | override val value: ScalaVersionSpecific.SeqOfNode = if (_value == null) null else _value match {
32 | case ns: ScalaVersionSpecific.SeqOfNode => ns
33 | case _ => _value.toVector
34 | }
35 |
36 | final override val pre: scala.Null = null
37 | override val next: MetaData = if (value != null) next1 else next1.remove(key)
38 |
39 | /** same as this(key, Text(value), next), or no attribute if value is null */
40 | def this(key: String, value: String, next: MetaData) =
41 | this(key, if (value != null) Text(value) else null: NodeSeq, next)
42 |
43 | /** same as this(key, value.get, next), or no attribute if value is None */
44 | def this(key: String, value: Option[Seq[Node]], next: MetaData) =
45 | this(key, value.orNull, next)
46 |
47 | /** returns a copy of this unprefixed attribute with the given next field*/
48 | override def copy(next: MetaData): UnprefixedAttribute = new UnprefixedAttribute(key, value, next)
49 |
50 | final override def getNamespace(owner: Node): String = null
51 |
52 | /**
53 | * Gets value of unqualified (unprefixed) attribute with given key, null if not found
54 | *
55 | * @param key
56 | * @return value as Seq[Node] if key is found, null otherwise
57 | */
58 | override def apply(key: String): ScalaVersionSpecific.SeqOfNode =
59 | if (key == this.key) value else next(key)
60 |
61 | /**
62 | * Forwards the call to next (because caller looks for prefixed attribute).
63 | *
64 | * @param namespace
65 | * @param scope
66 | * @param key
67 | * @return ..
68 | */
69 | override def apply(namespace: String, scope: NamespaceBinding, key: String): ScalaVersionSpecific.SeqOfNode =
70 | next(namespace, scope, key)
71 | }
72 | object UnprefixedAttribute {
73 | def unapply(x: UnprefixedAttribute): Some[(String, Seq[Node], MetaData)] = Some((x.key, x.value, x.next))
74 | }
75 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/Null.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 |
16 | import scala.collection.Iterator
17 | import scala.collection.Seq
18 |
19 | /**
20 | * Essentially, every method in here is a dummy, returning Zero[T].
21 | * It provides a backstop for the unusual collection defined by MetaData,
22 | * sort of a linked list of tails.
23 | *
24 | * @author Burak Emir
25 | */
26 | // Note: used by the Scala compiler.
27 | case object Null extends MetaData {
28 | override def iterator: Iterator[Nothing] = Iterator.empty
29 | override def size: Int = 0
30 | override def append(m: MetaData, scope: NamespaceBinding = TopScope): MetaData = m
31 | override def filter(f: MetaData => Boolean): ScalaVersionSpecificReturnTypes.NullFilter = this
32 |
33 | override def copy(next: MetaData): MetaData = next
34 | override def getNamespace(owner: Node): ScalaVersionSpecificReturnTypes.NullGetNamespace = null
35 |
36 | override def hasNext: Boolean = false
37 | override def next: ScalaVersionSpecificReturnTypes.NullNext = null
38 | override def key: ScalaVersionSpecificReturnTypes.NullKey = null
39 | override def value: ScalaVersionSpecificReturnTypes.NullValue = null
40 | override def isPrefixed: Boolean = false
41 |
42 | override def length: Int = 0
43 | override def length(i: Int): Int = i
44 |
45 | override def strict_==(other: Equality): Boolean = other match {
46 | case x: MetaData => x.length == 0
47 | case _ => false
48 | }
49 | override protected def basisForHashCode: Seq[Any] = Nil
50 |
51 | override def apply(namespace: String, scope: NamespaceBinding, key: String): ScalaVersionSpecificReturnTypes.NullApply3 = null
52 | override def apply(key: String): ScalaVersionSpecific.SeqOfNode =
53 | if (Utility.isNameStart(key.head)) null
54 | else throw new IllegalArgumentException(s"not a valid attribute name '$key', so can never match !")
55 |
56 | override protected def toString1(sb: StringBuilder): Unit = ()
57 | override protected def toString1: String = ""
58 |
59 | override def toString: String = ""
60 |
61 | override def buildString(sb: StringBuilder): StringBuilder = sb
62 |
63 | override def wellformed(scope: NamespaceBinding): Boolean = true
64 |
65 | override def remove(key: String): ScalaVersionSpecificReturnTypes.NullRemove = this
66 | override def remove(namespace: String, scope: NamespaceBinding, key: String): ScalaVersionSpecificReturnTypes.NullRemove = this
67 | }
68 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/dtd/impl/NondetWordAutom.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml.dtd.impl
15 |
16 | import scala.collection.{immutable, mutable}
17 | import scala.collection.Seq
18 |
19 | /**
20 | * A nondeterministic automaton. States are integers, where
21 | * 0 is always the only initial state. Transitions are represented
22 | * in the delta function. Default transitions are transitions that
23 | * are taken when no other transitions can be applied.
24 | * All states are reachable. Accepting states are those for which
25 | * the partial function `finals` is defined.
26 | */
27 | // TODO: still used in ContentModel -- @deprecated("This class will be removed", "2.10.0")
28 | private[dtd] abstract class NondetWordAutom[T <: AnyRef] {
29 | val nstates: Int
30 | val labels: Seq[T]
31 | val finals: Array[Int] // 0 means not final
32 | val delta: Array[mutable.Map[T, immutable.BitSet]]
33 | val default: Array[immutable.BitSet]
34 |
35 | /** @return true if the state is final */
36 | final def isFinal(state: Int): Boolean = finals(state) > 0
37 |
38 | /** @return tag of final state */
39 | final def finalTag(state: Int): Int = finals(state)
40 |
41 | /** @return true if the set of states contains at least one final state */
42 | final def containsFinal(Q: immutable.BitSet): Boolean = Q.exists(isFinal)
43 |
44 | /** @return true if there are no accepting states */
45 | final def isEmpty: Boolean = 0.until(nstates).forall(x => !isFinal(x))
46 |
47 | /** @return a immutable.BitSet with the next states for given state and label */
48 | def next(q: Int, a: T): immutable.BitSet = delta(q).getOrElse(a, default(q))
49 |
50 | /** @return a immutable.BitSet with the next states for given state and label */
51 | def next(Q: immutable.BitSet, a: T): immutable.BitSet = next(Q, next(_, a))
52 | def nextDefault(Q: immutable.BitSet): immutable.BitSet = next(Q, default)
53 |
54 | private def next(Q: immutable.BitSet, f: Int => immutable.BitSet): immutable.BitSet =
55 | Q.toSet.map(f).foldLeft(immutable.BitSet.empty)(_ ++ _)
56 |
57 | private def finalStates: immutable.Seq[Int] = 0.until(nstates).filter(isFinal)
58 | override def toString: String = {
59 | val finalString: String = Map(finalStates.map(j => j -> finals(j)): _*).toString
60 | val deltaString: String = 0.until(nstates)
61 | .map(i => s" $i->${delta(i)}\n _>${default(i)}\n").mkString
62 |
63 | s"[NondetWordAutom nstates=$nstates finals=$finalString delta=\n$deltaString"
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/shared/src/test/scala/scala/xml/ShouldCompile.scala:
--------------------------------------------------------------------------------
1 | package scala.xml
2 |
3 | // these tests depend on xml, so they ended up here,
4 | // though really they are compiler tests
5 |
6 | import scala.collection._
7 | import scala.collection.mutable.ArrayBuffer
8 |
9 | // t1761
10 | class Foo {
11 | val elements: Seq[Node] = Nil
12 | val innerTransform: PartialFunction[Elem, String] = {
13 | case Elem(_, l: String, _, _, _@ _*) if elements.exists(_.label == l) =>
14 | l
15 | }
16 | }
17 |
18 | // t2281
19 | class t2281A {
20 | def f(x: Boolean): Seq[Node] = if (x)
else
21 | }
22 |
23 | class t2281B {
24 | def splitSentences(text: String): ArrayBuffer[String] = {
25 | val outarr: ArrayBuffer[String] = new ArrayBuffer[String]
26 | var outstr: StringBuffer = new StringBuffer
27 | var prevspace: Boolean = false
28 | val ctext: String = text.replaceAll("\n+", "\n")
29 | ctext.foreach { c =>
30 | outstr.append(c)
31 | if (c == '.' || c == '!' || c == '?' || c == '\n' || c == ':' || c == ';' || (prevspace && c == '-')) {
32 | outarr += outstr.toString
33 | outstr = new StringBuffer
34 | }
35 | if (c == '\n') {
36 | outarr += "\n\n"
37 | }
38 | prevspace = c == ' '
39 | }
40 | if (outstr.length > 0) {
41 | outarr += outstr.toString
42 | }
43 | outarr
44 | }
45 |
46 | def spanForSentence(x: String, picktext: String): Seq[Node] =
47 | if (x == "\n\n") {
48 |
49 | } else {
50 | { x }
51 | }
52 |
53 | def selectableSentences(text: String, picktext: String): ArrayBuffer[Seq[Node]] = {
54 | val sentences: ArrayBuffer[String] = splitSentences(text)
55 | sentences.map(x => spanForSentence(x, picktext))
56 | }
57 | }
58 |
59 | // SI-5858
60 | object SI_5858 {
61 | new Elem(null, null, Null, TopScope, true, Nil: _*) // was ambiguous
62 | }
63 |
64 | class Floozy {
65 | def fooz(x: Node => String): Unit = ()
66 | def foo(m: Node): Unit = fooz {
67 | case Elem(_, _, _, _, n, _*) if n == m => "gaga"
68 | }
69 | }
70 |
71 | object guardedMatch { // SI-3705
72 | // guard caused verifyerror in oldpatmat -- TODO: move this to compiler test suite
73 | def updateNodes(ns: ScalaVersionSpecific.SeqOfNode): ScalaVersionSpecific.SeqOfNode =
74 | for (subnode <- ns) yield subnode match {
75 | case { _ } if true => abc
76 | case Elem(prefix, label, attribs, scope, children @ _*) =>
77 | Elem(prefix, label, attribs, scope, minimizeEmpty = true, updateNodes(children): _*)
78 | case other => other
79 | }
80 | updateNodes()
81 | }
82 |
83 | // SI-6897
84 | object shouldCompile {
85 | val html: Seq[Node] = (null: Any) match {
86 | case 1 =>
87 | case 2 =>
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/dtd/ExternalID.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 | package dtd
16 |
17 | /**
18 | * an ExternalIDs - either PublicID or SystemID
19 | *
20 | * @author Burak Emir
21 | */
22 | sealed abstract class ExternalID extends parsing.TokenTests {
23 | def quoted(s: String): String = {
24 | val c: Char = if (s.contains('"')) '\'' else '"'
25 | s"$c$s$c"
26 | }
27 |
28 | // public != null: PUBLIC " " publicLiteral " " [systemLiteral]
29 | // public == null: SYSTEM " " systemLiteral
30 | override def toString: String =
31 | if (publicId == null) s"SYSTEM ${quoted(systemId)}" else
32 | if (systemId == null) s"PUBLIC ${quoted(publicId)}" else
33 | s"PUBLIC ${quoted(publicId)} ${quoted(systemId)}"
34 |
35 | def buildString(sb: StringBuilder): StringBuilder =
36 | sb.append(this.toString)
37 |
38 | def systemId: String
39 | def publicId: String
40 | }
41 |
42 | /**
43 | * a system identifier
44 | *
45 | * @author Burak Emir
46 | * @param systemId the system identifier literal
47 | */
48 | case class SystemID(override val systemId: String) extends ExternalID {
49 | override val publicId: scala.Null = null
50 |
51 | if (!checkSysID(systemId))
52 | throw new IllegalArgumentException("can't use both \" and ' in systemId")
53 | }
54 |
55 | /**
56 | * a public identifier (see http://www.w3.org/QA/2002/04/valid-dtd-list.html).
57 | *
58 | * @author Burak Emir
59 | * @param publicId the public identifier literal
60 | * @param systemId (can be null for notation pubIDs) the system identifier literal
61 | */
62 | case class PublicID(override val publicId: String, override val systemId: String) extends ExternalID {
63 | if (!checkPubID(publicId))
64 | throw new IllegalArgumentException("publicId must consist of PubidChars")
65 |
66 | if (systemId != null && !checkSysID(systemId))
67 | throw new IllegalArgumentException("can't use both \" and ' in systemId")
68 |
69 | /** the constant "#PI" */
70 | def label: String = "#PI"
71 |
72 | /** always empty */
73 | def attribute: ScalaVersionSpecificReturnTypes.ExternalIDAttribute = Node.NoAttributes
74 |
75 | /** always empty */
76 | def child: Nil.type = Nil
77 | }
78 |
79 | /**
80 | * A marker used when a `DocType` contains no external id.
81 | *
82 | * @author Michael Bayne
83 | */
84 | object NoExternalID extends ExternalID {
85 | override val publicId: ScalaVersionSpecificReturnTypes.NoExternalIDId = null
86 | override val systemId: ScalaVersionSpecificReturnTypes.NoExternalIDId = null
87 |
88 | override def toString: String = ""
89 | }
90 |
--------------------------------------------------------------------------------
/shared/src/test/scala-2.x/scala/xml/TransformersTest.scala:
--------------------------------------------------------------------------------
1 | package scala.xml
2 |
3 | import scala.collection.Seq
4 | import scala.xml.transform._
5 |
6 | import org.junit.Test
7 | import org.junit.Assert.assertEquals
8 |
9 | class TransformersTest {
10 |
11 | def transformer: RuleTransformer = new RuleTransformer(new RewriteRule {
12 | override def transform(n: Node): NodeSeq = n match {
13 | case { _* } =>
14 | case n => n
15 | }
16 | })
17 |
18 | @Test
19 | def transform(): Unit = // SI-2124
20 | assertEquals(transformer.transform(
),
21 |
)
22 |
23 | @Test
24 | def transformNamespaced(): Unit = // SI-2125
25 | assertEquals(transformer.transform(
),
26 | Group(
))
27 |
28 | @Test
29 | def rewriteRule(): Unit = { // SI-2276
30 | val inputXml: Node =
31 |
32 |
33 | 1
34 |
35 |
36 | 1
37 |
38 |
39 |
40 | object t1 extends RewriteRule {
41 | override def transform(n: Node): Seq[Node] = n match {
42 | case { x } if x.toString.toInt < 4 => { x.toString.toInt + 1 }
43 | case other => other
44 | }
45 | }
46 |
47 | val ruleTransformer: RuleTransformer = new RuleTransformer(t1)
48 | JUnitAssertsForXML.assertEquals(ruleTransformer(inputXml).toString, // TODO: why do we need toString?
49 |
50 |
51 | 2
52 |
53 |
54 | 2
55 |
56 | )
57 | }
58 |
59 | @Test
60 | def preserveReferentialComplexityInLinearComplexity(): Unit = { // SI-4528
61 | var i: Int = 0
62 |
63 | val xmlNode: Elem = Hello Example
64 |
65 | new RuleTransformer(new RewriteRule {
66 | override def transform(n: Node): Seq[Node] = {
67 | n match {
68 | case t: Text if t.text.trim.nonEmpty =>
69 | i += 1
70 | Text(s"${t.text}!")
71 | case _ => n
72 | }
73 | }
74 | }).transform(xmlNode)
75 |
76 | assertEquals(1, i)
77 | }
78 |
79 | @Test
80 | def appliesRulesRecursivelyOnPreviousChanges(): Unit = { // #257
81 | def add(outer: Elem, inner: Node): RewriteRule = new RewriteRule {
82 | override def transform(n: Node): Seq[Node] = n match {
83 | case e: Elem if e.label == outer.label => e.copy(child = e.child ++ inner)
84 | case other => other
85 | }
86 | }
87 |
88 | def transformer: RuleTransformer = new RuleTransformer(add(, ), add(, ))
89 |
90 | assertEquals(, transformer())
91 | }
92 | }
93 |
94 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/package.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 |
15 | /**
16 | * This library provides support for the XML literal syntax in Scala programs.
17 | * {{{
18 | * val planets: scala.xml.Elem =
19 | *
20 | * Earth
21 | * 5.9742e24
22 | * 6378.14e3
23 | *
24 | *
25 | * Mars
26 | * 0.64191e24
27 | * 3397.0e3
28 | *
29 | *
30 | * }}}
31 | *
32 | * Additionally, you can mix Scala expressions in your XML elements by
33 | * using the curly brace notation:
34 | *
35 | * {{{
36 | * val sunMass = 1.99e30
37 | * val sunRadius = 6.96e8
38 | * val star =
39 | * Sun
40 | * { sunMass }
41 | * { sunRadius }
42 | * { 4 * Math.PI * Math.pow(sunRadius, 2) }
43 | * { 4/3 * Math.PI * Math.pow(sunRadius, 3) }
44 | *
45 | * }}}
46 | *
47 | * An XML element, for example `` and ``, is
48 | * represented in this library as a case class, [[scala.xml.Elem]].
49 | *
50 | * The sub-elements of XML values share a common base class,
51 | * [[scala.xml.Node]].
52 | *
53 | * However, the non-element declarations found in XML files share a
54 | * different common base class, [[scala.xml.dtd.Decl]]. Additionally,
55 | * document type declarations are represented by a different trait,
56 | * [[scala.xml.dtd.DTD]].
57 | *
58 | * For reading and writing XML data to and from files, see
59 | * [[scala.xml.XML]]. The default parser of XML data is the
60 | * [[http://xerces.apache.org/ Xerces]] parser and is provided in Java
61 | * by [[javax.xml.parsers.SAXParser]].
62 | *
63 | * For more control of the input, use the parser written in Scala that
64 | * is provided, [[scala.xml.parsing.ConstructingParser]].
65 | *
66 | * For working with XHTML input, use [[scala.xml.parsing.XhtmlParser]].
67 | *
68 | * For more control of the output, use the [[scala.xml.PrettyPrinter]].
69 | *
70 | * Utility methods for working with XML data are provided in
71 | * [[scala.xml.Utility]].
72 | *
73 | * XML values in Scala are immutable, but you can traverse and
74 | * transform XML data with a [[scala.xml.transform.RuleTransformer]].
75 | */
76 | package object xml {
77 | val XercesClassName: String = "org.apache.xerces.parsers.SAXParser"
78 |
79 | type SAXException = org.xml.sax.SAXException
80 | type SAXParseException = org.xml.sax.SAXParseException
81 | type EntityResolver = org.xml.sax.EntityResolver
82 | type InputSource = org.xml.sax.InputSource
83 | type XMLReader = org.xml.sax.XMLReader
84 | type SAXParser = javax.xml.parsers.SAXParser
85 | }
86 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/NamespaceBinding.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 |
16 | import scala.collection.Seq
17 |
18 | /**
19 | * The class `NamespaceBinding` represents namespace bindings
20 | * and scopes. The binding for the default namespace is treated as a null
21 | * prefix. the absent namespace is represented with the null uri. Neither
22 | * prefix nor uri may be empty, which is not checked.
23 | *
24 | * @author Burak Emir
25 | */
26 | // Note: used by the Scala compiler.
27 | @SerialVersionUID(0 - 2518644165573446725L)
28 | case class NamespaceBinding(prefix: String, uri: String, parent: NamespaceBinding) extends AnyRef with Equality {
29 | if (prefix == "")
30 | throw new IllegalArgumentException("zero length prefix not allowed")
31 |
32 | def getURI(prefix: String): String =
33 | if (this.prefix == prefix) uri else parent.getURI(prefix)
34 |
35 | /**
36 | * Returns some prefix that is mapped to the URI.
37 | *
38 | * @param _uri the input URI
39 | * @return the prefix that is mapped to the input URI, or null
40 | * if no prefix is mapped to the URI.
41 | */
42 | def getPrefix(uri: String): String =
43 | if (uri == this.uri) prefix else parent.getPrefix(uri)
44 |
45 | override def toString: String = Utility.sbToString(buildString(_, TopScope))
46 |
47 | private def shadowRedefined(stop: NamespaceBinding): NamespaceBinding = {
48 | def prefixList(x: NamespaceBinding): List[String] =
49 | if ((x == null) || x.eq(stop)) Nil
50 | else x.prefix :: prefixList(x.parent)
51 | def fromPrefixList(l: List[String]): NamespaceBinding = l match {
52 | case Nil => stop
53 | case x :: xs => NamespaceBinding(x, this.getURI(x), fromPrefixList(xs))
54 | }
55 | val ps0: List[String] = prefixList(this).reverse
56 | val ps: List[String] = ps0.distinct
57 | if (ps.size == ps0.size) this
58 | else fromPrefixList(ps)
59 | }
60 |
61 | override def canEqual(other: Any): Boolean = other match {
62 | case _: NamespaceBinding => true
63 | case _ => false
64 | }
65 |
66 | override def strict_==(other: Equality): Boolean = other match {
67 | case x: NamespaceBinding => (prefix == x.prefix) && (uri == x.uri) && (parent == x.parent)
68 | case _ => false
69 | }
70 |
71 | override def basisForHashCode: Seq[Any] = List(prefix, uri, parent)
72 |
73 | def buildString(stop: NamespaceBinding): String = Utility.sbToString(buildString(_, stop))
74 |
75 | def buildString(sb: StringBuilder, stop: NamespaceBinding): Unit =
76 | shadowRedefined(stop).doBuildString(sb, stop)
77 |
78 | private def doBuildString(sb: StringBuilder, stop: NamespaceBinding): Unit = {
79 | if (List(null, stop, TopScope).contains(this)) return
80 |
81 | val prefixStr: String = if (prefix != null) s":$prefix" else ""
82 | val uriStr: String = if (uri != null) uri else ""
83 | parent.doBuildString(sb.append(s""" xmlns$prefixStr="$uriStr""""), stop) // copy(ignore)
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/shared/src/test/scala/scala/xml/NodeSeqTest.scala:
--------------------------------------------------------------------------------
1 | package scala.xml
2 |
3 | import org.junit.Test
4 | import org.junit.Assert.assertEquals
5 | import org.junit.Assert.fail
6 |
7 | import scala.collection.immutable
8 |
9 | class NodeSeqTest {
10 |
11 | @Test
12 | def testAppend(): Unit = { // Bug #392.
13 | val a: NodeSeq = Hello
14 | val b: Elem = Hi
15 | a ++ Hi match {
16 | case res: NodeSeq => assertEquals(2, res.size.toLong)
17 | case _: Seq[Node] => fail("Should be NodeSeq was Seq[Node]") // Unreachable code?
18 | }
19 | val res: NodeSeq = a ++ b
20 | val exp: NodeSeq = NodeSeq.fromSeq(Seq(Hello, Hi))
21 | assertEquals(exp, res)
22 | }
23 |
24 | @Test
25 | def testAppendedAll(): Unit = { // Bug #392.
26 | val a: NodeSeq = Hello
27 | val b: Elem = Hi
28 | a :+ Hi match {
29 | case res: Seq[Node] => assertEquals(2, res.size.toLong)
30 | case _: NodeSeq => fail("Should be Seq[Node] was NodeSeq") // Unreachable code?
31 | }
32 | val res: NodeSeq = a :+ b // implicit seqToNodeSeq
33 | val exp: NodeSeq = NodeSeq.fromSeq(Seq(Hello, Hi))
34 | assertEquals(exp, res)
35 | }
36 |
37 | @Test
38 | def testPrepended(): Unit = {
39 | val a: NodeSeq = Hello
40 | val b: Elem = Hi
41 | a +: Hi match {
42 | case res: Seq[Node] => assertEquals(2, res.size.toLong)
43 | case _: NodeSeq => fail("Should be Seq[Node] was NodeSeq") // Unreachable code?
44 | }
45 | val res: Seq[NodeSeq] = a +: b
46 | val exp: NodeBuffer = {
47 | HelloHi
48 | }
49 | assertEquals(exp.toSeq, res)
50 | }
51 |
52 | @Test
53 | def testPrependedAll(): Unit = {
54 | val a: NodeSeq = Hello
55 | val b: Elem = Hi
56 | val c: Elem = Hey
57 | a ++: Hi ++: Hey match {
58 | case res: Seq[Node] => assertEquals(3, res.size.toLong)
59 | case _: NodeSeq => fail("Should be Seq[Node] was NodeSeq") // Unreachable code?
60 | }
61 | val res: NodeSeq = a ++: b ++: c // implicit seqToNodeSeq
62 | val exp: NodeSeq = NodeSeq.fromSeq(Seq(Hello, Hi, Hey))
63 | assertEquals(exp, res)
64 | }
65 |
66 | @Test
67 | def testMap(): Unit = {
68 | val a: NodeSeq = Hello
69 | val exp: NodeSeq = Seq(Hi) // implicit seqToNodeSeq
70 | assertEquals(exp, a.map(_ => Hi))
71 | assertEquals(exp, for { _ <- a } yield { Hi })
72 | }
73 |
74 | @Test
75 | def testFlatMap(): Unit = {
76 | val a: NodeSeq = Hello
77 | val exp: NodeSeq = Seq(Hi) // implicit seqToNodeSeq
78 | assertEquals(exp, a.flatMap(_ => Seq(Hi)))
79 | assertEquals(exp, for { b <- a; _ <- b } yield { Hi })
80 | assertEquals(exp, for { b <- a; c <- b; _ <- c } yield { Hi })
81 | }
82 |
83 | @Test
84 | def testStringProjection(): Unit = {
85 | val a: Elem =
86 |
87 | b
88 |
89 |
90 | e
91 | e
92 |
93 | c
94 |
95 |
96 | val res: Seq[String] = for {
97 | b <- a \ "b"
98 | c <- b.child
99 | e <- (c \ "e").headOption
100 | } yield {
101 | e.text.trim
102 | }
103 | assertEquals(Seq("e"), res)
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/shared/src/test/scala/scala/xml/PatternMatchingTest.scala:
--------------------------------------------------------------------------------
1 | package scala.xml
2 |
3 | import scala.collection.Seq
4 | import org.junit.Test
5 | import org.junit.Assert.assertTrue
6 | import org.junit.Assert.assertEquals
7 |
8 | class PatternMatchingTest {
9 | @Test
10 | def unprefixedAttribute(): Unit = {
11 | val li: List[String] = List("1", "2", "3", "4")
12 | assertTrue(matchSeq(li))
13 | assertTrue(matchList(li))
14 | }
15 |
16 | def matchSeq(args: Seq[String]): Boolean = args match {
17 | case Seq(a, b, c, d @ _*) => true
18 | }
19 |
20 | def matchList(args: List[String]): Boolean =
21 | Elem(null, "bla", Null, TopScope, minimizeEmpty = true, args.map { x => Text(x) }: _*) match {
22 | case Elem(_, _, _, _, Text("1"), _*) => true
23 | }
24 |
25 | @Test
26 | def simpleNode(): Unit =
27 | assertTrue( match {
28 | case => true
29 | })
30 |
31 | @Test
32 | def nameSpaced(): Unit =
33 | assertTrue( match {
34 | case => true
35 | })
36 |
37 | val cx: Elem =
38 | crazy text world
39 |
40 |
41 | @Test
42 | def nodeContents(): Unit = {
43 | assertTrue(Utility.trim(cx) match {
44 | case n @ crazy text world if (n \ "@foo") xml_== "bar" => true
45 | })
46 | assertTrue(Utility.trim(cx) match {
47 | case n @ crazy text world if (n \ "@foo") xml_== "bar" => true
48 | })
49 | assertTrue( match {
50 | case QNode("gaga", "foo", md, child @ _*) => true
51 | })
52 |
53 | assertTrue( match {
54 | case Node("foo", md, child @ _*) => true
55 | })
56 | }
57 |
58 | object SafeNodeSeq {
59 | def unapplySeq(any: Any): Option[Seq[Node]] = any match {
60 | case s: Seq[?] => Some(s.flatMap {
61 | case n: Node => n
62 | case _ => NodeSeq.Empty
63 | })
64 | case _ => None
65 | }
66 | }
67 |
68 | @Test
69 | def nodeSeq(): Unit = { // t0646
70 | val books: Elem =
71 |
72 | Blabla
73 | Blubabla
74 | Baaaaaaalabla
75 |
76 |
77 | assertTrue(NodeSeq.fromSeq(books.child) match {
78 | case t @ Seq(Blabla) => false
79 | case _ => true
80 | })
81 |
82 | // SI-1059
83 | val m: PartialFunction[Any, Any] = { case SafeNodeSeq(s @ _*) => s }
84 |
85 | assertEquals(m( ++ ), List(, ))
86 | assertTrue(m.isDefinedAt( ++ ))
87 | }
88 |
89 | @Test
90 | def SI_4124(): Unit = {
91 | val body: Node = hi
92 |
93 | assertTrue((body: AnyRef, "foo") match {
94 | case (node: Node, "bar") => false
95 | case (ser: Serializable, "foo") => true
96 | case (_, _) => false
97 | })
98 |
99 | assertTrue((body, "foo") match {
100 | case (node: Node, "bar") => false
101 | case (ser: Serializable, "foo") => true
102 | case (_, _) => false
103 | })
104 |
105 | assertTrue((body: AnyRef, "foo") match {
106 | case (node: Node, "foo") => true
107 | case (ser: Serializable, "foo") => false
108 | case (_, _) => false
109 | })
110 |
111 | assertTrue((body: AnyRef, "foo") match {
112 | case (node: Node, "foo") => true
113 | case (ser: Serializable, "foo") => false
114 | case (_, _) => false
115 | })
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/shared/src/main/scala-2.13+/scala/xml/ScalaVersionSpecific.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala.xml
14 |
15 | import scala.collection.immutable.StrictOptimizedSeqOps
16 | import scala.collection.{View, SeqOps, IterableOnce, immutable, mutable}
17 | import scala.collection.BuildFrom
18 | import scala.collection.mutable.Builder
19 |
20 | private[xml] object ScalaVersionSpecific {
21 | import NodeSeq.Coll
22 | type CBF[-From, -A, +C] = BuildFrom[From, A, C]
23 | object NodeSeqCBF extends BuildFrom[Coll, Node, NodeSeq] {
24 | def newBuilder(from: Coll): Builder[Node, NodeSeq] = NodeSeq.newBuilder
25 | def fromSpecific(from: Coll)(it: IterableOnce[Node]): NodeSeq = (NodeSeq.newBuilder ++= from).result()
26 | }
27 | type SeqOfNode = scala.collection.immutable.Seq[Node]
28 | type SeqOfText = scala.collection.immutable.Seq[Text]
29 | }
30 |
31 | private[xml] trait ScalaVersionSpecificNodeSeq
32 | extends SeqOps[Node, immutable.Seq, NodeSeq]
33 | with StrictOptimizedSeqOps[Node, immutable.Seq, NodeSeq] { self: NodeSeq =>
34 | override def fromSpecific(coll: IterableOnce[Node]): NodeSeq = (NodeSeq.newBuilder ++= coll).result()
35 | override def newSpecificBuilder: mutable.Builder[Node, NodeSeq] = NodeSeq.newBuilder
36 | override def empty: NodeSeq = NodeSeq.Empty
37 | def concat(suffix: IterableOnce[Node]): NodeSeq =
38 | fromSpecific(iterator ++ suffix.iterator)
39 | @inline final def ++ (suffix: Seq[Node]): NodeSeq = concat(suffix)
40 | def appended(base: Node): NodeSeq =
41 | fromSpecific(new View.Appended(this, base))
42 | def appendedAll(suffix: IterableOnce[Node]): NodeSeq =
43 | concat(suffix)
44 | def prepended(base: Node): NodeSeq =
45 | fromSpecific(new View.Prepended(base, this))
46 | def prependedAll(prefix: IterableOnce[Node]): NodeSeq =
47 | fromSpecific(prefix.iterator ++ iterator)
48 | def map(f: Node => Node): NodeSeq =
49 | fromSpecific(new View.Map(this, f))
50 | def flatMap(f: Node => IterableOnce[Node]): NodeSeq =
51 | fromSpecific(new View.FlatMap(this, f))
52 |
53 | def theSeq: scala.collection.Seq[Node]
54 | }
55 |
56 | private[xml] trait ScalaVersionSpecificNodeBuffer { self: NodeBuffer =>
57 | override def className: String = "NodeBuffer"
58 | }
59 |
60 | private[xml] trait ScalaVersionSpecificNode { self: Node =>
61 | // These methods are overridden in Node with return type `immutable.Seq`. The declarations here result
62 | // in a bridge method in `Node` with result type `collection.Seq` which is needed for binary compatibility.
63 | def child: scala.collection.Seq[Node]
64 | def nonEmptyChildren: scala.collection.Seq[Node]
65 | }
66 |
67 | private[xml] trait ScalaVersionSpecificMetaData { self: MetaData =>
68 | def apply(key: String): scala.collection.Seq[Node]
69 | def apply(namespace_uri: String, owner: Node, key: String): scala.collection.Seq[Node]
70 | def apply(namespace_uri: String, scp: NamespaceBinding, k: String): scala.collection.Seq[Node]
71 |
72 | def value: scala.collection.Seq[Node]
73 | }
74 |
75 | private[xml] trait ScalaVersionSpecificTextBuffer { self: TextBuffer =>
76 | def toText: scala.collection.Seq[Text]
77 | }
78 |
79 | private[xml] trait ScalaVersionSpecificUtility { self: Utility.type =>
80 | def trimProper(x: Node): scala.collection.Seq[Node]
81 | def parseAttributeValue(value: String): scala.collection.Seq[Node]
82 | }
83 |
--------------------------------------------------------------------------------
/jvm/src/test/scala/scala/xml/parsing/ConstructingParserTest.scala:
--------------------------------------------------------------------------------
1 | package scala.xml
2 | package parsing
3 |
4 | import scala.io.Source
5 | import org.junit.Test
6 | import scala.xml.JUnitAssertsForXML.{ assertEquals => assertXml }
7 | import org.junit.Assert.assertEquals
8 |
9 | class ConstructingParserTest {
10 |
11 | @Test
12 | def t9060(): Unit = {
13 | val a: String = """"""
14 | val source: Source = new Source {
15 | override val iter: Iterator[Char] = a.iterator
16 | override def reportError(pos: Int, msg: String, out: java.io.PrintStream = Console.err): Unit = ()
17 | }
18 | val doc: NodeSeq = ConstructingParser.fromSource(source, preserveWS = false).content(TopScope)
19 | assertXml(a, doc)
20 | }
21 |
22 | /* Example of using SYSTEM in DOCTYPE */
23 | @Test
24 | def docbookTest(): Unit = {
25 | val xml: String =
26 | """|
27 | |
28 | | Book
29 | |
30 | | Chapter
31 | | Text
32 | |
33 | |""".stripMargin
34 |
35 | val expected: Elem =
36 | Book
37 |
38 | Chapter
39 | Text
40 |
41 |
42 |
43 | val source: Source = new Source {
44 | override val iter: Iterator[Char] = xml.iterator
45 | override def reportError(pos: Int, msg: String, out: java.io.PrintStream = Console.err): Unit = ()
46 | }
47 |
48 | val doc: Document = ConstructingParser.fromSource(source, preserveWS = true).document()
49 |
50 | assertEquals(expected, doc.theSeq)
51 | }
52 |
53 | /* Unsupported use of lowercase DOCTYPE and SYSTEM */
54 | @Test(expected = classOf[scala.xml.parsing.FatalError])
55 | def docbookFail(): Unit = {
56 | val xml: String =
57 | """|
58 | |
59 | |Book
60 | |
61 | |Chapter
62 | |Text
63 | |
64 | |""".stripMargin
65 |
66 | val source: Source = new Source {
67 | override val iter: Iterator[Char] = xml.iterator
68 | override def reportError(pos: Int, msg: String, out: java.io.PrintStream = Console.err): Unit = ()
69 | }
70 |
71 | ConstructingParser.fromSource(source, preserveWS = true).content(TopScope)
72 | }
73 |
74 | @Test
75 | def SI6341issue65(): Unit = {
76 | val str: String = """"""
77 | val cpa: ConstructingParser = ConstructingParser.fromSource(Source.fromString(str), preserveWS = true)
78 | val cpadoc: Document = cpa.document()
79 | val ppr: PrettyPrinter = new PrettyPrinter(80,5)
80 | val out: String = ppr.format(cpadoc.docElem)
81 | assertEquals(str, out)
82 | }
83 |
84 | // https://github.com/scala/scala-xml/issues/541
85 | @Test
86 | def issue541(): Unit = {
87 | val xml: String =
88 | """|""".stripMargin
91 | val parser: ConstructingParser = ConstructingParser.fromSource(Source.fromString(xml), preserveWS = true)
92 | parser.document().docElem // shouldn't crash
93 | }
94 |
95 | @Test(expected = classOf[scala.xml.parsing.FatalError])
96 | def issue656(): Unit = {
97 | // mismatched quotes should not cause an infinite loop
98 | XhtmlParser(Source.fromString(""" true
31 | case _ => false
32 | }
33 | /**
34 | * {{{
35 | * (#x20 | #x9 | #xD | #xA)+
36 | * }}}
37 | */
38 | final def isSpace(cs: Seq[Char]): Boolean = cs.nonEmpty && cs.forall(isSpace)
39 |
40 | /** These are 99% sure to be redundant but refactoring on the safe side. */
41 | def isAlpha(c: Char): Boolean = (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
42 | def isAlphaDigit(c: Char): Boolean = isAlpha(c) || (c >= '0' && c <= '9')
43 |
44 | /**
45 | * {{{
46 | * NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' | #xB7
47 | * | CombiningChar | Extender
48 | * }}}
49 | * See [4] and [4a] of Appendix B of XML 1.0 specification.
50 | */
51 | def isNameChar(ch: Char): Boolean = {
52 | import java.lang.Character._
53 | // The constants represent groups Mc, Me, Mn, Lm, and Nd.
54 |
55 | isNameStart(ch) || (getType(ch).toByte match {
56 | case COMBINING_SPACING_MARK |
57 | ENCLOSING_MARK | NON_SPACING_MARK |
58 | MODIFIER_LETTER | DECIMAL_DIGIT_NUMBER => true
59 | case _ => ".-:·".contains(ch)
60 | })
61 | }
62 |
63 | /**
64 | * {{{
65 | * NameStart ::= ( Letter | '_' | ':' )
66 | * }}}
67 | * where Letter means in one of the Unicode general
68 | * categories `{ Ll, Lu, Lo, Lt, Nl }`.
69 | *
70 | * We do not allow a name to start with `:`.
71 | * See [4] and Appendix B of XML 1.0 specification
72 | */
73 | def isNameStart(ch: Char): Boolean = {
74 | import java.lang.Character._
75 |
76 | getType(ch).toByte match {
77 | case LOWERCASE_LETTER |
78 | UPPERCASE_LETTER | OTHER_LETTER |
79 | TITLECASE_LETTER | LETTER_NUMBER => true
80 | case _ => ":_".contains(ch)
81 | }
82 | }
83 |
84 | /**
85 | * {{{
86 | * Name ::= ( Letter | '_' ) (NameChar)*
87 | * }}}
88 | * See [5] of XML 1.0 specification.
89 | */
90 | def isName(s: String): Boolean =
91 | s.nonEmpty && isNameStart(s.head) && s.tail.forall(isNameChar)
92 |
93 | def isPubIDChar(ch: Char): Boolean =
94 | isAlphaDigit(ch) || (isSpace(ch) && ch != '\u0009') ||
95 | """-\()+,./:=?;!*#@$_%""".contains(ch)
96 |
97 | /**
98 | * Returns `true` if the encoding name is a valid IANA encoding.
99 | * This method does not verify that there is a decoder available
100 | * for this encoding, only that the characters are valid for an
101 | * IANA encoding name.
102 | *
103 | * @param ianaEncoding The IANA encoding name.
104 | */
105 | def isValidIANAEncoding(ianaEncoding: Seq[Char]): Boolean = {
106 | def charOK(c: Char): Boolean = isAlphaDigit(c) || "._-".contains(c)
107 |
108 | ianaEncoding.nonEmpty && isAlpha(ianaEncoding.head) &&
109 | ianaEncoding.tail.forall(charOK)
110 | }
111 |
112 | def checkSysID(s: String): Boolean = List('"', '\'').exists(c => !s.contains(c))
113 | def checkPubID(s: String): Boolean = s.forall(isPubIDChar)
114 | }
115 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Scala XML Changes
2 |
3 | ## 2.0.1 (2021-07-21)
4 |
5 | Binary compatible with Scala XML 2.0.0.
6 |
7 | Published for Scala 2.12 and 2.13, Scala 3, Scala.js 1.6, and Scala
8 | Native 0.4.
9 |
10 | ### Added
11 |
12 | - No new functionality.
13 |
14 | ### Fixed
15 |
16 | - Fixed runtime error for `MarkupParser` on Scala 3 by changing the
17 | access modifier of internal class, `WithLookAhead` (#542)
18 |
19 | ## 2.0.0 (2021-05-13)
20 |
21 | Not binary compatible with Scala XML 1.3.0.
22 |
23 | Published for Scala 2.12, 2.13, and 3.0, Scala.js 1.5,
24 | and Scala Native 0.4.
25 |
26 | Artifacts are no longer published for Scala 2.11 and Scala.js 0.6.
27 |
28 | A number of deprecated elements have been removed from the library;
29 | see the "[Removed](#Removed)" section below. The library's JAR byte
30 | size is about 15% smaller.
31 |
32 | ### Added
33 |
34 | - Add `scala.xml.transform.NestingTransformer`, to apply a single rule
35 | recursively, to give the original behavior of `RuleTransformer`, see
36 | below.
37 | - The `apiURL` is now published in ivy metadata so that hyperlinks
38 | exist in downstream projects that reference Scala XML in their
39 | Scaladocs.
40 | - Declare version policy of with early-semver in Mima with
41 | sbt-version-policy plugin
42 |
43 | ### Changed
44 |
45 | - Changes to the default parser settings for the JDK SAXParser, see
46 | [Safe parser defaults](https://github.com/scala/scala-xml/wiki/Safer-parser-defaults)
47 | page on the wiki.
48 | - The parser used by the load methods from `scala.xml.XML` and from
49 | `scala.xml.factory.XMLLoader` is now a `ThreadLocal` instance of
50 | SAXParser to reuse the parser instance and avoid repeatedly
51 | allocating one on every file load.
52 | - Improve `scala.xml.transform.RuleTransformer` to apply all rules recursively.
53 | - Reject invalid comment text that ends in a dash (-) in `scala.xml.Comment`.
54 | - Changed use of `scala.collection.mutable.Stack` in `FactoryAdapter` to a
55 | `scala.collection.immutable.List`. These members were affected.
56 | - `attribStack`
57 | - `hStack`
58 | - `tagStack`
59 | - `scopeStack`
60 | - The abstract class `FactoryAdapter`, see above, is used elsewhere
61 | within the library, as well, so the previous changes are also
62 | inherited by:
63 | - `scala.xml.parsing.NoBindingFactoryAdapter` implemented class
64 | - `scala.xml.factory.XMLLoader.adapter` static member
65 |
66 | ### Fixed
67 |
68 | - Attribute order is preserved for XML elements, not reversed.
69 | - Don't escape quotes in `scala.xml.PCData` and `CDATA` as an XML `"`
70 |
71 | ### Removed
72 |
73 | Most of these deletions are of vestigial code that is either unused,
74 | of poor quality or both. Very few users of Scala XML will even notice
75 | the removed parts. Most users will not be affected.
76 |
77 | The deletions represent about 1500 lines of code (sloc). By
78 | comparison Scala XML is 10,000 sloc, so this is about 15% reduction in
79 | sloc. The code that supports XML literals is maintained upstream in
80 | the Scala compiler, not in the Scala XML library.
81 |
82 | - Remove deprecated `scala.xml.pull.XMLEventReader`
83 | - Remove deprecated versions of `scala.xml.Elem` constructors
84 | - Remove deprecated `scala.xml.Elem.xmlToProcess` and
85 | `scala.xml.Elem.processXml`
86 | - Remove deprecated definitions under `scala.xml.persistent`
87 | - `CachedFileStorage`
88 | - `Index`
89 | - `SetStorage`
90 | - Remove `scala.xml.dtd.impl.PointedHedgeExp`
91 | - Remove `scala.xml.dtd.Scanner`
92 | - Remove `scala.xml.dtd.ContentModelParser`
93 | - Remove `scala.xml.dtd.ElementValidator`
94 | - Remove `scala.xml.factory.Binder`
95 | - Remove `scala.xml.parsing.ValidatingMarkupHandler`
96 | - Remove `scala.xml.Properties`
97 | - Remove `scala.xml.factory.LoggedNodeFactory`
98 | - Remove `scala.xml.parsing.MarkupHandler.log`
99 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | scala-xml
2 | [](http://mvnrepository.com/artifact/org.scala-lang.modules/scala-xml_2.12)
3 | [](http://mvnrepository.com/artifact/org.scala-lang.modules/scala-xml_2.13)
4 | [](http://mvnrepository.com/artifact/org.scala-lang.modules/scala-xml_3)
5 | =========
6 |
7 | The standard Scala XML library. Please file XML issues here, not at https://github.com/scala/bug/issues or http://github.com/scala/scala3/issues.
8 |
9 | The decoupling of scala-xml from the Scala compiler and standard library is possible because the compiler desugars XML literals in Scala source code into a set of method calls.
10 | Alternative implementations of these calls are welcome!
11 | Compiler code that shows the calls needed:
12 | [Scala 2.11](https://github.com/scala/scala/blob/2.11.x/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala),
13 | [Scala 2.12](https://github.com/scala/scala/blob/2.12.x/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala),
14 | [Scala 2.13](https://github.com/scala/scala/blob/2.13.x/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala),
15 | [Scala 3](https://github.com/scala/scala3/blob/main/compiler/src/dotty/tools/dotc/parsing/xml/SymbolicXMLBuilder.scala).
16 |
17 | API documentation is available [here](https://javadoc.io/doc/org.scala-lang.modules/scala-xml_2.13/).
18 |
19 | How-to documentation is available in the [wiki](https://github.com/scala/scala-xml/wiki)
20 |
21 | ## Maintenance status
22 |
23 | This library is community-maintained. Maintainers with merge rights include [@aaron_s_hawley](https://github.com/ashawley) and [@dubinsky](https://github.com/dubinsky).
24 |
25 | Contributors are welcome. Please consult the [contributor guide](https://github.com/scala/scala-xml/wiki/Contributor-guide) on the wiki.
26 |
27 | ## Issues
28 |
29 | Some old issues from the Scala issue tracker have been migrated
30 | here, but not all of them. Community assistance identifying and
31 | migrating still-relevant issues is welcome. See [this
32 | page](https://github.com/scala/scala-xml/issues/62) for details.
33 |
34 | ## Related projects
35 |
36 | - [Advxml](https://github.com/geirolz/advxml) - Functional library combining scala-xml with cats-core
37 | - [Binding.scala](https://github.com/ThoughtWorksInc/Binding.scala) - Reactive programming library
38 | - [ezXML](https://github.com/JulienSt/ezXML) - Extensions for traverse, encoding, decoding and mapping XML
39 | - [http4s-scala-xml](https://http4s.github.io/http4s-scala-xml/) - XML literal support in http4s
40 | - [Json4s XML](https://github.com/json4s/json4s) - Conversion to and from JSON
41 | - [monadic-html](https://github.com/OlivierBlanvillain/monadic-html) - DOM-like event-based programming with XHTML
42 | - [phobos](https://github.com/TinkoffCreditSystems/phobos) - Data-binding library based on stream parsing using Aalto XML
43 | - [scalacheck-xml](https://github.com/typelevel/scalacheck-xml) - Provides Scalacheck instances for scala-xml
44 | - [scalaxb](http://scalaxb.org/) - XML data binding, serialization, SOAP and WSDL support
45 | - [ScalaTags](https://github.com/lihaoyi/scalatags) - Alternative syntax for XML literals
46 | - [scala-xml-dotty](https://github.com/felixmulder/scala-xml-dotty) - Macro library for XML literals in Dotty
47 | - [XML SPaC](https://github.com/dylemma/xml-spac) - Streaming event-based parser combinators
48 | - [xs4s](https://github.com/ScalaWilliam/xs4s) - XML streaming for Scala
49 | - [xtract](https://github.com/lucidsoftware/xtract) - A library for deserializing XML
50 |
51 | You might also [search "XML" on Scaladex](https://index.scala-lang.org/search?q=xml).
52 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/Xhtml.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 |
16 | import parsing.XhtmlEntities
17 | import Utility.{ sbToString, isAtomAndNotText }
18 | import scala.collection.Seq
19 |
20 | /* (c) David Pollak 2007 WorldWide Conferencing, LLC */
21 |
22 | object Xhtml {
23 | /**
24 | * Convenience function: same as toXhtml(node, false, false)
25 | *
26 | * @param node the node
27 | */
28 | def toXhtml(node: Node): String = sbToString(sb => toXhtml(x = node, sb = sb))
29 |
30 | /**
31 | * Convenience function: amounts to calling toXhtml(node) on each
32 | * node in the sequence.
33 | *
34 | * @param nodeSeq the node sequence
35 | */
36 | def toXhtml(nodeSeq: NodeSeq): String = sbToString(sb => sequenceToXML(nodeSeq: Seq[Node], sb = sb))
37 |
38 | /**
39 | * Elements which we believe are safe to minimize if minimizeTags is true.
40 | * See http://www.w3.org/TR/xhtml1/guidelines.html#C_3
41 | */
42 | private val minimizableElements: List[String] =
43 | List("base", "meta", "link", "hr", "br", "param", "img", "area", "input", "col")
44 |
45 | def toXhtml(
46 | x: Node,
47 | pscope: NamespaceBinding = TopScope,
48 | sb: StringBuilder = new StringBuilder,
49 | stripComments: Boolean = false,
50 | decodeEntities: Boolean = false,
51 | preserveWhitespace: Boolean = false,
52 | minimizeTags: Boolean = true
53 | ): Unit = {
54 | def decode(er: EntityRef): StringBuilder = XhtmlEntities.entMap.get(er.entityName) match {
55 | case Some(chr) if chr.toInt >= 128 => sb.append(chr)
56 | case _ => er.buildString(sb)
57 | }
58 | def shortForm: Boolean =
59 | minimizeTags &&
60 | (x.child == null || x.child.isEmpty) &&
61 | minimizableElements.contains(x.label)
62 |
63 | x match {
64 | case c: Comment => if (!stripComments) c.buildString(sb)
65 | case er: EntityRef if decodeEntities => decode(er)
66 | case x: SpecialNode => x.buildString(sb)
67 | case g: Group =>
68 | g.nodes.foreach { toXhtml(_, x.scope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags) }
69 |
70 | case _ =>
71 | sb.append('<')
72 | x.nameToString(sb)
73 | if (x.attributes != null) x.attributes.buildString(sb)
74 | x.scope.buildString(sb, pscope)
75 |
76 | if (shortForm) sb.append(" />")
77 | else {
78 | sb.append('>')
79 | sequenceToXML(x.child, x.scope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags)
80 | sb.append("")
81 | x.nameToString(sb)
82 | sb.append('>')
83 | }
84 | }
85 | }
86 |
87 | /**
88 | * Amounts to calling toXhtml(node, ...) with the given parameters on each node.
89 | */
90 | def sequenceToXML(
91 | children: Seq[Node],
92 | pscope: NamespaceBinding = TopScope,
93 | sb: StringBuilder = new StringBuilder,
94 | stripComments: Boolean = false,
95 | decodeEntities: Boolean = false,
96 | preserveWhitespace: Boolean = false,
97 | minimizeTags: Boolean = true
98 | ): Unit = if (children.nonEmpty) {
99 | val doSpaces: Boolean = children.forall(isAtomAndNotText) // interleave spaces
100 | for (c <- children.take(children.length - 1)) {
101 | toXhtml(c, pscope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags)
102 | if (doSpaces) sb.append(' ')
103 | }
104 | toXhtml(children.last, pscope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags)
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/dtd/impl/BaseBerrySethi.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml.dtd.impl
15 |
16 | import scala.collection.{ mutable, immutable }
17 | import scala.collection.Seq
18 |
19 | // todo: replace global variable pos with acc
20 |
21 | /**
22 | * This class turns a regular expression over `A` into a
23 | * [[scala.xml.dtd.impl.NondetWordAutom]] over `A` using the celebrated
24 | * position automata construction (also called ''Berry-Sethi'' or ''Glushkov'').
25 | */
26 | @deprecated("This class will be removed", "2.10.0")
27 | private[dtd] abstract class BaseBerrySethi {
28 | val lang: Base
29 | import lang.{ Alt, Eps, Meta, RegExp, Sequ, Star }
30 |
31 | protected var pos: Int = 0
32 |
33 | // results which hold all info for the NondetWordAutomaton
34 | protected var follow: mutable.HashMap[Int, Set[Int]] = _
35 |
36 | protected var finalTag: Int = _
37 |
38 | protected var finals: immutable.Map[Int, Int] = _ // final states
39 |
40 | // constants --------------------------
41 |
42 | final val emptySet: Set[Int] = Set()
43 |
44 | private def doComp(r: RegExp, compFunction: RegExp => Set[Int]): Set[Int] = r match {
45 | case x: Alt => x.rs.map(compFirst).foldLeft(emptySet)(_ ++ _)
46 | case Eps => emptySet
47 | case x: Meta => compFunction(x.r)
48 | case x: Sequ =>
49 | val (l1: Seq[lang._regexpT], l2: Seq[lang._regexpT]) = x.rs.span(_.isNullable)
50 | (l1 ++ l2.take(1)).map(compFunction).foldLeft(emptySet)(_ ++ _)
51 | case Star(t) => compFunction(t)
52 | case _ => throw new IllegalArgumentException(s"unexpected pattern ${r.getClass}")
53 | }
54 |
55 | /** Computes `first(r)` for the word regexp `r`. */
56 | protected def compFirst(r: RegExp): Set[Int] = doComp(r, compFirst)
57 |
58 | /** Computes `last(r)` for the regexp `r`. */
59 | protected def compLast(r: RegExp): Set[Int] = doComp(r, compLast)
60 |
61 | /**
62 | * Starts from the right-to-left
63 | * precondition: pos is final
64 | * pats are successor patterns of a Sequence node
65 | */
66 | protected def compFollow(rs: Seq[RegExp]): Set[Int] = {
67 | follow(0) =
68 | if (rs.isEmpty) emptySet
69 | else rs.foldRight(Set(pos))((p, fol) => {
70 | val first: Set[Int] = compFollow1(fol, p)
71 |
72 | if (p.isNullable) fol ++ first
73 | else first
74 | })
75 |
76 | follow(0)
77 | }
78 |
79 | /**
80 | * Returns the first set of an expression, setting the follow set along the way.
81 | */
82 | protected def compFollow1(fol1: Set[Int], r: RegExp): Set[Int] = r match {
83 | case x: Alt => Set((x.rs reverseMap (compFollow1(fol1, _))).flatten: _*)
84 | case x: Meta => compFollow1(fol1, x.r)
85 | case x: Star => compFollow1(fol1 ++ compFirst(x.r), x.r)
86 | case x: Sequ =>
87 | x.rs.foldRight(fol1) { (p, fol) =>
88 | val first: Set[Int] = compFollow1(fol, p)
89 |
90 | if (p.isNullable) fol ++ first
91 | else first
92 | }
93 | case _ => throw new IllegalArgumentException(s"unexpected pattern: ${r.getClass}")
94 | }
95 |
96 | /**
97 | * Returns the "Sethi-length" of a pattern, creating the set of position along the way.
98 | */
99 | protected def traverse(r: RegExp): Unit = r match {
100 | // (is tree automaton stuff, more than Berry-Sethi)
101 | case x: Alt => x.rs.foreach(traverse)
102 | case x: Sequ => x.rs.foreach(traverse)
103 | case x: Meta => traverse(x.r)
104 | case Star(t) => traverse(t)
105 | case _ => throw new IllegalArgumentException(s"unexp pattern ${r.getClass}")
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/include/sax/EncodingHeuristics.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 | package include.sax
16 |
17 | import java.io.InputStream
18 | import scala.util.matching.Regex
19 |
20 | /**
21 | * `EncodingHeuristics` reads from a stream
22 | * (which should be buffered) and attempts to guess
23 | * what the encoding of the text in the stream is.
24 | * If it fails to determine the type of the encoding,
25 | * it returns the default UTF-8.
26 | *
27 | * @author Burak Emir
28 | * @author Paul Phillips
29 | */
30 | object EncodingHeuristics {
31 | object EncodingNames {
32 | // UCS-4 isn't yet implemented in java releases anyway...
33 | val bigUCS4: String = "UCS-4"
34 | val littleUCS4: String = "UCS-4"
35 | val unusualUCS4: String = "UCS-4"
36 | val bigUTF16: String = "UTF-16BE"
37 | val littleUTF16: String = "UTF-16LE"
38 | val utf8: String = "UTF-8"
39 | val default: String = utf8
40 | }
41 | import EncodingNames._
42 |
43 | /**
44 | * This utility method attempts to determine the XML character encoding
45 | * by examining the input stream, as specified at
46 | * [[http://www.w3.org/TR/xml/#sec-guessing w3]].
47 | *
48 | * @param in `InputStream` to read from.
49 | * @throws java.io.IOException if the stream cannot be reset
50 | * @return the name of the encoding.
51 | */
52 | def readEncodingFromStream(in: InputStream): String = {
53 | var ret: String = null
54 | val bytesToRead: Int = 1024 // enough to read most XML encoding declarations
55 | def resetAndRet: String = { in.reset(); ret }
56 |
57 | // This may fail if there are a lot of space characters before the end
58 | // of the encoding declaration
59 | in mark bytesToRead
60 | val bytes: (Int, Int, Int, Int) = (in.read, in.read, in.read, in.read)
61 |
62 | // first look for byte order mark
63 | ret = bytes match {
64 | case (0x00, 0x00, 0xFE, 0xFF) => bigUCS4
65 | case (0xFF, 0xFE, 0x00, 0x00) => littleUCS4
66 | case (0x00, 0x00, 0xFF, 0xFE) => unusualUCS4
67 | case (0xFE, 0xFF, 0x00, 0x00) => unusualUCS4
68 | case (0xFE, 0xFF, _, _) => bigUTF16
69 | case (0xFF, 0xFE, _, _) => littleUTF16
70 | case (0xEF, 0xBB, 0xBF, _) => utf8
71 | case _ => null
72 | }
73 | if (ret != null)
74 | return resetAndRet
75 |
76 | def readASCIIEncoding: String = {
77 | val data: Array[Byte] = new Array[Byte](bytesToRead - 4)
78 | val length: Int = in.read(data, 0, bytesToRead - 4)
79 |
80 | // Use Latin-1 (ISO-8859-1) because all byte sequences are legal.
81 | val declaration: String = new String(data, 0, length, "ISO-8859-1")
82 | val regexp: Regex = """(?m).*?encoding\s*=\s*["'](.+?)['"]""".r
83 | regexp.findFirstMatchIn(declaration) match {
84 | case None => default
85 | case Some(md) => md.subgroups(0)
86 | }
87 | }
88 |
89 | // no byte order mark present; first character must be '<' or whitespace
90 | ret = bytes match {
91 | case (0x00, 0x00, 0x00, '<') => bigUCS4
92 | case ('<', 0x00, 0x00, 0x00) => littleUCS4
93 | case (0x00, 0x00, '<', 0x00) => unusualUCS4
94 | case (0x00, '<', 0x00, 0x00) => unusualUCS4
95 | case (0x00, '<', 0x00, '?') => bigUTF16 // XXX must read encoding
96 | case ('<', 0x00, '?', 0x00) => littleUTF16 // XXX must read encoding
97 | case ('<', '?', 'x', 'm') => readASCIIEncoding
98 | case (0x4C, 0x6F, 0xA7, 0x94) => utf8 // XXX EBCDIC
99 | case _ => utf8 // no XML or text declaration present
100 | }
101 | resetAndRet
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/Document.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 |
16 | import scala.collection.Seq
17 |
18 | /**
19 | * A document information item (according to InfoSet spec). The comments
20 | * are copied from the Infoset spec, only augmented with some information
21 | * on the Scala types for definitions that might have no value.
22 | * Also plays the role of an `XMLEvent` for pull parsing.
23 | *
24 | * @author Burak Emir
25 | */
26 | @SerialVersionUID(-2289320563321795109L)
27 | class Document extends NodeSeq with Serializable {
28 |
29 | /**
30 | * An ordered list of child information items, in document
31 | * order. The list contains exactly one element information item. The
32 | * list also contains one processing instruction information item for
33 | * each processing instruction outside the document element, and one
34 | * comment information item for each comment outside the document
35 | * element. Processing instructions and comments within the DTD are
36 | * excluded. If there is a document type declaration, the list also
37 | * contains a document type declaration information item.
38 | */
39 | var children: Seq[Node] = _ // effectively an `immutable.Seq`, not changed due to binary compatibility
40 |
41 | /** The element information item corresponding to the document element. */
42 | var docElem: Node = _
43 |
44 | /** The dtd that comes with the document, if any */
45 | var dtd: scala.xml.dtd.DTD = _
46 |
47 | /**
48 | * An unordered set of notation information items, one for each notation
49 | * declared in the DTD. If any notation is multiply declared, this property
50 | * has no value.
51 | */
52 | def notations: Seq[scala.xml.dtd.NotationDecl] =
53 | dtd.notations
54 |
55 | /**
56 | * An unordered set of unparsed entity information items, one for each
57 | * unparsed entity declared in the DTD.
58 | */
59 | def unparsedEntities: Seq[scala.xml.dtd.EntityDecl] =
60 | dtd.unparsedEntities
61 |
62 | /** The base URI of the document entity. */
63 | var baseURI: String = _
64 |
65 | /**
66 | * The name of the character encoding scheme in which the document entity
67 | * is expressed.
68 | */
69 | var encoding: Option[String] = _
70 |
71 | /**
72 | * An indication of the standalone status of the document, either
73 | * true or false. This property is derived from the optional standalone
74 | * document declaration in the XML declaration at the beginning of the
75 | * document entity, and has no value (`None`) if there is no
76 | * standalone document declaration.
77 | */
78 | var standAlone: Option[Boolean] = _
79 |
80 | /**
81 | * A string representing the XML version of the document. This
82 | * property is derived from the XML declaration optionally present at
83 | * the beginning of the document entity, and has no value (`None`)
84 | * if there is no XML declaration.
85 | */
86 | var version: Option[String] = _
87 |
88 | /**
89 | * 9. This property is not strictly speaking part of the infoset of
90 | * the document. Rather it is an indication of whether the processor
91 | * has read the complete DTD. Its value is a boolean. If it is false,
92 | * then certain properties (indicated in their descriptions below) may
93 | * be unknown. If it is true, those properties are never unknown.
94 | */
95 | var allDeclarationsProcessed: Boolean = false
96 |
97 | // methods for NodeSeq
98 |
99 | override def theSeq: ScalaVersionSpecific.SeqOfNode = this.docElem
100 |
101 | override def canEqual(other: Any): Boolean = other match {
102 | case _: Document => true
103 | case _ => false
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/Attribute.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 |
16 | import scala.collection.Seq
17 |
18 | /**
19 | * This singleton object contains the `apply` and `unapply` methods for
20 | * convenient construction and deconstruction.
21 | *
22 | * @author Burak Emir
23 | */
24 | object Attribute {
25 | def unapply(x: Attribute): Option[(String, Seq[Node], MetaData)] = x match {
26 | case PrefixedAttribute(_, key, value, next) => Some((key, value, next))
27 | case UnprefixedAttribute(key, value, next) => Some((key, value, next))
28 | case _ => None
29 | }
30 |
31 | /** Convenience functions which choose Un/Prefixedness appropriately */
32 | def apply(key: String, value: Seq[Node], next: MetaData): Attribute =
33 | new UnprefixedAttribute(key, value, next)
34 |
35 | def apply(pre: String, key: String, value: String, next: MetaData): Attribute =
36 | if (pre == null || pre == "") new UnprefixedAttribute(key, value, next)
37 | else new PrefixedAttribute(pre, key, value, next)
38 |
39 | def apply(pre: String, key: String, value: Seq[Node], next: MetaData): Attribute =
40 | if (pre == null || pre == "") new UnprefixedAttribute(key, value, next)
41 | else new PrefixedAttribute(pre, key, value, next)
42 |
43 | def apply(pre: Option[String], key: String, value: Seq[Node], next: MetaData): Attribute =
44 | pre match {
45 | case None => new UnprefixedAttribute(key, value, next)
46 | case Some(p) => new PrefixedAttribute(p, key, value, next)
47 | }
48 | }
49 |
50 | /**
51 | * The `Attribute` trait defines the interface shared by both
52 | * [[scala.xml.PrefixedAttribute]] and [[scala.xml.UnprefixedAttribute]].
53 | *
54 | * @author Burak Emir
55 | */
56 | trait Attribute extends MetaData with ScalaVersionSpecificMetaData {
57 | def pre: String // will be null if unprefixed
58 | override val key: String
59 | override val value: ScalaVersionSpecific.SeqOfNode
60 | override val next: MetaData
61 |
62 | override def apply(key: String): ScalaVersionSpecific.SeqOfNode
63 | override def apply(namespace: String, scope: NamespaceBinding, key: String): ScalaVersionSpecific.SeqOfNode
64 | override def copy(next: MetaData): Attribute
65 |
66 | override def remove(key: String): MetaData =
67 | if (!isPrefixed && this.key == key) next
68 | else copy(next.remove(key))
69 |
70 | override def remove(namespace: String, scope: NamespaceBinding, key: String): MetaData =
71 | if (this.key == key && scope.getURI(pre) == namespace) next
72 | else copy(next.remove(namespace, scope, key))
73 |
74 | override def isPrefixed: Boolean = pre != null
75 |
76 | override def getNamespace(owner: Node): String
77 |
78 | override def wellformed(scope: NamespaceBinding): Boolean = {
79 | val arg: String = if (isPrefixed) scope.getURI(pre) else null
80 | (next(arg, scope, key) == null) && next.wellformed(scope)
81 | }
82 |
83 | /** Returns an iterator on attributes */
84 | override def iterator: Iterator[MetaData] =
85 | if (value == null) next.iterator
86 | else Iterator.single(this) ++ next.iterator
87 |
88 | override def size: Int =
89 | if (value == null) next.size
90 | else 1 + next.size
91 |
92 | /**
93 | * Appends string representation of only this attribute to stringbuffer.
94 | */
95 | override protected def toString1(sb: StringBuilder): Unit = {
96 | if (value == null)
97 | return
98 | if (isPrefixed)
99 | sb.append(s"$pre:")
100 |
101 | sb.append(s"$key=")
102 | val sb2: StringBuilder = new StringBuilder()
103 | Utility.sequenceToXML(value, TopScope, sb2, stripComments = true)
104 | Utility.appendQuoted(sb2.toString, sb)
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/jvm/src/test/scala/scala/xml/ReuseNodesTest.scala:
--------------------------------------------------------------------------------
1 | package scala.xml
2 |
3 | import scala.xml.transform._
4 | import scala.collection.Seq
5 | import org.junit.Assert.assertSame
6 | import org.junit.experimental.theories.Theories
7 | import org.junit.experimental.theories.Theory
8 | import org.junit.experimental.theories.DataPoints
9 | import org.junit.runner.RunWith
10 | /**
11 | * This test verifies that after the transform, the resultant xml node
12 | * uses as many old nodes as possible.
13 | *
14 | * Three transformers class for case -
15 | * One for original, one for modified, and one proposed which shows
16 | * all are equivalent when it comes to reusing as many nodes as possible
17 | */
18 | object ReuseNodesTest {
19 |
20 | class OriginalTranformr(rules: RewriteRule*) extends RuleTransformer(rules:_*) {
21 | override def transform(ns: Seq[Node]): Seq[Node] = {
22 | val xs: Seq[Seq[Node]] = ns.toStream.map(transform)
23 | val (xs1: Seq[(Seq[Node], Node)], xs2: Seq[(Seq[Node], Node)]) = xs.zip(ns).span { case (x, n) => unchanged(n, x) }
24 |
25 | if (xs2.isEmpty) ns
26 | else xs1.map(_._2) ++ xs2.head._1 ++ transform(ns.drop(xs1.length + 1))
27 | }
28 | override def transform(n: Node): Seq[Node] = super.transform(n)
29 | }
30 |
31 | class ModifiedTranformr(rules: RewriteRule*) extends RuleTransformer(rules:_*) {
32 | override def transform(ns: Seq[Node]): Seq[Node] = {
33 | val changed: Seq[Node] = ns.flatMap(transform)
34 |
35 | if (changed.length != ns.length || changed.zip(ns).exists(p => p._1 != p._2)) changed
36 | else ns
37 | }
38 | override def transform(n: Node): Seq[Node] = super.transform(n)
39 | }
40 |
41 | class AlternateTranformr(rules: RewriteRule*) extends RuleTransformer(rules:_*) {
42 | override def transform(ns: Seq[Node]): Seq[Node] = {
43 | val xs: Seq[Seq[Node]] = ns.toStream.map(transform)
44 | val (xs1: Seq[(Seq[Node], Node)], xs2: Seq[(Seq[Node], Node)]) = xs.zip(ns).span { case (x, n) => unchanged(n, x) }
45 |
46 | if (xs2.isEmpty) ns
47 | else xs1.map(_._2) ++ xs2.head._1 ++ transform(ns.drop(xs1.length + 1))
48 | }
49 | override def transform(n: Node): Seq[Node] = super.transform(n)
50 | }
51 |
52 | def rewriteRule: RewriteRule = new RewriteRule {
53 | override def transform(n: Node): NodeSeq = n match {
54 | case n if n.label == "change" => Elem(
55 | n.prefix, "changed", n.attributes, n.scope, n.child.isEmpty, n.child : _*)
56 | case _ => n
57 | }
58 | }
59 |
60 | @DataPoints
61 | def tranformers(): Array[RuleTransformer] = Array(
62 | new OriginalTranformr(rewriteRule),
63 | new ModifiedTranformr(rewriteRule),
64 | new AlternateTranformr(rewriteRule))
65 | }
66 |
67 | @RunWith(classOf[Theories])
68 | class ReuseNodesTest {
69 |
70 | @Theory
71 | def transformReferentialEquality(rt: RuleTransformer): Unit = {
72 | val original: Elem =
73 | val tranformed: Seq[Node] = rt.transform(original)
74 | assertSame(original, tranformed)
75 | }
76 |
77 | @Theory
78 | def transformReferentialEqualityOnly(rt: RuleTransformer): Unit = {
79 | val original: Elem =
80 | val transformed: Seq[Node] = rt.transform(original)
81 | recursiveAssert(original,transformed)
82 | }
83 |
84 | def recursiveAssert(original: Seq[Node], transformed: Seq[Node]): Unit = {
85 | original.zip(transformed).foreach {
86 | case (x, y) => recursiveAssert(x, y)
87 | }
88 | }
89 |
90 | def recursiveAssert(original: Node, transformed: Node): Unit = {
91 | transformed.label match {
92 | case "changed" => // do nothing expect this node to be changed
93 | recursiveAssert(original.child,transformed.child)
94 | case _ =>
95 | assertSame(original, transformed)
96 | // No need to check for children, node being immutable
97 | // children can't be different if parents are referentially equal
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/dtd/impl/SubsetConstruction.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml.dtd.impl
15 |
16 | import scala.collection.{immutable, mutable}
17 |
18 | // TODO: still used in ContentModel -- @deprecated("This class will be removed", "2.10.0")
19 | private[dtd] class SubsetConstruction[T <: AnyRef](val nfa: NondetWordAutom[T]) {
20 | import nfa.labels
21 |
22 | def selectTag(Q: immutable.BitSet, finals: Array[Int]): Int =
23 | Q.map(finals).filter(_ > 0).min
24 |
25 | def determinize: DetWordAutom[T] = {
26 | // for assigning numbers to bitsets
27 | val indexMap: mutable.Map[immutable.BitSet, Int] = mutable.Map[immutable.BitSet, Int]()
28 | val invIndexMap: mutable.Map[Int, immutable.BitSet] = mutable.Map[Int, immutable.BitSet]()
29 | var ix: Int = 0
30 |
31 | // we compute the dfa with states = bitsets
32 | val q0: immutable.BitSet = immutable.BitSet(0) // the set { 0 }
33 | val sink: immutable.BitSet = immutable.BitSet.empty // the set { }
34 |
35 | var states: Set[immutable.BitSet] = Set(q0, sink) // initial set of sets
36 | val delta: mutable.HashMap[immutable.BitSet, mutable.HashMap[T, immutable.BitSet]] =
37 | new mutable.HashMap[immutable.BitSet, mutable.HashMap[T, immutable.BitSet]]
38 | val deftrans: mutable.Map[immutable.BitSet, immutable.BitSet] = mutable.Map(q0 -> sink, sink -> sink) // initial transitions
39 | val finals: mutable.Map[immutable.BitSet, Int] = mutable.Map()
40 | var rest: immutable.List[immutable.BitSet] = immutable.List.empty[immutable.BitSet]
41 |
42 | rest = q0 :: sink :: rest
43 |
44 | def addFinal(q: immutable.BitSet): Unit = {
45 | if (nfa.containsFinal(q))
46 | finals(q) = selectTag(q, nfa.finals)
47 | }
48 | def add(Q: immutable.BitSet): Unit = {
49 | if (!states(Q)) {
50 | states += Q
51 | rest = Q :: rest
52 | addFinal(Q)
53 | }
54 | }
55 |
56 | addFinal(q0) // initial state may also be a final state
57 |
58 | while (rest.nonEmpty) {
59 | val P: immutable.BitSet = rest.head
60 | rest = rest.tail
61 | // assign a number to this bitset
62 | indexMap(P) = ix
63 | invIndexMap(ix) = P
64 | ix += 1
65 |
66 | // make transition map
67 | val Pdelta: mutable.HashMap[T, immutable.BitSet] = new mutable.HashMap[T, immutable.BitSet]
68 | delta.update(P, Pdelta)
69 |
70 | labels.foreach { label =>
71 | val Q: immutable.BitSet = nfa.next(P, label)
72 | Pdelta.update(label, Q)
73 | add(Q)
74 | }
75 |
76 | // collect default transitions
77 | val Pdef: immutable.BitSet = nfa nextDefault P
78 | deftrans(P) = Pdef
79 | add(Pdef)
80 | }
81 |
82 | // create DetWordAutom, using indices instead of sets
83 | val nstatesR: Int = states.size
84 | val deltaR: Array[mutable.Map[T, Int]] = new Array[mutable.Map[T, Int]](nstatesR)
85 | val defaultR: Array[Int] = new Array[Int](nstatesR)
86 | val finalsR: Array[Int] = new Array[Int](nstatesR)
87 |
88 | for (Q <- states) {
89 | val q: Int = indexMap(Q)
90 | val trans: mutable.Map[T, immutable.BitSet] = delta(Q)
91 | val transDef: immutable.BitSet = deftrans(Q)
92 | val qDef: Int = indexMap(transDef)
93 | val ntrans: mutable.Map[T, Int] = new mutable.HashMap[T, Int]()
94 |
95 | for ((label, value) <- trans) {
96 | val p: Int = indexMap(value)
97 | if (p != qDef)
98 | ntrans.update(label, p)
99 | }
100 |
101 | deltaR(q) = ntrans
102 | defaultR(q) = qDef
103 | }
104 |
105 | finals.foreach { case (k, v) => finalsR(indexMap(k)) = v }
106 |
107 | new DetWordAutom[T] {
108 | override val nstates: Int = nstatesR
109 | override val delta: Array[mutable.Map[T, Int]] = deltaR
110 | override val default: Array[Int] = defaultR
111 | override val finals: Array[Int] = finalsR
112 | }
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/Elem.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 |
16 | import scala.collection.Seq
17 |
18 | /**
19 | * This singleton object contains the `apply` and `unapplySeq` methods for
20 | * convenient construction and deconstruction. It is possible to deconstruct
21 | * any `Node` instance (that is not a `SpecialNode` or a `Group`) using the
22 | * syntax `case Elem(prefix, label, attribs, scope, child @ _*) => ...`
23 | */
24 | // Note: used by the Scala compiler.
25 | object Elem {
26 |
27 | def apply(prefix: String, label: String, attributes: MetaData, scope: NamespaceBinding, minimizeEmpty: Boolean, child: Node*): Elem =
28 | new Elem(prefix, label, attributes, scope, minimizeEmpty, child: _*)
29 |
30 | def unapplySeq(n: Node): Option[(String, String, MetaData, NamespaceBinding, ScalaVersionSpecific.SeqOfNode)] =
31 | n match {
32 | case _: SpecialNode | _: Group => None
33 | case _ => Some((n.prefix, n.label, n.attributes, n.scope, n.child))
34 | }
35 | }
36 |
37 | /**
38 | * An immutable data object representing an XML element.
39 | *
40 | * Child elements can be other [[Elem]]s or any one of the other [[Node]] types.
41 | *
42 | * XML attributes are implemented with the [[scala.xml.MetaData]] base
43 | * class.
44 | *
45 | * Optional XML namespace scope is represented by
46 | * [[scala.xml.NamespaceBinding]].
47 | *
48 | * @param prefix namespace prefix (may be null, but not the empty string)
49 | * @param label the element name
50 | * @param attributes1 the attribute map
51 | * @param scope the scope containing the namespace bindings
52 | * @param minimizeEmpty `true` if this element should be serialized as minimized (i.e. "<el/>") when
53 | * empty; `false` if it should be written out in long form.
54 | * @param child the children of this node
55 | */
56 | // Note: used by the Scala compiler.
57 | class Elem(
58 | override val prefix: String,
59 | override val label: String,
60 | attributes1: MetaData,
61 | override val scope: NamespaceBinding,
62 | val minimizeEmpty: Boolean,
63 | override val child: Node*
64 | ) extends Node with Serializable {
65 |
66 | final override def doCollectNamespaces: Boolean = true
67 | final override def doTransform: Boolean = true
68 |
69 | override val attributes: MetaData = MetaData.normalize(attributes1, scope)
70 |
71 | if (prefix == "")
72 | throw new IllegalArgumentException("prefix of zero length, use null instead")
73 |
74 | if (scope == null)
75 | throw new IllegalArgumentException("scope is null, use scala.xml.TopScope for empty scope")
76 |
77 | //@todo: copy the children,
78 | // setting namespace scope if necessary
79 | // cleaning adjacent text nodes if necessary
80 |
81 | override protected def basisForHashCode: Seq[Any] =
82 | prefix :: label :: attributes :: child.toList
83 |
84 | /**
85 | * Returns a new element with updated attributes, resolving namespace uris
86 | * from this element's scope. See MetaData.update for details.
87 | *
88 | * @param updates MetaData with new and updated attributes
89 | * @return a new symbol with updated attributes
90 | */
91 | final def %(updates: MetaData): Elem =
92 | copy(attributes = MetaData.update(attributes, scope, updates))
93 |
94 | /**
95 | * Returns a copy of this element with any supplied arguments replacing
96 | * this element's value for that field.
97 | *
98 | * @return a new symbol with updated attributes
99 | */
100 | def copy(
101 | prefix: String = this.prefix,
102 | label: String = this.label,
103 | attributes: MetaData = this.attributes,
104 | scope: NamespaceBinding = this.scope,
105 | minimizeEmpty: Boolean = this.minimizeEmpty,
106 | child: Seq[Node] = this.child
107 | ): Elem = Elem(prefix, label, attributes, scope, minimizeEmpty, child.toSeq: _*)
108 |
109 | /**
110 | * Returns concatenation of `text(n)` for each child `n`.
111 | */
112 | override def text: String = child.map(_.text).mkString
113 | }
114 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/Equality.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 |
16 | import scala.collection.Seq
17 |
18 | /**
19 | * In an attempt to contain the damage being inflicted on consistency by the
20 | * ad hoc `equals` methods spread around `xml`, the logic is centralized and
21 | * all the `xml` classes go through the `xml.Equality trait`. There are two
22 | * forms of `xml` comparison.
23 | *
24 | * 1. `'''def''' strict_==(other: scala.xml.Equality)`
25 | *
26 | * This one tries to honor the little things like symmetry and hashCode
27 | * contracts. The `equals` method routes all comparisons through this.
28 | *
29 | * 1. `xml_==(other: Any)`
30 | *
31 | * This one picks up where `strict_==` leaves off. It might declare any two
32 | * things equal.
33 | *
34 | * As things stood, the logic not only made a mockery of the collections
35 | * equals contract, but also laid waste to that of case classes.
36 | *
37 | * Among the obstacles to sanity are/were:
38 | *
39 | * Node extends NodeSeq extends Seq[Node]
40 | * MetaData extends Iterable[MetaData]
41 | * The hacky "Group" xml node which throws exceptions
42 | * with wild abandon, so don't get too close
43 | * Rampant asymmetry and impossible hashCodes
44 | * Most classes claiming to be equal to "String" if
45 | * some specific stringification of it was the same.
46 | * String was never going to return the favor.
47 | */
48 |
49 | object Equality {
50 | def asRef(x: Any): AnyRef = x.asInstanceOf[AnyRef]
51 |
52 | /**
53 | * Note - these functions assume strict equality has already failed.
54 | */
55 | def compareBlithely(x1: AnyRef, x2: String): Boolean = x1 match {
56 | case x: Atom[?] => x.data == x2
57 | case x: NodeSeq => x.text == x2
58 | case _ => false
59 | }
60 | def compareBlithely(x1: AnyRef, x2: Node): Boolean = x1 match {
61 | case x: NodeSeq if x.length == 1 => x2 == x(0)
62 | case _ => false
63 | }
64 | def compareBlithely(x1: AnyRef, x2: AnyRef): Boolean =
65 | if (x1 == null || x2 == null) x1 == null && x2 == null else x2 match {
66 | case s: String => compareBlithely(x1, s)
67 | case n: Node => compareBlithely(x1, n)
68 | case _ => false
69 | }
70 | }
71 |
72 | trait Equality extends scala.Equals {
73 | protected def basisForHashCode: Seq[Any]
74 |
75 | def strict_==(other: Equality): Boolean
76 | def strict_!=(other: Equality): Boolean = !strict_==(other)
77 |
78 | /**
79 | * We insist we're only equal to other `xml.Equality` implementors,
80 | * which heads off a lot of inconsistency up front.
81 | */
82 | override def canEqual(other: Any): Boolean = other match {
83 | case _: Equality => true
84 | case _ => false
85 | }
86 |
87 | /**
88 | * It's be nice to make these final, but there are probably
89 | * people out there subclassing the XML types, especially when
90 | * it comes to equals. However WE at least can pretend they
91 | * are final since clearly individual classes cannot be trusted
92 | * to maintain a semblance of order.
93 | */
94 | override def hashCode: Int = basisForHashCode.##
95 | override def equals(other: Any): Boolean = doComparison(other, blithe = false)
96 | final def xml_==(other: Any): Boolean = doComparison(other, blithe = true)
97 | final def xml_!=(other: Any): Boolean = !xml_==(other)
98 |
99 | /**
100 | * The "blithe" parameter expresses the caller's unconcerned attitude
101 | * regarding the usual constraints on equals. The method is thereby
102 | * given carte blanche to declare any two things equal.
103 | */
104 | private def doComparison(other: Any, blithe: Boolean): Boolean = {
105 | val strictlyEqual: Boolean = other match {
106 | case x: AnyRef if this.eq(x) => true
107 | case x: Equality => x.canEqual(this) && this.strict_==(x)
108 | case _ => false
109 | }
110 |
111 | strictlyEqual || (blithe && Equality.compareBlithely(this, Equality.asRef(other)))
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/dtd/ContentModel.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 | package dtd
16 |
17 | import scala.collection.Seq
18 | import scala.xml.dtd.impl._
19 | import scala.xml.Utility.sbToString
20 | import PartialFunction._
21 |
22 | /*
23 | @deprecated("Avoidance", since="2.10")
24 | trait ContentModelLaundry extends WordExp
25 | object ContentModelLaundry extends ContentModelLaundry {
26 | }
27 | */
28 |
29 | object ContentModel extends WordExp {
30 |
31 | override type _labelT = ElemName
32 | override type _regexpT = RegExp
33 |
34 | @deprecated("Avoidance", since="2.10")
35 | trait Translator extends WordBerrySethi
36 | object Translator extends Translator {
37 | override val lang: ContentModel.this.type = ContentModel.this
38 | }
39 |
40 | case class ElemName(name: String) extends Label {
41 | override def toString: String = s"""ElemName("$name")"""
42 | }
43 |
44 | def isMixed(cm: ContentModel): Boolean = cond(cm) { case _: MIXED => true }
45 | def containsText(cm: ContentModel): Boolean = (cm == PCDATA) || isMixed(cm)
46 |
47 | def getLabels(r: RegExp): Set[String] = {
48 | def traverse(r: RegExp): Set[String] = r match { // !!! check for match translation problem
49 | case Letter(ElemName(name)) => Set(name)
50 | case Star(x@_) => traverse(x) // bug if x@_*
51 | case Sequ(xs@_*) => Set(xs.flatMap(traverse): _*)
52 | case Alt(xs@_*) => Set(xs.flatMap(traverse): _*)
53 | }
54 |
55 | traverse(r)
56 | }
57 |
58 | def buildString(r: RegExp): String = sbToString(buildString(r, _))
59 |
60 | /* precond: rs.length >= 1 */
61 | private def buildString(rs: Seq[RegExp], sb: StringBuilder, sep: Char): Unit = {
62 | buildString(rs.head, sb)
63 | for (z <- rs.tail) {
64 | sb.append(sep)
65 | buildString(z, sb)
66 | }
67 | }
68 |
69 | def buildString(c: ContentModel, sb: StringBuilder): StringBuilder = c match {
70 | case ANY => sb.append("ANY")
71 | case EMPTY => sb.append("EMPTY")
72 | case PCDATA => sb.append("(#PCDATA)")
73 | case ELEMENTS(_) | MIXED(_) => c.buildString(sb)
74 | }
75 |
76 | def buildString(r: RegExp, sb: StringBuilder): StringBuilder =
77 | r match { // !!! check for match translation problem
78 | case Eps =>
79 | sb
80 | case Sequ(rs@_*) =>
81 | sb.append('('); buildString(rs, sb, ','); sb.append(')')
82 | case Alt(rs@_*) =>
83 | sb.append('('); buildString(rs, sb, '|'); sb.append(')')
84 | case Star(r: RegExp) =>
85 | sb.append('('); buildString(r, sb); sb.append(")*")
86 | case Letter(ElemName(name)) =>
87 | sb.append(name)
88 | }
89 | }
90 |
91 | sealed abstract class ContentModel {
92 | override def toString: String = sbToString(buildString)
93 | def buildString(sb: StringBuilder): StringBuilder
94 | }
95 |
96 | import ContentModel.RegExp
97 |
98 | case object PCDATA extends ContentModel {
99 | override def buildString(sb: StringBuilder): StringBuilder = sb.append("(#PCDATA)")
100 | }
101 | case object EMPTY extends ContentModel {
102 | override def buildString(sb: StringBuilder): StringBuilder = sb.append("EMPTY")
103 | }
104 | case object ANY extends ContentModel {
105 | override def buildString(sb: StringBuilder): StringBuilder = sb.append("ANY")
106 | }
107 | sealed abstract class DFAContentModel extends ContentModel {
108 | import ContentModel.{ElemName, Translator}
109 | def r: RegExp
110 |
111 | lazy val dfa: DetWordAutom[ElemName] = {
112 | val nfa: NondetWordAutom[ElemName] = Translator.automatonFrom(r, 1)
113 | new SubsetConstruction(nfa).determinize
114 | }
115 | }
116 |
117 | case class MIXED(override val r: RegExp) extends DFAContentModel {
118 | import ContentModel.Alt
119 |
120 | override def buildString(sb: StringBuilder): StringBuilder = {
121 | val newAlt: Alt = r match { case Alt(rs@_*) => Alt(rs.drop(1): _*) }
122 |
123 | sb.append("(#PCDATA|")
124 | ContentModel.buildString(newAlt: RegExp, sb)
125 | sb.append(")*")
126 | }
127 | }
128 |
129 | case class ELEMENTS(override val r: RegExp) extends DFAContentModel {
130 | override def buildString(sb: StringBuilder): StringBuilder =
131 | ContentModel.buildString(r, sb)
132 | }
133 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/parsing/MarkupHandler.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 | package parsing
16 |
17 | import scala.collection.mutable
18 | import scala.io.Source
19 | import scala.xml.dtd._
20 |
21 | /**
22 | * class that handles markup - provides callback methods to MarkupParser.
23 | * the default is nonvalidating behaviour
24 | *
25 | * @author Burak Emir
26 | * @todo can we ignore more entity declarations (i.e. those with extIDs)?
27 | * @todo expanding entity references
28 | */
29 | abstract class MarkupHandler {
30 |
31 | /** returns true is this markup handler is validating */
32 | val isValidating: Boolean = false
33 |
34 | var decls: List[Decl] = Nil
35 | var ent: mutable.Map[String, EntityDecl] = new mutable.HashMap[String, EntityDecl]()
36 |
37 | def lookupElemDecl(Label: String): ElemDecl =
38 | (for (case z@ElemDecl(Label, _) <- decls) yield z).headOption.orNull
39 |
40 | def replacementText(entityName: String): Source =
41 | Source.fromString(ent.get(entityName) match {
42 | case Some(ParsedEntityDecl(_, IntDef(value))) => value
43 | case Some(ParameterEntityDecl(_, IntDef(value))) => s" value "
44 | case Some(_) => s""
45 | case None => s""
46 | })
47 |
48 | def endDTD(n: String): Unit = ()
49 |
50 | /**
51 | * callback method invoked by MarkupParser after start-tag of element.
52 | *
53 | * @param pos the position in the sourcefile
54 | * @param pre the prefix
55 | * @param label the local name
56 | * @param attrs the attributes (metadata)
57 | */
58 | def elemStart(pos: Int, pre: String, label: String, attrs: MetaData, scope: NamespaceBinding): Unit = ()
59 |
60 | /**
61 | * callback method invoked by MarkupParser after end-tag of element.
62 | *
63 | * @param pos the position in the source file
64 | * @param pre the prefix
65 | * @param label the local name
66 | */
67 | def elemEnd(pos: Int, pre: String, label: String): Unit = ()
68 |
69 | /**
70 | * callback method invoked by MarkupParser after parsing an element,
71 | * between the elemStart and elemEnd callbacks
72 | *
73 | * @param pos the position in the source file
74 | * @param pre the prefix
75 | * @param label the local name
76 | * @param attrs the attributes (metadata)
77 | * @param empty `true` if the element was previously empty; `false` otherwise.
78 | * @param args the children of this element
79 | */
80 | def elem(pos: Int, pre: String, label: String, attrs: MetaData, scope: NamespaceBinding, empty: Boolean, args: NodeSeq): NodeSeq
81 |
82 | /**
83 | * callback method invoked by MarkupParser after parsing PI.
84 | */
85 | def procInstr(pos: Int, target: String, txt: String): NodeSeq
86 |
87 | /**
88 | * callback method invoked by MarkupParser after parsing comment.
89 | */
90 | def comment(pos: Int, comment: String): NodeSeq
91 |
92 | /**
93 | * callback method invoked by MarkupParser after parsing entity ref.
94 | * @todo expanding entity references
95 | */
96 | def entityRef(pos: Int, n: String): NodeSeq
97 |
98 | /**
99 | * callback method invoked by MarkupParser after parsing text.
100 | */
101 | def text(pos: Int, txt: String): NodeSeq
102 |
103 | // DTD handler methods
104 |
105 | def elemDecl(n: String, cmstr: String): Unit = ()
106 |
107 | def attListDecl(name: String, attList: List[AttrDecl]): Unit = ()
108 |
109 | private def someEntityDecl(name: String, edef: EntityDef, f: (String, EntityDef) => EntityDecl): Unit =
110 | edef match {
111 | case _: ExtDef if !isValidating => // ignore (cf REC-xml 4.4.1)
112 | case _ =>
113 | val y: EntityDecl = f(name, edef)
114 | decls ::= y
115 | ent.update(name, y)
116 | }
117 |
118 | def parameterEntityDecl(name: String, edef: EntityDef): Unit =
119 | someEntityDecl(name, edef, ParameterEntityDecl.apply)
120 |
121 | def parsedEntityDecl(name: String, edef: EntityDef): Unit =
122 | someEntityDecl(name, edef, ParsedEntityDecl.apply)
123 |
124 | def peReference(name: String): Unit = { decls ::= PEReference(name) }
125 | def unparsedEntityDecl(name: String, extID: ExternalID, notat: String): Unit = ()
126 | def notationDecl(notat: String, extID: ExternalID): Unit = ()
127 | def reportSyntaxError(pos: Int, str: String): Unit
128 | }
129 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/parsing/XhtmlEntities.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 | package parsing
16 |
17 | import scala.xml.dtd.{ IntDef, ParsedEntityDecl }
18 |
19 | /**
20 | * @author (c) David Pollak 2007 WorldWide Conferencing, LLC.
21 | *
22 | */
23 | object XhtmlEntities {
24 | val entList: List[(String, Int)] = List(("quot", 34), ("amp", 38), ("lt", 60), ("gt", 62), ("nbsp", 160), ("iexcl", 161), ("cent", 162), ("pound", 163), ("curren", 164), ("yen", 165),
25 | ("euro", 8364), ("brvbar", 166), ("sect", 167), ("uml", 168), ("copy", 169), ("ordf", 170), ("laquo", 171), ("shy", 173), ("reg", 174), ("trade", 8482),
26 | ("macr", 175), ("deg", 176), ("plusmn", 177), ("sup2", 178), ("sup3", 179), ("acute", 180), ("micro", 181), ("para", 182), ("middot", 183), ("cedil", 184),
27 | ("sup1", 185), ("ordm", 186), ("raquo", 187), ("frac14", 188), ("frac12", 189), ("frac34", 190), ("iquest", 191), ("times", 215), ("divide", 247),
28 | ("Agrave", 192), ("Aacute", 193), ("Acirc", 194), ("Atilde", 195), ("Auml", 196), ("Aring", 197), ("AElig", 198), ("Ccedil", 199), ("Egrave", 200),
29 | ("Eacute", 201), ("Ecirc", 202), ("Euml", 203), ("Igrave", 204), ("Iacute", 205), ("Icirc", 206), ("Iuml", 207), ("ETH", 208), ("Ntilde", 209),
30 | ("Ograve", 210), ("Oacute", 211), ("Ocirc", 212), ("Otilde", 213), ("Ouml", 214), ("Oslash", 216), ("Ugrave", 217), ("Uacute", 218), ("Ucirc", 219),
31 | ("Uuml", 220), ("Yacute", 221), ("THORN", 222), ("szlig", 223), ("agrave", 224), ("aacute", 225), ("acirc", 226), ("atilde", 227), ("auml", 228),
32 | ("aring", 229), ("aelig", 230), ("ccedil", 231), ("egrave", 232), ("eacute", 233), ("ecirc", 234), ("euml", 235), ("igrave", 236), ("iacute", 237),
33 | ("icirc", 238), ("iuml", 239), ("eth", 240), ("ntilde", 241), ("ograve", 242), ("oacute", 243), ("ocirc", 244), ("otilde", 245), ("ouml", 246),
34 | ("oslash", 248), ("ugrave", 249), ("uacute", 250), ("ucirc", 251), ("uuml", 252), ("yacute", 253), ("thorn", 254), ("yuml", 255), ("OElig", 338),
35 | ("oelig", 339), ("Scaron", 352), ("scaron", 353), ("Yuml", 376), ("circ", 710), ("ensp", 8194), ("emsp", 8195), ("zwnj", 204), ("zwj", 8205), ("lrm", 8206),
36 | ("rlm", 8207), ("ndash", 8211), ("mdash", 8212), ("lsquo", 8216), ("rsquo", 8217), ("sbquo", 8218), ("ldquo", 8220), ("rdquo", 8221), ("bdquo", 8222),
37 | ("dagger", 8224), ("Dagger", 8225), ("permil", 8240), ("lsaquo", 8249), ("rsaquo", 8250), ("fnof", 402), ("bull", 8226), ("hellip", 8230), ("prime", 8242),
38 | ("Prime", 8243), ("oline", 8254), ("frasl", 8260), ("weierp", 8472), ("image", 8465), ("real", 8476), ("alefsym", 8501), ("larr", 8592), ("uarr", 8593),
39 | ("rarr", 8594), ("darr", 8595), ("harr", 8596), ("crarr", 8629), ("lArr", 8656), ("uArr", 8657), ("rArr", 8658), ("dArr", 8659), ("hArr", 8660),
40 | ("forall", 8704), ("part", 8706), ("exist", 8707), ("empty", 8709), ("nabla", 8711), ("isin", 8712), ("notin", 8713), ("ni", 8715), ("prod", 8719),
41 | ("sum", 8721), ("minus", 8722), ("lowast", 8727), ("radic", 8730), ("prop", 8733), ("infin", 8734), ("ang", 8736), ("and", 8743), ("or", 8744),
42 | ("cap", 8745), ("cup", 8746), ("int", 8747), ("there4", 8756), ("sim", 8764), ("cong", 8773), ("asymp", 8776), ("ne", 8800), ("equiv", 8801), ("le", 8804),
43 | ("ge", 8805), ("sub", 8834), ("sup", 8835), ("nsub", 8836), ("sube", 8838), ("supe", 8839), ("oplus", 8853), ("otimes", 8855), ("perp", 8869), ("sdot", 8901),
44 | ("lceil", 8968), ("rceil", 8969), ("lfloor", 8970), ("rfloor", 8971), ("lang", 9001), ("rang", 9002), ("loz", 9674), ("spades", 9824), ("clubs", 9827),
45 | ("hearts", 9829), ("diams", 9830), ("Alpha", 913), ("Beta", 914), ("Gamma", 915), ("Delta", 916), ("Epsilon", 917), ("Zeta", 918), ("Eta", 919),
46 | ("Theta", 920), ("Iota", 921), ("Kappa", 922), ("Lambda", 923), ("Mu", 924), ("Nu", 925), ("Xi", 926), ("Omicron", 927), ("Pi", 928), ("Rho", 929),
47 | ("Sigma", 931), ("Tau", 932), ("Upsilon", 933), ("Phi", 934), ("Chi", 935), ("Psi", 936), ("Omega", 937), ("alpha", 945), ("beta", 946), ("gamma", 947),
48 | ("delta", 948), ("epsilon", 949), ("zeta", 950), ("eta", 951), ("theta", 952), ("iota", 953), ("kappa", 954), ("lambda", 955), ("mu", 956), ("nu", 957),
49 | ("xi", 958), ("omicron", 959), ("pi", 960), ("rho", 961), ("sigmaf", 962), ("sigma", 963), ("tau", 964), ("upsilon", 965), ("phi", 966), ("chi", 967),
50 | ("psi", 968), ("omega", 969), ("thetasym", 977), ("upsih", 978), ("piv", 982))
51 |
52 | val entMap: Map[String, Char] = Map.empty[String, Char] ++ entList.map { case (name, value) => (name, value.toChar) }
53 |
54 | val entities: List[(String, ParsedEntityDecl)] = entList.
55 | map { case (name, value) => (name, ParsedEntityDecl(name, IntDef(value.toChar.toString))) }
56 |
57 | def apply(): List[(String, ParsedEntityDecl)] = entities
58 | }
59 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/XML.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 |
16 | import factory.XMLLoader
17 | import java.io.{File, FileDescriptor, FileInputStream, FileOutputStream, InputStream, Reader, StringReader, Writer}
18 | import java.nio.channels.Channels
19 | import scala.util.control.Exception
20 |
21 | object Source {
22 | def fromFile(name: String): InputSource = fromFile(new File(name))
23 | def fromFile(file: File): InputSource = fromUrl(file.toURI.toURL)
24 | def fromUrl(url: java.net.URL): InputSource = fromSysId(url.toString)
25 | def fromSysId(sysID: String): InputSource = new InputSource(sysID)
26 | def fromFile(fd: FileDescriptor): InputSource = fromInputStream(new FileInputStream(fd))
27 | def fromInputStream(is: InputStream): InputSource = new InputSource(is)
28 | def fromString(string: String): InputSource = fromReader(new StringReader(string))
29 | def fromReader(reader: Reader): InputSource = new InputSource(reader)
30 | }
31 |
32 | /**
33 | * Governs how empty elements (i.e. those without child elements) should be serialized.
34 | */
35 | object MinimizeMode extends Enumeration {
36 | /**
37 | * Minimize empty tags if they were originally empty when parsed, or if they were constructed
38 | * with [[scala.xml.Elem]]`#minimizeEmpty` == true
39 | */
40 | val Default: Value = Value
41 |
42 | /**
43 | * Always minimize empty tags. Note that this may be problematic for XHTML, in which
44 | * case [[scala.xml.Xhtml]]`#toXhtml` should be used instead.
45 | */
46 | val Always: Value = Value
47 |
48 | /**
49 | * Never minimize empty tags.
50 | */
51 | val Never: Value = Value
52 | }
53 |
54 | /**
55 | * The object `XML` provides constants, and functions to load
56 | * and save XML elements. Use this when data binding is not desired, i.e.
57 | * when XML is handled using `Symbol` nodes.
58 | *
59 | * @author Burak Emir
60 | */
61 | object XML extends XMLLoader[Elem] {
62 | val xml: String = "xml"
63 | val xmlns: String = "xmlns"
64 | val namespace: String = "http://www.w3.org/XML/1998/namespace"
65 | val preserve: String = "preserve"
66 | val space: String = "space"
67 | val lang: String = "lang"
68 | val encoding: String = "UTF-8"
69 |
70 | /** Returns an XMLLoader whose load* methods will use the supplied SAXParser. */
71 | def withSAXParser(p: SAXParser): XMLLoader[Elem] = new XMLLoader[Elem] {
72 | override val parser: SAXParser = p
73 | }
74 |
75 | /** Returns an XMLLoader whose load* methods will use the supplied XMLReader. */
76 | def withXMLReader(r: XMLReader): XMLLoader[Elem] = new XMLLoader[Elem] {
77 | override val reader: XMLReader = r
78 | }
79 |
80 | /**
81 | * Saves a node to a file with given filename using given encoding
82 | * optionally with xmldecl and doctype declaration.
83 | *
84 | * Note: Before scala-xml 1.1.0, the default encoding was ISO-8859-1 (latin1).
85 | * If your code depends on characters in non-ASCII latin1 range, specify
86 | * ISO-8859-1 encoding explicitly.
87 | *
88 | * @param filename the filename
89 | * @param node the xml node we want to write
90 | * @param enc encoding to use
91 | * @param xmlDecl if true, write xml declaration
92 | * @param doctype if not null, write doctype declaration
93 | */
94 | final def save(
95 | filename: String,
96 | node: Node,
97 | enc: String = "UTF-8",
98 | xmlDecl: Boolean = false,
99 | doctype: dtd.DocType = null
100 | ): Unit = {
101 | val fos: FileOutputStream = new FileOutputStream(filename)
102 | val w: Writer = Channels.newWriter(fos.getChannel, enc)
103 |
104 | Exception.ultimately(w.close())(
105 | write(w, node, enc, xmlDecl, doctype)
106 | )
107 | }
108 |
109 | /**
110 | * Writes the given node using writer, optionally with xml decl and doctype.
111 | * It's the caller's responsibility to close the writer.
112 | *
113 | * @param w the writer
114 | * @param node the xml node we want to write
115 | * @param enc the string to be used in `xmlDecl`
116 | * @param xmlDecl if true, write xml declaration
117 | * @param doctype if not null, write doctype declaration
118 | */
119 | final def write(
120 | w: Writer,
121 | node: Node,
122 | enc: String,
123 | xmlDecl: Boolean,
124 | doctype: dtd.DocType,
125 | minimizeTags: MinimizeMode.Value = MinimizeMode.Default
126 | ): Unit = {
127 | /* TODO: optimize by giving writer parameter to toXML*/
128 | if (xmlDecl) w.write(s"\n")
129 | if (doctype != null) w.write(s"$doctype\n")
130 | w.write(Utility.serialize(node, minimizeTags = minimizeTags).toString)
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2.1
2 |
3 | executors:
4 | scala_jdk8_executor:
5 | docker:
6 | - image: cimg/openjdk:8.0-node
7 | resource_class: small
8 | scala_jdk11_executor:
9 | docker:
10 | - image: cimg/openjdk:11.0-node
11 | resource_class: small
12 | scala_jdk17_executor:
13 | docker:
14 | - image: cimg/openjdk:17.0-node
15 | resource_class: small
16 | scala_jdk21_executor:
17 | docker:
18 | - image: cimg/openjdk:21.0-node
19 | resource_class: small
20 |
21 | commands:
22 | sbt_cmd:
23 | description: "Build with sbt"
24 | parameters:
25 | scala_version:
26 | type: string
27 | default: 2.12.18
28 | sbt_tasks:
29 | type: string
30 | default: update compile test:compile test doc package osgiBundle
31 | steps:
32 | - restore_cache:
33 | keys:
34 | - sbt-deps-v1-{{ checksum "build.sbt" }}
35 | - sbt-deps-v1-
36 | - run: sbt -Dsbt.io.jdktimestamps=true ++<< parameters.scala_version >> << parameters.sbt_tasks >>
37 | - save_cache:
38 | key: sbt-deps-v1-{{ checksum "build.sbt" }}
39 | paths:
40 | - "~/.cache/coursier"
41 | - "~/.ivy2/cache"
42 | - "~/.sbt"
43 | - "~/.m2"
44 |
45 | jobs:
46 | scala_job:
47 | executor: scala_<>_executor
48 | parameters:
49 | scala_version:
50 | description: "Scala version"
51 | default: 2.12.18
52 | type: string
53 | java_version:
54 | description: "Java version"
55 | default: jdk8
56 | type: string
57 | steps:
58 | - checkout
59 | - run: java -version
60 | - sbt_cmd:
61 | scala_version: << parameters.scala_version >>
62 | sbt_tasks: xml/update xml/compile xml/Test/compile xml/test xml/doc xml/package xml/osgiBundle
63 | scalajs_job:
64 | executor: scala_jdk8_executor
65 | parameters:
66 | scala_version:
67 | description: "Scala version"
68 | default: 2.12.18
69 | type: string
70 | steps:
71 | - checkout
72 | - run: java -version
73 | - run: node -v
74 | - sbt_cmd:
75 | scala_version: << parameters.scala_version >>
76 | sbt_tasks: xmlJS/update xmlJS/compile xmlJS/Test/compile xmlJS/test xmlJS/doc xmlJS/package
77 | scalanative_job:
78 | executor: scala_jdk8_executor
79 | parameters:
80 | scala_version:
81 | description: "Scala version"
82 | default: 2.12.18
83 | type: string
84 | steps:
85 | - checkout
86 | - run:
87 | name: Install dependencies
88 | command: |
89 | sudo apt-get update
90 | sudo apt-get install -y clang
91 | - sbt_cmd:
92 | scala_version: << parameters.scala_version >>
93 | sbt_tasks: xmlNative/update xmlNative/compile xmlNative/test:compile xmlNative/test xmlNative/doc xmlNative/package
94 |
95 | workflows:
96 | build:
97 | jobs:
98 | - scala_job:
99 | name: 2.12.x
100 | java_version: jdk8
101 | scala_version: 2.12.21
102 | - scala_job:
103 | name: 2.13.x
104 | java_version: jdk8
105 | scala_version: 2.13.18
106 | - scala_job:
107 | name: 3.x
108 | java_version: jdk8
109 | scala_version: 3.3.7
110 | - scala_job:
111 | name: jdk11_2.12.x
112 | java_version: jdk11
113 | scala_version: 2.12.21
114 | - scala_job:
115 | name: jdk11_2.13.x
116 | java_version: jdk11
117 | scala_version: 2.13.18
118 | - scala_job:
119 | name: jdk11_3.x
120 | java_version: jdk11
121 | scala_version: 3.3.7
122 | - scala_job:
123 | name: jdk17_2.12.x
124 | java_version: jdk17
125 | scala_version: 2.12.21
126 | - scala_job:
127 | name: jdk17_2.13.x
128 | java_version: jdk17
129 | scala_version: 2.13.18
130 | - scala_job:
131 | name: jdk17_3.x
132 | java_version: jdk17
133 | scala_version: 3.3.7
134 | - scala_job:
135 | name: jdk21_2.12.x
136 | java_version: jdk21
137 | scala_version: 2.12.21
138 | - scala_job:
139 | name: jdk21_2.13.x
140 | java_version: jdk21
141 | scala_version: 2.13.18
142 | - scala_job:
143 | name: jdk21_3.x
144 | java_version: jdk21
145 | scala_version: 3.3.7
146 | - scalajs_job:
147 | name: sjs1.0_2.12.x
148 | scala_version: 2.12.21
149 | - scalajs_job:
150 | name: sjs1.0_2.13.x
151 | scala_version: 2.13.18
152 | - scalajs_job:
153 | name: sjs1.0_3.x
154 | scala_version: 3.3.7
155 | - scalanative_job:
156 | name: native0.4_2.12.x
157 | scala_version: 2.12.21
158 | - scalanative_job:
159 | name: native0.4_2.13.x
160 | scala_version: 2.13.18
161 | - scalanative_job:
162 | name: native0.4_3.x
163 | scala_version: 3.3.7
164 |
--------------------------------------------------------------------------------
/jvm/src/test/scala-2.x/scala/xml/XMLTestJVM2x.scala:
--------------------------------------------------------------------------------
1 | package scala.xml
2 |
3 | import org.junit.{Test => UnitTest}
4 | import org.junit.Assert.assertEquals
5 | import scala.xml.parsing.ConstructingParser
6 |
7 | class XMLTestJVM2x {
8 | @UnitTest
9 | def t2354(): Unit = {
10 | val xml_good: String = ""
11 | val xml_bad: String = ""
12 |
13 | val parser1: ConstructingParser = ConstructingParser.fromSource(io.Source.fromString(xml_good), preserveWS = false)
14 | val parser2: ConstructingParser = ConstructingParser.fromSource(io.Source.fromString(xml_bad), preserveWS = false)
15 |
16 | parser1.document()
17 | parser2.document()
18 | }
19 |
20 | @UnitTest
21 | def t8253(): Unit = {
22 | // `identity(foo)` used to match the overly permissive match in SymbolXMLBuilder
23 | // which was intended to more specifically match `_root_.scala.xml.Text(...)`
24 |
25 | import reflect.runtime.universe._ // not using the XML library in compiler tests
26 |
27 | val ns1: String = "ns1"
28 | assertEquals(reify(ns1).tree.toString, q"ns1".toString)
29 | assertEquals("",
30 | """|{
31 | | var $tmpscope: _root_.scala.xml.NamespaceBinding = $scope;
32 | | $tmpscope = new _root_.scala.xml.NamespaceBinding(null, "ns1", $tmpscope);
33 | | {
34 | | val $scope: _root_.scala.xml.NamespaceBinding = $tmpscope;
35 | | new _root_.scala.xml.Elem(null, "sample", _root_.scala.xml.Null, $scope, true)
36 | | }
37 | |}""".stripMargin,
38 | q"".toString)
39 | assertEquals("",
40 | """|{
41 | | var $tmpscope: _root_.scala.xml.NamespaceBinding = $scope;
42 | | $tmpscope = new _root_.scala.xml.NamespaceBinding(null, ns1, $tmpscope);
43 | | {
44 | | val $scope: _root_.scala.xml.NamespaceBinding = $tmpscope;
45 | | new _root_.scala.xml.Elem(null, "sample", _root_.scala.xml.Null, $scope, true)
46 | | }
47 | |}""".stripMargin,
48 | q"".toString)
49 | assertEquals("",
50 | """|{
51 | | var $tmpscope: _root_.scala.xml.NamespaceBinding = $scope;
52 | | $tmpscope = new _root_.scala.xml.NamespaceBinding("foo", "ns1", $tmpscope);
53 | | {
54 | | val $scope: _root_.scala.xml.NamespaceBinding = $tmpscope;
55 | | new _root_.scala.xml.Elem(null, "sample", _root_.scala.xml.Null, $scope, true)
56 | | }
57 | |}""".stripMargin,
58 | q"".toString)
59 | assertEquals("",
60 | """|{
61 | | var $tmpscope: _root_.scala.xml.NamespaceBinding = $scope;
62 | | $tmpscope = new _root_.scala.xml.NamespaceBinding("foo", ns1, $tmpscope);
63 | | {
64 | | val $scope: _root_.scala.xml.NamespaceBinding = $tmpscope;
65 | | new _root_.scala.xml.Elem(null, "sample", _root_.scala.xml.Null, $scope, true)
66 | | }
67 | |}""".stripMargin,
68 | q"".toString)
69 | }
70 |
71 | @UnitTest
72 | def t8466lift(): Unit = {
73 | import scala.reflect.runtime.universe._
74 |
75 | implicit val liftXmlComment: Liftable[Comment] = Liftable[Comment] { comment =>
76 | q"new _root_.scala.xml.Comment(${comment.commentText})"
77 | }
78 | liftXmlComment(Comment("foo"))
79 | assertEquals(q"${Comment("foo")}".toString, q"".toString)
80 | }
81 |
82 | @UnitTest
83 | def t8466unlift(): Unit = {
84 | import scala.reflect.runtime.universe._
85 |
86 | implicit val unliftXmlComment: Unliftable[Comment] = Unliftable[Comment] {
87 | case q"new _root_.scala.xml.Comment(${value: String})" => Comment(value)
88 | }
89 | unliftXmlComment.unapply(q"")
90 | val q"${comment: Comment}" = q""
91 | assertEquals(comment.commentText, "foo")
92 | }
93 |
94 | @UnitTest
95 | def t9027(): Unit = {
96 | // used to be parsed as .println
97 |
98 | import reflect.runtime._, universe._
99 |
100 | assertEquals(
101 | """|{
102 | | {
103 | | val $buf = new _root_.scala.xml.NodeBuffer();
104 | | $buf.$amp$plus(new _root_.scala.xml.Elem(null, "a", _root_.scala.xml.Null, $scope, true));
105 | | $buf.$amp$plus(new _root_.scala.xml.Elem(null, "b", _root_.scala.xml.Null, $scope, true));
106 | | $buf
107 | | };
108 | | println("hello, world.")
109 | |}""".stripMargin,
110 | q"""
111 | println("hello, world.")""".toString)
112 | assertEquals(
113 | """|{
114 | | {
115 | | val $buf = new _root_.scala.xml.NodeBuffer();
116 | | $buf.$amp$plus(new _root_.scala.xml.Elem(null, "a", _root_.scala.xml.Null, $scope, true));
117 | | $buf.$amp$plus(new _root_.scala.xml.Elem(null, "b", _root_.scala.xml.Null, $scope, true));
118 | | $buf.$amp$plus(new _root_.scala.xml.Elem(null, "c", _root_.scala.xml.Null, $scope, true));
119 | | $buf
120 | | };
121 | | println("hello, world.")
122 | |}""".stripMargin,
123 | q"""
124 |
125 |
126 | println("hello, world.")""".toString)
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/shared/src/main/scala/scala/xml/factory/XMLLoader.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Scala (https://www.scala-lang.org)
3 | *
4 | * Copyright EPFL and Lightbend, Inc.
5 | *
6 | * Licensed under Apache License 2.0
7 | * (http://www.apache.org/licenses/LICENSE-2.0).
8 | *
9 | * See the NOTICE file distributed with this work for
10 | * additional information regarding copyright ownership.
11 | */
12 |
13 | package scala
14 | package xml
15 | package factory
16 |
17 | import org.xml.sax.XMLReader
18 | import scala.xml.Source
19 | import javax.xml.parsers.SAXParserFactory
20 | import java.io.{File, FileDescriptor, InputStream, Reader}
21 | import java.net.URL
22 |
23 | /**
24 | * Presents collection of XML loading methods which use the parser
25 | * created by "def parser" or the reader created by "def reader".
26 | */
27 | trait XMLLoader[T <: Node] {
28 | private def setSafeDefaults(parserFactory: SAXParserFactory): Unit = {
29 | parserFactory.setFeature("http://javax.xml.XMLConstants/feature/secure-processing", true)
30 | parserFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false)
31 | parserFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true)
32 | parserFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false)
33 | parserFactory.setFeature("http://xml.org/sax/features/external-general-entities", false)
34 | parserFactory.setFeature("http://xml.org/sax/features/resolve-dtd-uris", false)
35 | parserFactory.setXIncludeAware(false)
36 | parserFactory.setNamespaceAware(false)
37 | }
38 |
39 | private lazy val parserInstance: ThreadLocal[SAXParser] = new ThreadLocal[SAXParser] {
40 | override def initialValue: SAXParser = {
41 | val parserFactory: SAXParserFactory = SAXParserFactory.newInstance
42 | setSafeDefaults(parserFactory)
43 | parserFactory.newSAXParser
44 | }
45 | }
46 |
47 | /* Override this to use a different SAXParser. */
48 | def parser: SAXParser = {
49 | val p = parserInstance.get
50 | try { p.reset() } catch { case _: UnsupportedOperationException => }
51 | p
52 | }
53 |
54 | /* Override this to use a different XMLReader. */
55 | def reader: XMLReader = parser.getXMLReader
56 |
57 | /**
58 | * Loads XML from the given InputSource, using the supplied parser or reader.
59 | * The methods available in scala.xml.XML use the XML parser in the JDK
60 | * (unless another parser is present on the classpath).
61 | */
62 |
63 | // TODO remove
64 | def loadXML(inputSource: InputSource, parser: SAXParser): T = getDocElem(adapter.loadDocument(inputSource, parser.getXMLReader))
65 | def loadXMLNodes(inputSource: InputSource, parser: SAXParser): Seq[Node] = adapter.loadDocument(inputSource, parser.getXMLReader).children.toSeq
66 | def adapter: parsing.FactoryAdapter = new parsing.NoBindingFactoryAdapter()
67 |
68 | /** Loads XML Document. */
69 | def loadDocument(inputSource: InputSource): Document = adapter.loadDocument(inputSource, reader)
70 | def loadFileDocument(fileName: String): Document = loadDocument(Source.fromFile(fileName))
71 | def loadFileDocument(file: File): Document = loadDocument(Source.fromFile(file))
72 | def loadDocument(url: URL): Document = loadDocument(Source.fromUrl(url))
73 | def loadDocument(sysId: String): Document = loadDocument(Source.fromSysId(sysId))
74 | def loadFileDocument(fileDescriptor: FileDescriptor): Document = loadDocument(Source.fromFile(fileDescriptor))
75 | def loadDocument(inputStream: InputStream): Document = loadDocument(Source.fromInputStream(inputStream))
76 | def loadDocument(reader: Reader): Document = loadDocument(Source.fromReader(reader))
77 | def loadStringDocument(string: String): Document = loadDocument(Source.fromString(string))
78 |
79 | /** Loads XML element. */
80 | private def getDocElem(document: Document): T = document.docElem.asInstanceOf[T]
81 | def load(inputSource: InputSource): T = getDocElem(loadDocument(inputSource))
82 | def loadFile(fileName: String): T = getDocElem(loadFileDocument(fileName))
83 | def loadFile(file: File): T = getDocElem(loadFileDocument(file))
84 | def load(url: URL): T = getDocElem(loadDocument(url))
85 | def load(sysId: String): T = getDocElem(loadDocument(sysId))
86 | def loadFile(fileDescriptor: FileDescriptor): T = getDocElem(loadFileDocument(fileDescriptor))
87 | def load(inputStream: InputStream): T = getDocElem(loadDocument(inputStream))
88 | def load(reader: Reader): T = getDocElem(loadDocument(reader))
89 | def loadString(string: String): T = getDocElem(loadStringDocument(string))
90 |
91 | /** Load XML nodes, including comments and processing instructions that precede and follow the root element. */
92 | def loadNodes(inputSource: InputSource): Seq[Node] = loadDocument(inputSource).children.toSeq
93 | def loadFileNodes(fileName: String): Seq[Node] = loadFileDocument(fileName).children.toSeq
94 | def loadFileNodes(file: File): Seq[Node] = loadFileDocument(file).children.toSeq
95 | def loadNodes(url: URL): Seq[Node] = loadDocument(url).children.toSeq
96 | def loadNodes(sysId: String): Seq[Node] = loadDocument(sysId).children.toSeq
97 | def loadFileNodes(fileDescriptor: FileDescriptor): Seq[Node] = loadFileDocument(fileDescriptor).children.toSeq
98 | def loadNodes(inputStream: InputStream): Seq[Node] = loadDocument(inputStream).children.toSeq
99 | def loadNodes(reader: Reader): Seq[Node] = loadDocument(reader).children.toSeq
100 | def loadStringNodes(string: String): Seq[Node] = loadStringDocument(string).children.toSeq
101 | }
102 |
--------------------------------------------------------------------------------