diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 000000000..8f3ac6805 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,163 @@ +version: 2.1 + +executors: + scala_jdk8_executor: + docker: + - image: cimg/openjdk:8.0-node + resource_class: small + scala_jdk11_executor: + docker: + - image: cimg/openjdk:11.0-node + resource_class: small + scala_jdk17_executor: + docker: + - image: cimg/openjdk:17.0-node + resource_class: small + scala_jdk21_executor: + docker: + - image: cimg/openjdk:21.0-node + resource_class: small + +commands: + sbt_cmd: + description: "Build with sbt" + parameters: + scala_version: + type: string + default: 2.12.18 + sbt_tasks: + type: string + default: update compile test:compile test doc package osgiBundle + steps: + - restore_cache: + keys: + - sbt-deps-v1-{{ checksum "build.sbt" }} + - sbt-deps-v1- + - run: sbt -Dsbt.io.jdktimestamps=true ++<< parameters.scala_version >> << parameters.sbt_tasks >> + - save_cache: + key: sbt-deps-v1-{{ checksum "build.sbt" }} + paths: + - "~/.cache/coursier" + - "~/.ivy2/cache" + - "~/.sbt" + - "~/.m2" + +jobs: + scala_job: + executor: scala_<>_executor + parameters: + scala_version: + description: "Scala version" + default: 2.12.18 + type: string + java_version: + description: "Java version" + default: jdk8 + type: string + steps: + - checkout + - run: java -version + - sbt_cmd: + scala_version: << parameters.scala_version >> + sbt_tasks: xml/update xml/compile xml/Test/compile xml/test xml/doc xml/package xml/osgiBundle + scalajs_job: + executor: scala_jdk8_executor + parameters: + scala_version: + description: "Scala version" + default: 2.12.18 + type: string + steps: + - checkout + - run: java -version + - run: node -v + - sbt_cmd: + scala_version: << parameters.scala_version >> + sbt_tasks: xmlJS/update xmlJS/compile xmlJS/Test/compile xmlJS/test xmlJS/doc xmlJS/package + scalanative_job: + executor: scala_jdk8_executor + parameters: + scala_version: + description: "Scala version" + default: 2.12.18 + type: string + steps: + - checkout + - run: + name: Install dependencies + command: | + sudo apt-get update + sudo apt-get install -y clang + - sbt_cmd: + scala_version: << parameters.scala_version >> + sbt_tasks: xmlNative/update xmlNative/compile xmlNative/test:compile xmlNative/test xmlNative/doc xmlNative/package + +workflows: + build: + jobs: + - scala_job: + name: 2.12.x + java_version: jdk8 + scala_version: 2.12.21 + - scala_job: + name: 2.13.x + java_version: jdk8 + scala_version: 2.13.18 + - scala_job: + name: 3.x + java_version: jdk8 + scala_version: 3.3.7 + - scala_job: + name: jdk11_2.12.x + java_version: jdk11 + scala_version: 2.12.21 + - scala_job: + name: jdk11_2.13.x + java_version: jdk11 + scala_version: 2.13.18 + - scala_job: + name: jdk11_3.x + java_version: jdk11 + scala_version: 3.3.7 + - scala_job: + name: jdk17_2.12.x + java_version: jdk17 + scala_version: 2.12.21 + - scala_job: + name: jdk17_2.13.x + java_version: jdk17 + scala_version: 2.13.18 + - scala_job: + name: jdk17_3.x + java_version: jdk17 + scala_version: 3.3.7 + - scala_job: + name: jdk21_2.12.x + java_version: jdk21 + scala_version: 2.12.21 + - scala_job: + name: jdk21_2.13.x + java_version: jdk21 + scala_version: 2.13.18 + - scala_job: + name: jdk21_3.x + java_version: jdk21 + scala_version: 3.3.7 + - scalajs_job: + name: sjs1.0_2.12.x + scala_version: 2.12.21 + - scalajs_job: + name: sjs1.0_2.13.x + scala_version: 2.13.18 + - scalajs_job: + name: sjs1.0_3.x + scala_version: 3.3.7 + - scalanative_job: + name: native0.4_2.12.x + scala_version: 2.12.21 + - scalanative_job: + name: native0.4_2.13.x + scala_version: 2.13.18 + - scalanative_job: + name: native0.4_3.x + scala_version: 3.3.7 diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..5ace4600a --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..727f99175 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,26 @@ +name: test +on: + push: + branches: + - main + pull_request: +jobs: + test: + strategy: + fail-fast: false + matrix: + java: [8, 11, 17, 21] + scala: [2.12.x, 2.13.x, 3.x] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + - uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: ${{matrix.java}} + cache: sbt + - uses: sbt/setup-sbt@v1 + - name: Test + run: sbt ++${{matrix.scala}} test headerCheck versionPolicyCheck package diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml new file mode 100644 index 000000000..3549dedc2 --- /dev/null +++ b/.github/workflows/cla.yml @@ -0,0 +1,11 @@ +name: "Check Scala CLA" +on: + pull_request: +jobs: + cla-check: + runs-on: ubuntu-latest + steps: + - name: Verify CLA + uses: scala/cla-checker@v1 + with: + author: ${{ github.event.pull_request.user.login }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..cdef358d8 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,22 @@ +name: Release +on: + push: + tags: ["*"] +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + - uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: 8 + - uses: sbt/setup-sbt@v1 + - run: sbt versionCheck ci-release + env: + PGP_PASSPHRASE: ${{secrets.PGP_PASSPHRASE}} + PGP_SECRET: ${{secrets.PGP_SECRET}} + SONATYPE_PASSWORD: ${{secrets.SONATYPE_PASSWORD}} + SONATYPE_USERNAME: ${{secrets.SONATYPE_USERNAME}} diff --git a/.gitignore b/.gitignore index f236bdfa0..5a0cc72f4 100644 --- a/.gitignore +++ b/.gitignore @@ -12,8 +12,6 @@ *.jar *~ -build.properties - # target directories for ant build /build/ /dists/ diff --git a/test/files/jvm/xml02.check b/.jvmopts similarity index 100% rename from test/files/jvm/xml02.check rename to .jvmopts diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..4cb529118 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,98 @@ +# Scala XML Changes + +## 2.0.1 (2021-07-21) + +Binary compatible with Scala XML 2.0.0. + +Published for Scala 2.12 and 2.13, Scala 3, Scala.js 1.6, and Scala +Native 0.4. + +### Added + +- No new functionality. + +### Fixed + +- Fixed runtime error for `MarkupParser` on Scala 3 by changing the + access modifier of internal class, `WithLookAhead` (#542) + +## 2.0.0 (2021-05-13) + +Not binary compatible with Scala XML 1.3.0. + +Published for Scala 2.12, 2.13, and 3.0, Scala.js 1.5, +and Scala Native 0.4. + +Artifacts are no longer published for Scala 2.11 and Scala.js 0.6. + +A number of deprecated elements have been removed from the library; +see the "[Removed](#Removed)" section below. The library's JAR byte +size is about 15% smaller. + +### Added + +- Add `scala.xml.transform.NestingTransformer`, to apply a single rule + recursively, to give the original behavior of `RuleTransformer`, see + below. +- The `apiURL` is now published in ivy metadata so that hyperlinks + exist in downstream projects that reference Scala XML in their + Scaladocs. +- Declare version policy of with early-semver in Mima with + sbt-version-policy plugin + +### Changed + +- Changes to the default parser settings for the JDK SAXParser, see + [Safe parser defaults](https://github.com/scala/scala-xml/wiki/Safer-parser-defaults) + page on the wiki. +- The parser used by the load methods from `scala.xml.XML` and from + `scala.xml.factory.XMLLoader` is now a `ThreadLocal` instance of + SAXParser to reuse the parser instance and avoid repeatedly + allocating one on every file load. +- Improve `scala.xml.transform.RuleTransformer` to apply all rules recursively. +- Reject invalid comment text that ends in a dash (-) in `scala.xml.Comment`. +- Changed use of `scala.collection.mutable.Stack` in `FactoryAdapter` to a + `scala.collection.immutable.List`. These members were affected. + - `attribStack` + - `hStack` + - `tagStack` + - `scopeStack` +- The abstract class `FactoryAdapter`, see above, is used elsewhere + within the library, as well, so the previous changes are also + inherited by: + - `scala.xml.parsing.NoBindingFactoryAdapter` implemented class + - `scala.xml.factory.XMLLoader.adapter` static member + +### Fixed + +- Attribute order is preserved for XML elements, not reversed. +- Don't escape quotes in `scala.xml.PCData` and `CDATA` as an XML `"` + +### Removed + +Most of these deletions are of vestigial code that is either unused, +of poor quality or both. Very few users of Scala XML will even notice +the removed parts. Most users will not be affected. + +The deletions represent about 1500 lines of code (sloc). By +comparison Scala XML is 10,000 sloc, so this is about 15% reduction in +sloc. The code that supports XML literals is maintained upstream in +the Scala compiler, not in the Scala XML library. + +- Remove deprecated `scala.xml.pull.XMLEventReader` +- Remove deprecated versions of `scala.xml.Elem` constructors +- Remove deprecated `scala.xml.Elem.xmlToProcess` and + `scala.xml.Elem.processXml` +- Remove deprecated definitions under `scala.xml.persistent` + - `CachedFileStorage` + - `Index` + - `SetStorage` +- Remove `scala.xml.dtd.impl.PointedHedgeExp` +- Remove `scala.xml.dtd.Scanner` +- Remove `scala.xml.dtd.ContentModelParser` +- Remove `scala.xml.dtd.ElementValidator` +- Remove `scala.xml.factory.Binder` +- Remove `scala.xml.parsing.ValidatingMarkupHandler` +- Remove `scala.xml.Properties` +- Remove `scala.xml.factory.LoggedNodeFactory` +- Remove `scala.xml.parsing.MarkupHandler.log` diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..0511f2126 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,7 @@ +all repositories in these organizations: + +* [scala](https://github.com/scala) +* [scalacenter](https://github.com/scalacenter) +* [lampepfl](https://github.com/lampepfl) + +are covered by the Scala Code of Conduct: https://scala-lang.org/conduct/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..6c3b9de32 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,10 @@ +Contributing +============ + +Please see the wiki for the scala-xml contributor guide: + +https://github.com/scala/scala-xml/wiki/Contributor-guide + +Thank you, + +The Scala XML maintainers diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/LICENSE.md b/LICENSE.md deleted file mode 100644 index c9f2ca8a7..000000000 --- a/LICENSE.md +++ /dev/null @@ -1,28 +0,0 @@ -Copyright (c) 2002-2013 EPFL -Copyright (c) 2011-2013 Typesafe, Inc. - -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of the EPFL nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/NOTICE b/NOTICE new file mode 100644 index 000000000..147f12c8c --- /dev/null +++ b/NOTICE @@ -0,0 +1,14 @@ +scala-xml +Copyright (c) 2002-2025 EPFL +Copyright (c) 2011-2025 Lightbend, Inc. dba Akka + +scala-xml includes software developed at +LAMP/EPFL (https://lamp.epfl.ch/) and +Akka (https://akka.io/). + +Licensed under the Apache License, Version 2.0 (the "License"). +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/README.md b/README.md index c9debc1b2..5e743beaa 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,51 @@ scala-xml +[![latest release for 2.12](https://img.shields.io/maven-central/v/org.scala-lang.modules/scala-xml_2.12.svg?label=scala+2.12)](http://mvnrepository.com/artifact/org.scala-lang.modules/scala-xml_2.12) +[![latest release for 2.13](https://img.shields.io/maven-central/v/org.scala-lang.modules/scala-xml_2.13.svg?label=scala+2.13)](http://mvnrepository.com/artifact/org.scala-lang.modules/scala-xml_2.13) +[![latest release for 3.0](https://img.shields.io/maven-central/v/org.scala-lang.modules/scala-xml_3.svg?label=scala+3)](http://mvnrepository.com/artifact/org.scala-lang.modules/scala-xml_3) ========= -The standard Scala XML library. +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. -As of Scala 2.11, this library is a separate jar that can be omitted from Scala projects that do not use XML. -We're also looking forward to alternative implementations! +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. +Alternative implementations of these calls are welcome! +Compiler code that shows the calls needed: + [Scala 2.11](https://github.com/scala/scala/blob/2.11.x/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala), + [Scala 2.12](https://github.com/scala/scala/blob/2.12.x/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala), + [Scala 2.13](https://github.com/scala/scala/blob/2.13.x/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala), + [Scala 3](https://github.com/scala/scala3/blob/main/compiler/src/dotty/tools/dotc/parsing/xml/SymbolicXMLBuilder.scala). -## Adding an SBT dependency -To depend on scala-xml in SBT, add something like this to your build.sbt: +API documentation is available [here](https://javadoc.io/doc/org.scala-lang.modules/scala-xml_2.13/). -``` -libraryDependencies += "org.scala-lang.modules" %% "scala-xml" % "1.0-RC2" -``` +How-to documentation is available in the [wiki](https://github.com/scala/scala-xml/wiki) -(Assuming you're using a `scalaVersion` for which a scala-xml is published. -The first 2.11 milestone for which this is true is 2.11.0-M4.) +## Maintenance status -To support multiple Scala versions: +This library is community-maintained. Maintainers with merge rights include [@aaron_s_hawley](https://github.com/ashawley) and [@dubinsky](https://github.com/dubinsky). - - SBT 0.12: -``` -libraryDependencies <++= (scalaVersion) { sVer => - if (sVer startsWith "2.11") Seq("org.scala-lang.modules" %% "scala-xml" % "1.0-RC2") - else Seq.empty -} -``` +Contributors are welcome. Please consult the [contributor guide](https://github.com/scala/scala-xml/wiki/Contributor-guide) on the wiki. - - SBT 0.13: -``` -libraryDependencies ++= ( - if (scalaVersion.value startsWith "2.11") Seq("org.scala-lang.modules" %% "scala-xml" % "1.0-RC2") - else Seq.empty -) -``` +## Issues + +Some old issues from the Scala issue tracker have been migrated +here, but not all of them. Community assistance identifying and +migrating still-relevant issues is welcome. See [this +page](https://github.com/scala/scala-xml/issues/62) for details. + +## Related projects + +- [Advxml](https://github.com/geirolz/advxml) - Functional library combining scala-xml with cats-core +- [Binding.scala](https://github.com/ThoughtWorksInc/Binding.scala) - Reactive programming library +- [ezXML](https://github.com/JulienSt/ezXML) - Extensions for traverse, encoding, decoding and mapping XML +- [http4s-scala-xml](https://http4s.github.io/http4s-scala-xml/) - XML literal support in http4s +- [Json4s XML](https://github.com/json4s/json4s) - Conversion to and from JSON +- [monadic-html](https://github.com/OlivierBlanvillain/monadic-html) - DOM-like event-based programming with XHTML +- [phobos](https://github.com/TinkoffCreditSystems/phobos) - Data-binding library based on stream parsing using Aalto XML +- [scalacheck-xml](https://github.com/typelevel/scalacheck-xml) - Provides Scalacheck instances for scala-xml +- [scalaxb](http://scalaxb.org/) - XML data binding, serialization, SOAP and WSDL support +- [ScalaTags](https://github.com/lihaoyi/scalatags) - Alternative syntax for XML literals +- [scala-xml-dotty](https://github.com/felixmulder/scala-xml-dotty) - Macro library for XML literals in Dotty +- [XML SPaC](https://github.com/dylemma/xml-spac) - Streaming event-based parser combinators +- [xs4s](https://github.com/ScalaWilliam/xs4s) - XML streaming for Scala +- [xtract](https://github.com/lucidsoftware/xtract) - A library for deserializing XML + +You might also [search "XML" on Scaladex](https://index.scala-lang.org/search?q=xml). diff --git a/build.sbt b/build.sbt index b4a9629eb..00b8c209a 100644 --- a/build.sbt +++ b/build.sbt @@ -1,112 +1,210 @@ -organization := "org.scala-lang.modules" - -name := "scala-xml" - -version := "1.0-RC4" - -// standard stuff follows: -scalaVersion := "2.11.0-M5" - -// NOTE: not necessarily equal to scalaVersion -// (e.g., during PR validation, we override scalaVersion to validate, -// but don't rebuild scalacheck, so we don't want to rewire that dependency) -scalaBinaryVersion := "2.11.0-M5" - - -// don't use for doc scope, scaladoc warnings are not to be reckoned with -scalacOptions in compile ++= Seq("-optimize", "-Xfatal-warnings", "-feature", "-deprecation", "-unchecked", "-Xlint") - - -// Generate $name.properties to store our version as well as the scala version used to build -resourceGenerators in Compile <+= Def.task { - val props = new java.util.Properties - props.put("version.number", version.value) - props.put("scala.version.number", scalaVersion.value) - props.put("scala.binary.version.number", scalaBinaryVersion.value) - val file = (resourceManaged in Compile).value / s"${name.value}.properties" - IO.write(props, null, file) - Seq(file) -} - -mappings in (Compile, packageBin) += { - (baseDirectory.value / s"${name.value}.properties") -> s"${name.value}.properties" -} - - -// maven publishing -publishTo := { - val nexus = "https://oss.sonatype.org/" - if (version.value.trim.endsWith("SNAPSHOT")) - Some("snapshots" at nexus + "content/repositories/snapshots") - else - Some("releases" at nexus + "service/local/staging/deploy/maven2") -} - -publishMavenStyle := true - -publishArtifact in Test := false - -pomIncludeRepository := { _ => false } - -pomExtra := ( - http://www.scala-lang.org/ - 2002 - - - repo - BSD 3-Clause - https://github.com/scala/{name.value}/blob/master/LICENSE.md - - - - scm:git:git://github.com/scala/{name.value}.git - https://github.com/scala/{name.value} - - - JIRA - https://issues.scala-lang.org/ - - - - epfl - EPFL - - - Typesafe - Typesafe, Inc. - - +import com.typesafe.tools.mima.core._ +import sbtcrossproject.CrossPlugin.autoImport.{CrossType, crossProject} + +publish / skip := true // root project + +ThisBuild / startYear := Some(2002) +ThisBuild / licenses += (("Apache-2.0", url("https://www.apache.org/licenses/LICENSE-2.0"))) + +// because it doesn't declare it itself +ThisBuild / libraryDependencySchemes += "org.scala-js" %% "scalajs-library" % "semver-spec" +ThisBuild / apiURL := Some(url("https://javadoc.io/doc/org.scala-lang.modules/scala-xml_2.13/")) + +lazy val configSettings: Seq[Setting[?]] = Seq( + unmanagedSourceDirectories ++= { + unmanagedSourceDirectories.value.flatMap { dir => + def forVersion(version: String): File = file(dir.getPath ++ "-" ++ version) + CrossVersion.partialVersion(scalaVersion.value) match { + case Some((3, _)) => Seq(forVersion("3"), forVersion("2.13+")) + case Some((2, minor)) => + Seq(forVersion("2")) ++ (minor match { + case 13 => Seq(forVersion("2.13"), forVersion("2.13+")) + case 12 => Seq(forVersion("2.12")) + case _ => Seq() + }) + case _ => Seq() + } + } + } ) -// for testing with partest -libraryDependencies += "org.scala-lang.modules" %% "scala-partest-interface" % "0.2" % "test" - -// the actual partest the interface calls into -- must be binary version close enough to ours -// so that it can link to the compiler/lib we're using (testing) -libraryDependencies += "org.scala-lang.modules" %% "scala-partest" % "1.0-RC5" % "test" - -// necessary for partest -- see comments in its build.sbt -conflictWarning ~= { _.copy(failOnConflict = false) } - -fork in Test := true - -javaOptions in Test += "-Xmx1G" - -testFrameworks += new TestFramework("scala.tools.partest.Framework") - -definedTests in Test += ( - new sbt.TestDefinition( - "partest", - // marker fingerprint since there are no test classes - // to be discovered by sbt: - new sbt.testing.AnnotatedFingerprint { - def isModule = true - def annotationName = "partest" - }, true, Array()) +lazy val xml = crossProject(JSPlatform, JVMPlatform, NativePlatform) + .withoutSuffixFor(JVMPlatform) + .crossType(CrossType.Full) + .in(file(".")) + .settings(ScalaModulePlugin.scalaModuleSettings) + .jvmSettings(ScalaModulePlugin.scalaModuleOsgiSettings) + .settings( + name := "scala-xml", + scalaModuleAutomaticModuleName := Some("scala.xml"), + crossScalaVersions := Seq("2.13.18", "2.12.21", "3.3.7"), + scalaVersion := "2.12.21", + + scalacOptions ++= (CrossVersion.partialVersion(scalaVersion.value) match { + case Some((3, _)) => + Seq("-language:Scala2") + case _ => + // Compiler team advised avoiding the -Xsource:3 option for releases. + // The output with -Xsource:3 should be periodically checked, though. + Seq("-deprecation:false", "-feature", "-Xlint:-stars-align,-nullary-unit,_") + }), + + Test / scalacOptions += "-Xxml:coalescing", + + headerLicense := Some(HeaderLicense.Custom( + s"""|Scala (https://www.scala-lang.org) + | + |Copyright EPFL and Lightbend, Inc. + | + |Licensed under Apache License 2.0 + |(http://www.apache.org/licenses/LICENSE-2.0). + | + |See the NOTICE file distributed with this work for + |additional information regarding copyright ownership. + |""".stripMargin)), + + versionPolicyIntention := Compatibility.BinaryCompatible, + // Note: See discussion on non-JVM Mima in https://github.com/scala/scala-xml/pull/517 + mimaBinaryIssueFilters ++= { + //import com.typesafe.tools.mima.core.{} + //import com.typesafe.tools.mima.core.ProblemFilters + Seq( // exclusions for all Scala versions + // new method in `Node` with return type `immutable.Seq` + ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.xml.Node.child"), + + // these used to be declared methods, but are now bridges without generic signature + ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.Node.nonEmptyChildren"), + ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.Group.child"), + ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.SpecialNode.child"), + + // new methods with return type immutable.Seq + ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.xml.Attribute.apply"), + ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.xml.Attribute.value"), + ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.xml.MetaData.apply"), + ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.xml.MetaData.value"), + ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.xml.NodeSeq.theSeq"), + + // Synthetic static accessors (for Java interop) have a changed return type + ProblemFilters.exclude[IncompatibleResultTypeProblem]("scala.xml.Null.apply"), + ProblemFilters.exclude[IncompatibleResultTypeProblem]("scala.xml.Null.value"), + ProblemFilters.exclude[IncompatibleResultTypeProblem]("scala.xml.Utility.parseAttributeValue"), + ProblemFilters.exclude[IncompatibleResultTypeProblem]("scala.xml.Utility.trimProper"), + + // used to be a declared method, now a bridge without generic signature + ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.MetaData.apply"), + ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.Null.apply"), + ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.Null.value"), + ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.PrefixedAttribute.apply"), + ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.PrefixedAttribute.value"), + ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.UnprefixedAttribute.apply"), + ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.UnprefixedAttribute.value"), + ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.Document.theSeq"), + ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.Group.theSeq"), + ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.Node.theSeq"), + ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.TextBuffer.toText"), + ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.Utility.trimProper"), + ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.Utility.parseAttributeValue"), + + // Option[c.Seq] => Option[i.Seq] results in a changed generic signature + ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.MetaData.get"), + ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.Null.get"), + ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.Node.attribute"), + + // trait Attribute now extends trait ScalaVersionSpecificMetaData to ensure the previous signatures + // with return type `collection.Seq` remain valid. + // (trait Attribute extends MetaData, but that parent is not present in bytecode because it's a class.) + ProblemFilters.exclude[InheritedNewAbstractMethodProblem]("scala.xml.Attribute.apply"), + ) ++ (CrossVersion.partialVersion(scalaVersion.value) match { + case Some((3, _)) => Seq( // Scala 3-specific exclusions + ) + case Some((2, minor)) => Seq( // Scala 2-specific exclusions + ) ++ (minor match { + case 13 => Seq( // Scala 2.13-specific exclusions + ) + case 12 => Seq( // Scala 2.12-specific exclusions + ) + }) + case _ => Seq() + }) + }, + // Mima signature checking stopped working after 3.0.2 upgrade, see #557 + mimaReportSignatureProblems := (CrossVersion.partialVersion(scalaVersion.value) match { + case Some((3, _)) => false + case _ => true + }), + + apiMappings ++= scalaInstance.value.libraryJars.filter { file => + file.getName.startsWith("scala-library") && file.getName.endsWith(".jar") + }.map { libraryJar => + libraryJar -> + url(s"http://www.scala-lang.org/api/${scalaVersion.value}/") + }.toMap ++ { + // http://stackoverflow.com/questions/16934488 + Option(System.getProperty("sun.boot.class.path")).flatMap { classPath => + classPath.split(java.io.File.pathSeparator).find(_.endsWith(java.io.File.separator + "rt.jar")) + }.map { jarPath => + Map( + file(jarPath) + -> url("http://docs.oracle.com/javase/8/docs/api") + ) + }.getOrElse { + // If everything fails, jam in Java 11 modules. + Map( + file("/modules/java.base") + -> url("https://docs.oracle.com/en/java/javase/11/docs/api/java.base"), + file("/modules/java.xml") + -> url("https://docs.oracle.com/en/java/javase/11/docs/api/java.xml") + ) + } + } + ) + .settings( + inConfig(Compile)(configSettings) ++ inConfig(Test)(configSettings) + ) + .jvmSettings( + OsgiKeys.exportPackage := Seq(s"scala.xml.*;version=${version.value}"), + + libraryDependencies += "junit" % "junit" % "4.13.2" % Test, + libraryDependencies += "com.github.sbt" % "junit-interface" % "0.13.3" % Test, + libraryDependencies += "org.apache.commons" % "commons-lang3" % "3.20.0" % Test, + libraryDependencies += "xerces" % "xercesImpl" % "2.12.2" % Test, + libraryDependencies ++= (CrossVersion.partialVersion(scalaVersion.value) match { + case Some((3, _)) => + Seq() + case _ => + Seq("org.scala-lang" % "scala-compiler" % scalaVersion.value % Test) + }), + ) + .jvmEnablePlugins(SbtOsgi) + .jsSettings( + versionPolicyCheck / skip := true, + versionCheck / skip := true, + // Scala.js cannot run forked tests + Test / fork := false + ) + .jsEnablePlugins(ScalaJSJUnitPlugin) + .nativeSettings( + versionPolicyCheck / skip := true, + versionCheck / skip := true, + // Scala Native cannot run forked tests + Test / fork := false, + libraryDependencies += "org.scala-native" %%% "junit-runtime" % nativeVersion % Test, + Test / scalacOptions += { + val log = streams.value.log + val retrieveDir = baseDirectory.value / "scala-native-junit-plugin-jars" + val lm = dependencyResolution.value + val cp = lm + .retrieve( + "org.scala-native" % s"junit-plugin_${scalaVersion.value}" % nativeVersion, + scalaModuleInfo = None, + retrieveDir, + log + ) + .fold(w => throw w.resolveException, identity) + val jarPath = cp + .find(_.toString.contains("junit-plugin")) + .getOrElse(throw new Exception("Can't find Scala Native junit-plugin jar")) + s"-Xplugin:$jarPath" + }, + Test / testOptions += Tests.Argument(TestFrameworks.JUnit, "-a", "-s", "-v"), ) - - -// TODO: mima -// import com.typesafe.tools.mima.plugin.MimaPlugin.mimaDefaultSettings -// import com.typesafe.tools.mima.plugin.MimaKeys.previousArtifact -// previousArtifact := Some(organization.value %% name.value % binaryReferenceVersion.value) diff --git a/jvm/src/test/resources/scala/xml/archive/books.xml b/jvm/src/test/resources/scala/xml/archive/books.xml new file mode 100644 index 000000000..00a491113 --- /dev/null +++ b/jvm/src/test/resources/scala/xml/archive/books.xml @@ -0,0 +1,3 @@ + + + diff --git a/jvm/src/test/resources/scala/xml/archive/books/book/author.xml b/jvm/src/test/resources/scala/xml/archive/books/book/author.xml new file mode 100644 index 000000000..3f2d2a9cd --- /dev/null +++ b/jvm/src/test/resources/scala/xml/archive/books/book/author.xml @@ -0,0 +1,3 @@ + + + diff --git a/jvm/src/test/resources/scala/xml/archive/books/book/author/volume/1.xml b/jvm/src/test/resources/scala/xml/archive/books/book/author/volume/1.xml new file mode 100644 index 000000000..7577b32c7 --- /dev/null +++ b/jvm/src/test/resources/scala/xml/archive/books/book/author/volume/1.xml @@ -0,0 +1 @@ + diff --git a/jvm/src/test/resources/scala/xml/includee.xml b/jvm/src/test/resources/scala/xml/includee.xml new file mode 100644 index 000000000..151eda262 --- /dev/null +++ b/jvm/src/test/resources/scala/xml/includee.xml @@ -0,0 +1,3 @@ + + Blah! + diff --git a/jvm/src/test/resources/scala/xml/includer.xml b/jvm/src/test/resources/scala/xml/includer.xml new file mode 100644 index 000000000..521954785 --- /dev/null +++ b/jvm/src/test/resources/scala/xml/includer.xml @@ -0,0 +1,3 @@ + + + diff --git a/jvm/src/test/resources/scala/xml/site.xml b/jvm/src/test/resources/scala/xml/site.xml new file mode 100644 index 000000000..9c77a297b --- /dev/null +++ b/jvm/src/test/resources/scala/xml/site.xml @@ -0,0 +1,3 @@ + + + diff --git a/jvm/src/test/resources/scala/xml/utf16.xml b/jvm/src/test/resources/scala/xml/utf16.xml new file mode 100644 index 000000000..52c4fbaa3 Binary files /dev/null and b/jvm/src/test/resources/scala/xml/utf16.xml differ diff --git a/jvm/src/test/resources/scala/xml/utf8.xml b/jvm/src/test/resources/scala/xml/utf8.xml new file mode 100644 index 000000000..bb59f58cb --- /dev/null +++ b/jvm/src/test/resources/scala/xml/utf8.xml @@ -0,0 +1,2 @@ + + diff --git a/jvm/src/test/scala-2.x/scala/xml/CompilerErrors.scala b/jvm/src/test/scala-2.x/scala/xml/CompilerErrors.scala new file mode 100644 index 000000000..9a0df05d5 --- /dev/null +++ b/jvm/src/test/scala-2.x/scala/xml/CompilerErrors.scala @@ -0,0 +1,208 @@ +package scala.xml + +import org.junit.Test + +class CompilerErrors extends CompilerTesting { + @Test + def t7185(): Unit = { + // the error message here differs a bit by Scala version + import util.Properties.versionNumberString + val thing: String = + if (versionNumberString.startsWith("2.12")) "method value" + else "method" + expectXmlError(s"""|overloaded $thing apply with alternatives: + | (f: scala.xml.Node => Boolean)scala.xml.NodeSeq + | (i: Int)scala.xml.Node + | cannot be applied to ()""".stripMargin, + """|object Test { + | () + |}""") + } + + @Test + def t1878_typer(): Unit = + expectXmlError("_* may only come last", + """|object Test extends App { + | // illegal - bug #1764 + | null match { + | case

{ _* }

=> + | } + |}""") + + + @Test + def t1017(): Unit = + expectXmlErrors(1, "not found: value foo", + """|object Test { + | { foo } + |}""") + + @Test + def t1011(): Unit = + expectXmlErrors(69, "not found: value entity", + """|import scala.xml._; + | + |abstract class Test { + | //val entity : String; + | def primitiveHeader : NodeSeq = + | Group({ + |
{Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)} + | {Text(entity)}
+ | } ++ // 3 seconds + | {}++ // 5 seconds + | {}++ // 10 seconds + | {}++ // 20 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + | {}++ // 5 seconds + | {}++ // 10 seconds + | {}++ // 20 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + | {}++ // 5 seconds + | {}++ // 10 seconds + | {}++ // 20 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + | {}++ // 40 seconds + |
); + |}""") +} + +// TODO: factor out somewhere? +class CompilerTesting { + // TODO: refine this; for now, just println -- the goal is to ensure we're testing the right version of scala-xml + // (it should print the same thing as the version in the sbt build) + // we used to always test the scala-xml jar that the compiler depends on, because it was part of the scala-tool ivy config + println(s"Testing scala-xml version ${Properties.versionNumberString}.") + + def errorMessages(errorSnippet: String, compileOptions: String = "")(code: String): List[String] = { + import scala.tools.reflect._ + val m: scala.reflect.runtime.universe.Mirror = scala.reflect.runtime.currentMirror + val tb: ToolBox[scala.reflect.runtime.universe.type] = m.mkToolBox(options = compileOptions) //: ToolBox[m.universe.type] + val fe: FrontEnd = tb.frontEnd + + try { + tb.eval(tb.parse(code)) + Nil + } catch { case _: ToolBoxError => + import fe._ + infos.toList collect { case Info(_, msg, ERROR) => msg } + } + } + + // note: `code` should have a | margin + // the import's needed because toolbox compiler does not accumulate imports like the real one (TODO: verify hypothesis) + def xmlErrorMessages(msg: String, code: String): List[String] = + errorMessages(msg)("import scala.xml.{TopScope => $scope}\n"+ code.stripMargin) // TODO what is this $scope? + + def expectXmlError(msg: String, code: String): Unit = { + val errors: List[String] = xmlErrorMessages(msg, code) + assert(errors.exists(_.contains(msg)), errors.mkString("\n")) + } + + def expectXmlErrors(msgCount: Int, msg: String, code: String): Unit = { + val errors: List[String] = xmlErrorMessages(msg, code) + val errorCount: Int = errors.count(_.contains(msg)) + assert(errorCount == msgCount, s"$errorCount occurrences of '$msg' found -- expected $msgCount in:\n${errors.mkString("\n")}") + } +} diff --git a/jvm/src/test/scala-2.x/scala/xml/XMLTestJVM2x.scala b/jvm/src/test/scala-2.x/scala/xml/XMLTestJVM2x.scala new file mode 100644 index 000000000..3d885717f --- /dev/null +++ b/jvm/src/test/scala-2.x/scala/xml/XMLTestJVM2x.scala @@ -0,0 +1,128 @@ +package scala.xml + +import org.junit.{Test => UnitTest} +import org.junit.Assert.assertEquals +import scala.xml.parsing.ConstructingParser + +class XMLTestJVM2x { + @UnitTest + def t2354(): Unit = { + val xml_good: String = "<![CDATA[Hello [tag]]]>" + val xml_bad: String = "<![CDATA[Hello [tag] ]]>" + + val parser1: ConstructingParser = ConstructingParser.fromSource(io.Source.fromString(xml_good), preserveWS = false) + val parser2: ConstructingParser = ConstructingParser.fromSource(io.Source.fromString(xml_bad), preserveWS = false) + + parser1.document() + parser2.document() + } + + @UnitTest + def t8253(): Unit = { + // `identity(foo)` used to match the overly permissive match in SymbolXMLBuilder + // which was intended to more specifically match `_root_.scala.xml.Text(...)` + + import reflect.runtime.universe._ // not using the XML library in compiler tests + + val ns1: String = "ns1" + assertEquals(reify(ns1).tree.toString, q"ns1".toString) + assertEquals("", + """|{ + | var $tmpscope: _root_.scala.xml.NamespaceBinding = $scope; + | $tmpscope = new _root_.scala.xml.NamespaceBinding(null, "ns1", $tmpscope); + | { + | val $scope: _root_.scala.xml.NamespaceBinding = $tmpscope; + | new _root_.scala.xml.Elem(null, "sample", _root_.scala.xml.Null, $scope, true) + | } + |}""".stripMargin, + q"".toString) + assertEquals("", + """|{ + | var $tmpscope: _root_.scala.xml.NamespaceBinding = $scope; + | $tmpscope = new _root_.scala.xml.NamespaceBinding(null, ns1, $tmpscope); + | { + | val $scope: _root_.scala.xml.NamespaceBinding = $tmpscope; + | new _root_.scala.xml.Elem(null, "sample", _root_.scala.xml.Null, $scope, true) + | } + |}""".stripMargin, + q"".toString) + assertEquals("", + """|{ + | var $tmpscope: _root_.scala.xml.NamespaceBinding = $scope; + | $tmpscope = new _root_.scala.xml.NamespaceBinding("foo", "ns1", $tmpscope); + | { + | val $scope: _root_.scala.xml.NamespaceBinding = $tmpscope; + | new _root_.scala.xml.Elem(null, "sample", _root_.scala.xml.Null, $scope, true) + | } + |}""".stripMargin, + q"".toString) + assertEquals("", + """|{ + | var $tmpscope: _root_.scala.xml.NamespaceBinding = $scope; + | $tmpscope = new _root_.scala.xml.NamespaceBinding("foo", ns1, $tmpscope); + | { + | val $scope: _root_.scala.xml.NamespaceBinding = $tmpscope; + | new _root_.scala.xml.Elem(null, "sample", _root_.scala.xml.Null, $scope, true) + | } + |}""".stripMargin, + q"".toString) + } + + @UnitTest + def t8466lift(): Unit = { + import scala.reflect.runtime.universe._ + + implicit val liftXmlComment: Liftable[Comment] = Liftable[Comment] { comment => + q"new _root_.scala.xml.Comment(${comment.commentText})" + } + liftXmlComment(Comment("foo")) + assertEquals(q"${Comment("foo")}".toString, q"".toString) + } + + @UnitTest + def t8466unlift(): Unit = { + import scala.reflect.runtime.universe._ + + implicit val unliftXmlComment: Unliftable[Comment] = Unliftable[Comment] { + case q"new _root_.scala.xml.Comment(${value: String})" => Comment(value) + } + unliftXmlComment.unapply(q"") + val q"${comment: Comment}" = q"" + assertEquals(comment.commentText, "foo") + } + + @UnitTest + def t9027(): Unit = { + // used to be parsed as .println + + import reflect.runtime._, universe._ + + assertEquals( + """|{ + | { + | val $buf = new _root_.scala.xml.NodeBuffer(); + | $buf.$amp$plus(new _root_.scala.xml.Elem(null, "a", _root_.scala.xml.Null, $scope, true)); + | $buf.$amp$plus(new _root_.scala.xml.Elem(null, "b", _root_.scala.xml.Null, $scope, true)); + | $buf + | }; + | println("hello, world.") + |}""".stripMargin, + q"""
+ println("hello, world.")""".toString) + assertEquals( + """|{ + | { + | val $buf = new _root_.scala.xml.NodeBuffer(); + | $buf.$amp$plus(new _root_.scala.xml.Elem(null, "a", _root_.scala.xml.Null, $scope, true)); + | $buf.$amp$plus(new _root_.scala.xml.Elem(null, "b", _root_.scala.xml.Null, $scope, true)); + | $buf.$amp$plus(new _root_.scala.xml.Elem(null, "c", _root_.scala.xml.Null, $scope, true)); + | $buf + | }; + | println("hello, world.") + |}""".stripMargin, + q""" + + + println("hello, world.")""".toString) + } +} diff --git a/jvm/src/test/scala/scala/xml/AttributeTest.scala b/jvm/src/test/scala/scala/xml/AttributeTest.scala new file mode 100644 index 000000000..24046ec5c --- /dev/null +++ b/jvm/src/test/scala/scala/xml/AttributeTest.scala @@ -0,0 +1,37 @@ +package scala.xml + +import org.junit.Test +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotEquals + +class AttributeTestJVM { + + @Test + def attributeOrder(): Unit = { + val x: Elem = + assertEquals("""""", x.toString) + } + + @Test + def attributesFromString(): Unit = { + val str: String = """""" + val doc: Elem = XML.loadString(str) + assertEquals(str, doc.toString) + } + + @Test + def attributesAndNamespaceFromString(): Unit = { + val str: String = """""" + val doc: Elem = XML.loadString(str) + assertNotEquals(str, doc.toString) + val str2: String = """""" + val doc2: Elem = XML.loadString(str2) + assertEquals(str2, doc2.toString) + } + + @Test(expected=classOf[SAXParseException]) + def attributesFromStringWithDuplicate(): Unit = { + val str: String = """""" + XML.loadString(str) + } +} diff --git a/jvm/src/test/scala/scala/xml/BillionLaughsAttackTest.scala b/jvm/src/test/scala/scala/xml/BillionLaughsAttackTest.scala new file mode 100644 index 000000000..434ab86a3 --- /dev/null +++ b/jvm/src/test/scala/scala/xml/BillionLaughsAttackTest.scala @@ -0,0 +1,35 @@ +package scala.xml + +import org.junit.Test + +class BillionLaughsAttackTest { + + /** + * org.xml.sax.SAXParseException: JAXP00010001: The parser has + * encountered more than "64000" entity expansions in this document; + * this is the limit imposed by the JDK. + */ + @Test(expected=classOf[org.xml.sax.SAXParseException]) + def lolzTest(): Unit = { + XML.loadString(lolz) + } + + // Copied from https://msdn.microsoft.com/en-us/magazine/ee335713.aspx + val lolz: String = + """ + | + | + | + | + | + | + | + | + | + | + | + |]> + |&lol9; + |""".stripMargin +} diff --git a/jvm/src/test/scala/scala/xml/JavaByteSerialization.scala b/jvm/src/test/scala/scala/xml/JavaByteSerialization.scala new file mode 100644 index 000000000..af88381cc --- /dev/null +++ b/jvm/src/test/scala/scala/xml/JavaByteSerialization.scala @@ -0,0 +1,18 @@ +package scala.xml + +import java.io.Serializable +import org.apache.commons.lang3.SerializationUtils + +object JavaByteSerialization { + def roundTrip[T <: Serializable](obj: T): T = { + SerializationUtils.roundtrip(obj) + } + + def serialize[T <: Serializable](in: T): Array[Byte] = { + SerializationUtils.serialize(in) + } + + def deserialize[T <: Serializable](in: Array[Byte]): T = { + SerializationUtils.deserialize(in) + } +} diff --git a/jvm/src/test/scala/scala/xml/ReuseNodesTest.scala b/jvm/src/test/scala/scala/xml/ReuseNodesTest.scala new file mode 100644 index 000000000..81c839bba --- /dev/null +++ b/jvm/src/test/scala/scala/xml/ReuseNodesTest.scala @@ -0,0 +1,100 @@ +package scala.xml + +import scala.xml.transform._ +import scala.collection.Seq +import org.junit.Assert.assertSame +import org.junit.experimental.theories.Theories +import org.junit.experimental.theories.Theory +import org.junit.experimental.theories.DataPoints +import org.junit.runner.RunWith +/** + * This test verifies that after the transform, the resultant xml node + * uses as many old nodes as possible. + * + * Three transformers class for case - + * One for original, one for modified, and one proposed which shows + * all are equivalent when it comes to reusing as many nodes as possible + */ +object ReuseNodesTest { + + class OriginalTranformr(rules: RewriteRule*) extends RuleTransformer(rules:_*) { + override def transform(ns: Seq[Node]): Seq[Node] = { + val xs: Seq[Seq[Node]] = ns.toStream.map(transform) + val (xs1: Seq[(Seq[Node], Node)], xs2: Seq[(Seq[Node], Node)]) = xs.zip(ns).span { case (x, n) => unchanged(n, x) } + + if (xs2.isEmpty) ns + else xs1.map(_._2) ++ xs2.head._1 ++ transform(ns.drop(xs1.length + 1)) + } + override def transform(n: Node): Seq[Node] = super.transform(n) + } + + class ModifiedTranformr(rules: RewriteRule*) extends RuleTransformer(rules:_*) { + override def transform(ns: Seq[Node]): Seq[Node] = { + val changed: Seq[Node] = ns.flatMap(transform) + + if (changed.length != ns.length || changed.zip(ns).exists(p => p._1 != p._2)) changed + else ns + } + override def transform(n: Node): Seq[Node] = super.transform(n) + } + + class AlternateTranformr(rules: RewriteRule*) extends RuleTransformer(rules:_*) { + override def transform(ns: Seq[Node]): Seq[Node] = { + val xs: Seq[Seq[Node]] = ns.toStream.map(transform) + val (xs1: Seq[(Seq[Node], Node)], xs2: Seq[(Seq[Node], Node)]) = xs.zip(ns).span { case (x, n) => unchanged(n, x) } + + if (xs2.isEmpty) ns + else xs1.map(_._2) ++ xs2.head._1 ++ transform(ns.drop(xs1.length + 1)) + } + override def transform(n: Node): Seq[Node] = super.transform(n) + } + + def rewriteRule: RewriteRule = new RewriteRule { + override def transform(n: Node): NodeSeq = n match { + case n if n.label == "change" => Elem( + n.prefix, "changed", n.attributes, n.scope, n.child.isEmpty, n.child : _*) + case _ => n + } + } + + @DataPoints + def tranformers(): Array[RuleTransformer] = Array( + new OriginalTranformr(rewriteRule), + new ModifiedTranformr(rewriteRule), + new AlternateTranformr(rewriteRule)) +} + +@RunWith(classOf[Theories]) +class ReuseNodesTest { + + @Theory + def transformReferentialEquality(rt: RuleTransformer): Unit = { + val original: Elem =

+ val tranformed: Seq[Node] = rt.transform(original) + assertSame(original, tranformed) + } + + @Theory + def transformReferentialEqualityOnly(rt: RuleTransformer): Unit = { + val original: Elem =
+ val transformed: Seq[Node] = rt.transform(original) + recursiveAssert(original,transformed) + } + + def recursiveAssert(original: Seq[Node], transformed: Seq[Node]): Unit = { + original.zip(transformed).foreach { + case (x, y) => recursiveAssert(x, y) + } + } + + def recursiveAssert(original: Node, transformed: Node): Unit = { + transformed.label match { + case "changed" => // do nothing expect this node to be changed + recursiveAssert(original.child,transformed.child) + case _ => + assertSame(original, transformed) + // No need to check for children, node being immutable + // children can't be different if parents are referentially equal + } + } +} diff --git a/jvm/src/test/scala/scala/xml/SerializationTest.scala b/jvm/src/test/scala/scala/xml/SerializationTest.scala new file mode 100644 index 000000000..f795daae4 --- /dev/null +++ b/jvm/src/test/scala/scala/xml/SerializationTest.scala @@ -0,0 +1,31 @@ +package scala.xml + +import scala.collection.Seq +import org.junit.Assert.assertEquals +import org.junit.Test + +class SerializationTest { + @Test + def xmlLiteral(): Unit = { + val n: Elem = + assertEquals(n, JavaByteSerialization.roundTrip(n)) + } + + @Test + def empty(): Unit = { + assertEquals(NodeSeq.Empty, JavaByteSerialization.roundTrip(NodeSeq.Empty)) + } + + @Test + def unmatched(): Unit = { + assertEquals(NodeSeq.Empty, JavaByteSerialization.roundTrip( \ "HTML")) + } + + @Test + def implicitConversion(): Unit = { + val parent: Elem = + val children: Seq[Node] = parent.child + val asNodeSeq: NodeSeq = children // implicit seqToNodeSeq + assertEquals(asNodeSeq, JavaByteSerialization.roundTrip(asNodeSeq)) + } +} diff --git a/jvm/src/test/scala/scala/xml/XMLSyntaxTest.scala b/jvm/src/test/scala/scala/xml/XMLSyntaxTest.scala new file mode 100644 index 000000000..e3ac914fd --- /dev/null +++ b/jvm/src/test/scala/scala/xml/XMLSyntaxTest.scala @@ -0,0 +1,25 @@ +package scala.xml + +import org.junit.Test +import org.junit.Assert.assertEquals + +class XMLSyntaxTestJVM { + + @Test + def test3(): Unit = { + // this demonstrates how to handle entities + val s: io.Source = io.Source.fromString(" ") + object parser extends xml.parsing.ConstructingParser(s, preserveWS = false /*ignore ws*/) { + override def replacementText(entityName: String): io.Source = { + entityName match { + case "nbsp" => io.Source.fromString("\u0160") + case _ => super.replacementText(entityName) + } + } + nextch() // !!important, to initialize the parser + } + val parsed: NodeSeq = parser.element(TopScope) // parse the source as element + // alternatively, we could call document() + assertEquals("Š", parsed.toString) + } +} diff --git a/jvm/src/test/scala/scala/xml/XMLTest.scala b/jvm/src/test/scala/scala/xml/XMLTest.scala new file mode 100644 index 000000000..4e5dba1a6 --- /dev/null +++ b/jvm/src/test/scala/scala/xml/XMLTest.scala @@ -0,0 +1,1085 @@ +package scala.xml + +import org.junit.{Test => UnitTest} +import org.junit.Assert.{assertEquals, assertFalse, assertNull, assertThrows, assertTrue} +import java.io.{ByteArrayInputStream, ByteArrayOutputStream, InputStreamReader, IOException, ObjectInputStream, + ObjectOutputStream, OutputStreamWriter, PrintStream, StringWriter} +import java.net.URL +import scala.xml.dtd.{DocType, PublicID} +import scala.xml.parsing.ConstructingParser +import scala.xml.Utility.sort + +object XMLTestJVM { + val e: MetaData = Null //Node.NoAttributes + val sc: NamespaceBinding = TopScope +} + +class XMLTestJVM { + import XMLTestJVM.{e, sc} + + def Elem(prefix: String, label: String, attributes: MetaData, scope: NamespaceBinding, child: Node*): Elem = + scala.xml.Elem.apply(prefix, label, attributes, scope, minimizeEmpty = true, child: _*) + + lazy val parsedxml1: Elem = XML.loadString("") + lazy val parsedxml11: Elem = XML.loadString("") + val xmlFile2: String = "Peter BunemanDan SuciuData on ze webJohn MitchellFoundations of Programming Languages" + lazy val parsedxml2: Elem = XML.loadString(xmlFile2) + + @UnitTest + def equality(): Unit = { + val c: Node = new Node { + override def label: String = "hello" + override def hashCode: Int = + Utility.hashCode(prefix, label, this.attributes.hashCode(), scope.hashCode(), child) + override def child: Seq[Node] = Elem(null, "world", e, sc) + //def attributes = e + override def text: String = "" + } + + assertEquals(c, parsedxml11) + assertEquals(parsedxml1, parsedxml11) + assertTrue(List(parsedxml1).sameElements(List(parsedxml11))) + assertTrue(Array(parsedxml1).toList.sameElements(List(parsedxml11))) + + val x2: String = "Peter BunemanDan SuciuData on ze web" + val x2p: Elem = XML.loadString(x2) + + assertEquals(Elem(null, "book", e, sc, + Elem(null, "author", e, sc, Text("Peter Buneman")), + Elem(null, "author", e, sc, Text("Dan Suciu")), + Elem(null, "title", e, sc, Text("Data on ze web"))), x2p) + } + + @UnitTest + def xpath(): Unit = { + assertTrue((parsedxml1 \ "_").sameElements(List(Elem(null, "world", e, sc)))) + + assertTrue((parsedxml1 \ "world").sameElements(List(Elem(null, "world", e, sc)))) + + assertTrue( + (parsedxml2 \ "_").sameElements(List( + Elem(null, "book", e, sc, + Elem(null, "author", e, sc, Text("Peter Buneman")), + Elem(null, "author", e, sc, Text("Dan Suciu")), + Elem(null, "title", e, sc, Text("Data on ze web"))), + Elem(null, "book", e, sc, + Elem(null, "author", e, sc, Text("John Mitchell")), + Elem(null, "title", e, sc, Text("Foundations of Programming Languages")))))) + assertTrue((parsedxml2 \ "author").isEmpty) + + assertTrue( + (parsedxml2 \ "book").sameElements(List( + Elem(null, "book", e, sc, + Elem(null, "author", e, sc, Text("Peter Buneman")), + Elem(null, "author", e, sc, Text("Dan Suciu")), + Elem(null, "title", e, sc, Text("Data on ze web"))), + Elem(null, "book", e, sc, + Elem(null, "author", e, sc, Text("John Mitchell")), + Elem(null, "title", e, sc, Text("Foundations of Programming Languages")))))) + + assertTrue( + (parsedxml2 \ "_" \ "_").sameElements(List( + Elem(null, "author", e, sc, Text("Peter Buneman")), + Elem(null, "author", e, sc, Text("Dan Suciu")), + Elem(null, "title", e, sc, Text("Data on ze web")), + Elem(null, "author", e, sc, Text("John Mitchell")), + Elem(null, "title", e, sc, Text("Foundations of Programming Languages"))))) + + assertTrue( + (parsedxml2 \ "_" \ "author").sameElements(List( + Elem(null, "author", e, sc, Text("Peter Buneman")), + Elem(null, "author", e, sc, Text("Dan Suciu")), + Elem(null, "author", e, sc, Text("John Mitchell"))))) + + assertTrue((parsedxml2 \ "_" \ "_" \ "author").isEmpty) + } + + @UnitTest + def xpathDESCENDANTS(): Unit = { + assertTrue( + (parsedxml2 \\ "author").sameElements(List( + Elem(null, "author", e, sc, Text("Peter Buneman")), + Elem(null, "author", e, sc, Text("Dan Suciu")), + Elem(null, "author", e, sc, Text("John Mitchell"))))) + + assertTrue( + (parsedxml2 \\ "title").sameElements(List( + Elem(null, "title", e, sc, Text("Data on ze web")), + Elem(null, "title", e, sc, Text("Foundations of Programming Languages"))))) + + assertEquals("Peter BunemanDan SuciuData on ze web", + (parsedxml2 \\ "book") { (n: Node) => (n \ "title") xml_== "Data on ze web" }.toString) + + assertTrue( + (NodeSeq.fromSeq(List(parsedxml2)) \\ "_").sameElements(List( + Elem(null, "bib", e, sc, + Elem(null, "book", e, sc, + Elem(null, "author", e, sc, Text("Peter Buneman")), + Elem(null, "author", e, sc, Text("Dan Suciu")), + Elem(null, "title", e, sc, Text("Data on ze web"))), + Elem(null, "book", e, sc, + Elem(null, "author", e, sc, Text("John Mitchell")), + Elem(null, "title", e, sc, Text("Foundations of Programming Languages")))), + Elem(null, "book", e, sc, + Elem(null, "author", e, sc, Text("Peter Buneman")), + Elem(null, "author", e, sc, Text("Dan Suciu")), + Elem(null, "title", e, sc, Text("Data on ze web"))), + Elem(null, "author", e, sc, Text("Peter Buneman")), + Elem(null, "author", e, sc, Text("Dan Suciu")), + Elem(null, "title", e, sc, Text("Data on ze web")), + Elem(null, "book", e, sc, + Elem(null, "author", e, sc, Text("John Mitchell")), + Elem(null, "title", e, sc, Text("Foundations of Programming Languages"))), + Elem(null, "author", e, sc, Text("John Mitchell")), + Elem(null, "title", e, sc, Text("Foundations of Programming Languages"))))) + } + + @UnitTest + def unparsed(): Unit = { + // println("attribute value normalization") + val xmlAttrValueNorm: String = "" + + { + val parsedxmlA: Elem = XML.loadString(xmlAttrValueNorm) + val c: Char = (parsedxmlA \ "@nom").text.charAt(0) + assertTrue(c == '\u015e') + } + // buraq: if the following test fails with 'character x not allowed', it is + // related to the mutable variable in a closures in MarkupParser.parsecharref + { + val isr: scala.io.Source = scala.io.Source.fromString(xmlAttrValueNorm) + val pxmlB: ConstructingParser = ConstructingParser.fromSource(isr, preserveWS = false) + val parsedxmlB: NodeSeq = pxmlB.element(TopScope) + val c: Char = (parsedxmlB \ "@nom").text.charAt(0) + assertTrue(c == '\u015e') + } + + // #60 test by round trip + + val p: ConstructingParser = ConstructingParser.fromSource(scala.io.Source.fromString(""), preserveWS = true) + val n: Node = p.element(NamespaceBinding("bar", "BAR", TopScope))(0) + assertTrue(n.attributes.get("BAR", n, "attr").nonEmpty) + } + + def f(s: String): Elem = { + + { + for (item <- s split ',') yield { item } + } + + } + + @UnitTest + def nodeBuffer(): Unit = + assertEquals( + """ + abc + """, f("a,b,c").toString) + + object Serialize { + @throws(classOf[IOException]) + def write[A](o: A): Array[Byte] = { + val ba: ByteArrayOutputStream = new ByteArrayOutputStream(512) + val out: ObjectOutputStream = new ObjectOutputStream(ba) + out.writeObject(o) + out.close() + ba.toByteArray + } + @throws(classOf[IOException]) + @throws(classOf[ClassNotFoundException]) + def read[A](buffer: Array[Byte]): A = { + val in: ObjectInputStream = + new ObjectInputStream(new ByteArrayInputStream(buffer)) + in.readObject().asInstanceOf[A] + } + def check[A, B](x: A, y: B): Unit = { + // println("x = " + x) + // println("y = " + y) + // println("x == y: " + (x == y) + ", y == x: " + (y == x)) + assertTrue(x == y && y == x) + // println() + } + } + + @UnitTest + def serializeAttribute(): Unit = { + // Attribute + val a1: PrefixedAttribute = new PrefixedAttribute("xml", "src", Text("hello"), Null) + val _a1: Attribute = Serialize.read(Serialize.write(a1)) + Serialize.check(a1, _a1) + } + + @UnitTest + def serializeDocument(): Unit = { + // Document + val d1: Document = new Document + d1.docElem = + d1.encoding = Some("UTF-8") + val _d1: Document = Serialize.read(Serialize.write(d1)) + Serialize.check(d1, _d1) + } + + @UnitTest + def serializeElem(): Unit = { + // Elem + val e1: Elem = title + val _e1: Elem = Serialize.read(Serialize.write(e1)) + Serialize.check(e1, _e1) + } + + @UnitTest + def serializeComplex(): Unit = { + case class Person(name: String, age: Int) + class AddressBook(a: Person*) { + private val people: List[Person] = a.toList + def toXHTML: Elem = + + + + + + { + for (p <- people) yield + + + + } +
Last NameFirst Name
{ p.name } { p.age.toString }
+ } + + val people: AddressBook = new AddressBook( + Person("Tom", 20), + Person("Bob", 22), + Person("James", 19)) + + val e2: Elem = + + + { people.toXHTML } + + + val _e2: Elem = Serialize.read(Serialize.write(e2)) + Serialize.check(e2, _e2) + } + + // t-486 + def wsdlTemplate1(serviceName: String): Node = + + + + def wsdlTemplate2(serviceName: String, targetNamespace: String): Node = + + + + def wsdlTemplate4(serviceName: String, targetNamespace: () => String): Node = + + + + @UnitTest + def wsdl(): Unit = { + assertEquals(""" + """, wsdlTemplate1("service1").toString) + assertEquals(""" + """, wsdlTemplate2("service2", "target2").toString) + assertEquals(""" + """, wsdlTemplate4("service4", () => "target4").toString) + } + + // Issue found with ISO-8859-1 in #121 that was fixed with UTF-8 default + @UnitTest + def writeReadNoDeclarationDefaultEncoding(): Unit = { + val chars: Seq[Char] = ((32 to 126) ++ (160 to 255)).map(_.toChar) + val xml: Elem = { chars.mkString } + + // com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: + // Invalid byte 1 of 1-byte UTF-8 sequence. + // scala.xml.XML.save("foo.xml", xml) + // scala.xml.XML.loadFile("foo.xml").toString + + val outputStream: ByteArrayOutputStream = new ByteArrayOutputStream + val streamWriter: OutputStreamWriter = new OutputStreamWriter(outputStream, "UTF-8") + + XML.write(streamWriter, xml, XML.encoding, xmlDecl = false, null) + streamWriter.flush() + + val inputStream: ByteArrayInputStream = new ByteArrayInputStream(outputStream.toByteArray) + val streamReader: InputStreamReader = new InputStreamReader(inputStream, XML.encoding) + + assertEquals(xml.toString, XML.load(streamReader).toString) + } + + @UnitTest + def t0663(): Unit = { + val src: scala.io.Source = scala.io.Source.fromString("") + val parser: ConstructingParser = ConstructingParser.fromSource(src, preserveWS = true) + assertEquals("", parser.document().toString) + } + + @UnitTest + def t1079(): Unit = assertFalse( == ) + + @UnitTest + def t1620(): Unit = { + val dt: DocType = DocType("foo", PublicID("-//Foo Corp//DTD 1.0//EN", "foo.dtd"), Seq()) + var pw: StringWriter = new StringWriter() + XML.write(pw, , "utf-8", xmlDecl = true, dt) + pw.flush() + assertEquals(""" + +""", pw.toString) + + pw = new StringWriter() + val dt2: DocType = DocType("foo", PublicID("-//Foo Corp//DTD 1.0//EN", null), Seq()) + XML.write(pw, , "utf-8", xmlDecl = true, dt2) + pw.flush() + assertEquals(""" + +""", pw.toString) + } + + @UnitTest + def t1773(): Unit = { + val xs: List[Elem] = List( + , + , + { NodeSeq.Empty }, + { "" }, + { if (true) "" else "I like turtles" }) + + for (x1 <- xs; x2 <- xs) assertTrue(x1 xml_== x2) + } + + @UnitTest + def t2771(): Unit = { + val xml1: Elem = + val xml2: Elem = XML.loadString("""""") + + def backslashSearch(x: Elem): String = "root:-" + (x \ "@{nsUri}at") + "-sub:-" + (x \ "sub" \ "@{nsUri}at") + "-" + + assertEquals("root:-rootVal-sub:-subVal-", backslashSearch(xml1)) + assertEquals("root:-rootVal-sub:-subVal-", backslashSearch(xml2)) + } + + @UnitTest + def t3886(): Unit = { + assertTrue( == ) + assertTrue( != ) + assertTrue( != ) + + assertTrue( != ) + assertTrue( != ) + assertTrue( != ) + } + + @UnitTest + def t4387(): Unit = { + import XML.loadString + def mkElem(arg: String): Elem = + + val x1: Elem = mkElem("5") + val x2: Elem = mkElem("50") + + assertEquals(x1, loadString("" + x1)) + assertTrue(x2 != loadString("" + x1)) + } + + @UnitTest + def t5052(): Unit = { + assertTrue( xml_== ) + assertTrue( xml_== ) + assertTrue( xml_== ) + assertTrue( xml_== ) + } + + @UnitTest + def t5115(): Unit = { + def assertHonorsIterableContract(i: Iterable[?]): Unit = assertEquals(i.size.toLong, i.iterator.size.toLong) + + assertHonorsIterableContract(.attributes) + assertHonorsIterableContract(.attributes) + assertHonorsIterableContract(.attributes) + assertHonorsIterableContract(.attributes) + assertHonorsIterableContract(.attributes) + assertHonorsIterableContract(.attributes) + assertHonorsIterableContract(.attributes) + assertHonorsIterableContract(.attributes) + } + + @UnitTest + def t5843(): Unit = { + val foo: Attribute = Attribute(null, "foo", "1", Null) + val bar: Attribute = Attribute(null, "bar", "2", foo) + val ns: NamespaceBinding = NamespaceBinding(null, "uri", TopScope) + + assertEquals(""" foo="1"""", foo.toString) + assertEquals(null, TopScope.getURI(foo.pre)) + assertEquals(""" bar="2"""", bar.remove("foo").toString) + assertEquals(""" foo="1"""", bar.remove("bar").toString) + assertEquals(""" bar="2"""", bar.remove(null, TopScope, "foo").toString) + assertEquals(""" foo="1"""", bar.remove(null, TopScope, "bar").toString) + assertEquals(""" bar="2" foo="1"""", bar.toString) + assertEquals(""" bar="2" foo="1"""", bar.remove(null, ns, "foo").toString) + assertEquals(""" bar="2" foo="1"""", bar.remove(null, ns, "bar").toString) + } + + @UnitTest + def t6939(): Unit = { + val foo: Elem = + assertEquals(foo.child.head.scope.toString, """ xmlns:x="http://bar.com/"""") + + val fooDefault: Elem = + assertEquals(fooDefault.child.head.scope.toString, """ xmlns="http://bar.com/"""") + + val foo2: Elem = XML.loadString("""""") + assertEquals(foo2.child.head.scope.toString, """ xmlns:x="http://bar.com/"""") + + val foo2Default: Elem = XML.loadString("""""") + assertEquals(foo2Default.child.head.scope.toString, """ xmlns="http://bar.com/"""") + } + + @UnitTest + def t7074(): Unit = { + assertEquals("""""", sort().toString) + assertEquals("""""", sort().toString) + assertEquals("""""", sort().toString) + assertEquals("""""", sort().toString) + assertEquals("""""", sort().toString) + assertEquals("""""", sort().toString) + assertEquals("""""", sort().toString) + assertEquals("""""", sort().toString) + assertEquals("""""", sort().toString) + } + + @UnitTest + def t9060(): Unit = { + val expected: String = """""" + assertEquals(expected, XML.loadString(expected).toString) + } + + @UnitTest + def attributes(): Unit = { + val noAttr: Elem = + val attrNull: Elem = + val attrNone: Elem = + val preAttrNull: Elem = + val preAttrNone: Elem = + assertEquals(noAttr, attrNull) + assertEquals(noAttr, attrNone) + assertEquals(noAttr, preAttrNull) + assertEquals(noAttr, preAttrNone) + + val xml1: Elem = + val xml2: Elem = + val xml3: Elem = + assertEquals(xml1, xml2) + assertEquals(xml1, xml3) + + assertEquals("""""", noAttr.toString) + assertEquals("""""", attrNull.toString) + assertEquals("""""", attrNone.toString) + assertEquals("""""", preAttrNull.toString) + assertEquals("""""", preAttrNone.toString) + assertEquals("""""", xml1.toString) + assertEquals("""""", xml2.toString) + assertEquals("""""", xml3.toString) + + // Check if attribute order is retained + assertEquals("""""", .toString) + assertEquals("""""", .toString) + assertEquals("""""", .toString) + assertEquals("""""", .toString) + } + + @UnitTest + def dontLoop(): Unit = { + val xml: String = " " + val sink: PrintStream = new PrintStream(new ByteArrayOutputStream()) + Console.withOut(sink) { + Console.withErr(sink) { + ConstructingParser.fromSource(io.Source.fromString(xml), preserveWS = true).document().docElem + } + } + } + + // With both internal and external Xerces now on the classpath, we explicitly disambiguate which one we want: + def xercesInternal: javax.xml.parsers.SAXParserFactory = + javax.xml.parsers.SAXParserFactory.newInstance("com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl", null) + + def xercesExternal: javax.xml.parsers.SAXParserFactory = + javax.xml.parsers.SAXParserFactory.newInstance("org.apache.xerces.jaxp.SAXParserFactoryImpl", null) + + /** Default SAXParserFactory */ + val defaultParserFactory: javax.xml.parsers.SAXParserFactory = xercesInternal + + @throws(classOf[org.xml.sax.SAXNotRecognizedException]) + def issue17UnrecognizedFeature(): Unit = { + assertTrue(defaultParserFactory.getFeature("foobar")) + } + + @UnitTest + def issue17SecureProcessing(): Unit = { + assertTrue(defaultParserFactory.getFeature("http://javax.xml.XMLConstants/feature/secure-processing")) + } + + @UnitTest + def issue17ExternalGeneralEntities(): Unit = { + assertTrue(defaultParserFactory.getFeature("http://xml.org/sax/features/external-general-entities")) + } + + @UnitTest + def issue17LoadExternalDtd(): Unit = { + assertTrue(defaultParserFactory.getFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd")) + } + + @UnitTest + def issue17DisallowDoctypeDecl(): Unit = { + assertFalse(defaultParserFactory.getFeature("http://apache.org/xml/features/disallow-doctype-decl")) + } + + @UnitTest + def issue17ExternalParameterEntities(): Unit = { + assertTrue(defaultParserFactory.getFeature("http://xml.org/sax/features/external-parameter-entities")) + } + + @UnitTest + def issue17ResolveDtdUris(): Unit = { + assertTrue(defaultParserFactory.getFeature("http://xml.org/sax/features/resolve-dtd-uris")) + } + + @UnitTest + def issue17isXIncludeAware(): Unit = { + assertFalse(XML.parser.isXIncludeAware) + } + + @UnitTest + def issue17isNamespaceAware(): Unit = { + assertFalse(XML.parser.isNamespaceAware) + } + + @UnitTest + def issue28(): Unit = { + val x: Elem = + // val ns = new NamespaceBinding("x", "gaga", sc) + // val x = Elem("x", "foo", e, ns) + val pp: PrettyPrinter = new PrettyPrinter(80, 2) + // This assertion passed + assertEquals("""""", x.toString) + // This was the bug, producing an errant xmlns attribute + assertEquals("""""", pp.format(x)) + } + + @UnitTest( expected = classOf[scala.xml.SAXParseException] ) + def issue35(): Unit = { + val broken: String = " ") + // does not work: roundtripNodes(" ") + } + + // using non-namespace-aware parser, this always worked; + // using namespace-aware parser, this works with FactoryAdapter enhanced to handle startPrefixMapping() events; + // see https://github.com/scala/scala-xml/issues/506 + def roundtrip(namespaceAware: Boolean, xml: String): Unit = { + val parserFactory: javax.xml.parsers.SAXParserFactory = xercesInternal + parserFactory.setFeature("http://javax.xml.XMLConstants/feature/secure-processing", true) + parserFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false) + parserFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true) + parserFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false) + parserFactory.setFeature("http://xml.org/sax/features/external-general-entities", false) + parserFactory.setFeature("http://xml.org/sax/features/resolve-dtd-uris", false) + parserFactory.setNamespaceAware(namespaceAware) + parserFactory.setXIncludeAware(namespaceAware) + + assertEquals(xml, XML.withSAXParser(parserFactory.newSAXParser).loadString(xml).toString) + } + + @UnitTest + def namespaceUnaware(): Unit = + roundtrip(namespaceAware = false, """""") + + @UnitTest + def namespaceAware(): Unit = + roundtrip(namespaceAware = true, """""") + + @UnitTest + def namespaceAware2(): Unit = + roundtrip(namespaceAware = true, """""") + + @UnitTest + def useXMLReaderWithXMLFilter(): Unit = { + val parent: org.xml.sax.XMLReader = xercesInternal.newSAXParser.getXMLReader + val filter: org.xml.sax.XMLFilter = new org.xml.sax.helpers.XMLFilterImpl(parent) { + override def characters(ch: Array[Char], start: Int, length: Int): Unit = { + for (i <- 0.until(length)) if (ch(start+i) == 'a') ch(start+i) = 'b' + super.characters(ch, start, length) + } + } + assertEquals(XML.withXMLReader(filter).loadString("caffeeaaay").toString, "cbffeebbby") + } + + @UnitTest + def checkThatErrorHandlerIsNotOverwritten(): Unit = { + var gotAnError: Boolean = false + val reader = XML.reader + reader.setErrorHandler(new org.xml.sax.ErrorHandler { + override def warning(e: SAXParseException): Unit = gotAnError = true + override def error(e: SAXParseException): Unit = gotAnError = true + override def fatalError(e: SAXParseException): Unit = gotAnError = true + }) + try { + XML.adapter.loadDocument(Source.fromString(""), reader) + } catch { + case _: org.xml.sax.SAXParseException => + } + assertTrue(gotAnError) + } + + def resourceUrl(resourceName: String): URL = getClass.getResource(s"$resourceName.xml") + + // Here we see that opening InputStream prematurely, as was done previously, breaks XInclude. + @UnitTest(expected = classOf[org.xml.sax.SAXParseException]) def xIncludeNeedsSystemId(): Unit = { + val parserFactory = xercesInternal + parserFactory.setNamespaceAware(true) + parserFactory.setXIncludeAware(true) + XML + .withSAXParser(parserFactory.newSAXParser) + .load(resourceUrl("site").openStream()) + .toString + } + + // Now that we can use XML parser configured to be namespace-aware, + // we can also configure it to be XInclude-aware and process XML Includes: + def check( + parserFactory: javax.xml.parsers.SAXParserFactory, + resourceName: String, + expected: String + ): Unit = { + parserFactory.setNamespaceAware(true) + parserFactory.setXIncludeAware(true) + val actual: String = XML + .withSAXParser(parserFactory.newSAXParser) + .load(resourceUrl(resourceName)) + .toString + + assertEquals(expected, actual) + } + + // Here we demonstrate that XInclude works with both the external and the built-in Xerces: + + val includerExpected: String = + s""" + | + | Blah! + | + |""".stripMargin + + @UnitTest def xIncludeWithExternalXerces(): Unit = check(xercesExternal, "includer", includerExpected) + @UnitTest def xIncludeWithInternalXerces(): Unit = check(xercesInternal, "includer", includerExpected) + + // And here we demonstrate that both external and built-in Xerces report incorrect `xml:base` + // when the XML file included contains its own include, and included files are not in the same directory: + // `xml:base` on the `` element is incorrect + // books/book/author/volume/1.xml instead of the correct + // archive/books/book/author/volume/1.xml! + val siteUnfortunatelyExpected: String = + s""" + | + | + | + | + | + |""".stripMargin + + // Turns out, this is a known Xerces bug https://issues.apache.org/jira/browse/XERCESJ-1102: + // - the bug was reported in October 2005 - more then seventeen years ago; + // - a patch fixing it (that I have not verified personally) was submitted many years ago; + // - the bug is still not fixed in the 2023 release of Xerces; + // - the bug was discussed by the Saxon users in https://saxonica.plan.io/issues/4664, + // and is allegedly fixed in SaxonC 11.1 - although how can this be with Saxon not shipping its own Xerces is not clear. + // + // In my own application, I had to "fix up" incorrect values produced by Xerces, taking into account + // specific directory layout being used. I can only speculate what others do, but none of the alternatives sound great: + // - avoid using nested includes altogether or flatten the directory hierarchy to appease the bug; + // - use privately patched version of Xerces; + // - use Saxon DOM parsing instead of Xerces' SAX. + // + // I find it utterly incomprehensible that foundational library shipped with JDK and used everywhere + // has a bug in its core functionality for years and it never gets fixed, but sadly, it is the state of affairs: + @UnitTest def xIncludeFailWithExternalXerces(): Unit = check(xercesExternal, "site", siteUnfortunatelyExpected) + @UnitTest def xIncludeFailWithInternalXerces(): Unit = check(xercesInternal, "site", siteUnfortunatelyExpected) + + @UnitTest + def documentBaseURI(): Unit = { + val url: URL = resourceUrl("site") + // XMLLoader returns the document's baseURI: + assert(XML.withSAXParser(xercesInternal.newSAXParser).loadDocument(url).baseURI.endsWith("/test-classes/scala/xml/site.xml")) + assert(XML.withSAXParser(xercesExternal.newSAXParser).loadDocument(url).baseURI.endsWith("/test-classes/scala/xml/site.xml")) + // ConstructingParser does not return it of course: since it uses scala.io.Source it has no idea where is the XML coming from: + assertNull(ConstructingParser.fromSource(scala.io.Source.fromURI(url.toURI), preserveWS = false).document().baseURI) + } + + @UnitTest + def xmlStandAlone(): Unit = { + val standAlone: String = s"""""" + val nonStandAlone: String = s"""""" + val default: String = s"""""" + val noXmlDeclaration: String = s"""""" + + // ConstructingParser returns standAlone status of the document straight from the `xml` declaration: + assertEquals(Some(true ), ConstructingParser.fromSource(scala.io.Source.fromString(standAlone), preserveWS = false).document().standAlone) + assertEquals(Some(false), ConstructingParser.fromSource(scala.io.Source.fromString(nonStandAlone), preserveWS = false).document().standAlone) + assertTrue(ConstructingParser.fromSource(scala.io.Source.fromString(default), preserveWS = false).document().standAlone.isEmpty) + // ConstructingParser incorrectly returns null standAlone value when the document does not have the xml declaration: + assertNull(ConstructingParser.fromSource(scala.io.Source.fromString(noXmlDeclaration), preserveWS = false).document().standAlone) + + // XMLLoader returns standAlone status of the document straight from the `xml` declaration: + assertTrue(XML.withSAXParser(xercesInternal.newSAXParser).loadStringDocument(standAlone).standAlone.contains(true)) + assertTrue(XML.withSAXParser(xercesInternal.newSAXParser).loadStringDocument(nonStandAlone).standAlone.contains(false)) + assertTrue(XML.withSAXParser(xercesInternal.newSAXParser).loadStringDocument(default).standAlone.contains(false)) + assertTrue(XML.withSAXParser(xercesInternal.newSAXParser).loadStringDocument(noXmlDeclaration).standAlone.contains(false)) + } + + @UnitTest + def xmlVersion(): Unit = { + val xml10 = s"""""" + val xml11 = s"""""" + val noXmlDeclaration: String = s"""""" + + // ConstructingParser returns XML version of the document straight from the `xml` declaration for version="1.0": + assertEquals(Some("1.0"), ConstructingParser.fromSource(scala.io.Source.fromString(xml10), preserveWS = false).document().version) + // ConstructingParser returns incorrect version value when the the version is "1.1" (and prints "cannot deal with versions != 1.0a"): + assertTrue(ConstructingParser.fromSource(scala.io.Source.fromString(xml11), preserveWS = false).document().version.isEmpty) + // ConstructingParser incorrectly returns null version value when the document does not have the xml declaration: + assertNull(ConstructingParser.fromSource(scala.io.Source.fromString(noXmlDeclaration), preserveWS = false).document().version) + + // XMLLoader returns XML version of the document straight from the `xml` declaration + assertTrue(xercesInternal.getFeature("http://xml.org/sax/features/xml-1.1")) + assertEquals(Some("1.0"), XML.withSAXParser(xercesInternal.newSAXParser).loadStringDocument(xml10).version) + assertEquals(Some("1.1"), XML.withSAXParser(xercesInternal.newSAXParser).loadStringDocument(xml11).version) + assertEquals(Some("1.0"), XML.withSAXParser(xercesInternal.newSAXParser).loadStringDocument(noXmlDeclaration).version) + } + + @UnitTest + def xmlEncoding(): Unit = { + val utf8: String = s"""""" + val utf16: String = s"""""" + val default: String = s"""""" + val noXmlDeclaration: String = s"""""" + + // ConstructingParser returns XML encoding name canonicalized from the `xml` declaration: + assertEquals(Some("UTF-8" ), ConstructingParser.fromSource(scala.io.Source.fromString(utf8 ), preserveWS = false).document().encoding) + assertEquals(Some("UTF-16"), ConstructingParser.fromSource(scala.io.Source.fromString(utf16 ), preserveWS = false).document().encoding) + assertEquals(None , ConstructingParser.fromSource(scala.io.Source.fromString(default), preserveWS = false).document().encoding) + // ConstructingParser incorrectly returns null encoding value when the document does not have the xml declaration: + assertNull(ConstructingParser.fromSource(scala.io.Source.fromString(noXmlDeclaration), preserveWS = false).document().encoding) + + // XMLLoader does not return the encoding specified in the `xml` declaration: + assertEquals(None, XML.loadStringDocument(utf8).encoding) + assertEquals(None, XML.loadStringDocument(utf16).encoding) + assertEquals(None, XML.loadStringDocument(default).encoding) + assertEquals(None, XML.loadStringDocument(noXmlDeclaration).encoding) + + // XMLLoader returns the encoding determined from the Byte Order Mark in the document itself: + assertEquals(Some("UTF-8"), XML.loadDocument(resourceUrl("utf8")).encoding) + assertEquals(Some("UTF-16BE"), XML.loadDocument(resourceUrl("utf16")).encoding) + + // ConstructingParser doesn't seem to be able to parse XML with Byte Order Mark: + assertThrows( + classOf[java.nio.charset.MalformedInputException], + () => ConstructingParser.fromSource(scala.io.Source.fromURI(resourceUrl("utf16").toURI), preserveWS = false).document().encoding + ) + } + + @UnitTest + def loadDtd(): Unit = { + val parserFactory: javax.xml.parsers.SAXParserFactory = xercesExternal + parserFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false) + + val xml: String = + s""" + | + | + | + | + | + | + | + | + | + |]> + |&AUTHOR; + |""".stripMargin + + val document: Document = XML.withSAXParser(parserFactory.newSAXParser).loadStringDocument(xml) + + // XMLLoader parses and returns DTD. + // Note: dtd.ContentModel that DTD uses to represent the element content model lacks fidelity: + // occurrence indicators "?" and "+" can not be expressed. + // Note: spurious parentheses come from the dtd.ContentModel's toString() methods... + assertEquals( + """DTD PUBLIC "-//OASIS//DTD DocBook V5.0//EN" "http://www.oasis-open.org/docbook/xml/5.0/docbook.dtd" [ + | + | + | + | + | + | + | + | + | + | + |]""".stripMargin, + document.dtd.toString) + + // XMLLoader resolves entities defined in the DTD - + // XML parser parses and uses the DTD internally, so there is no need to install any additional entity resolvers: + assertEquals("""John Doe""", document.docElem.toString) + + val document2: Document = ConstructingParser.fromSource(scala.io.Source.fromString(xml), preserveWS = false).document() + + // ConstructingParser + // ignores + // element declarations + // attribute list declarations + // some entity declarations + // notations + // captures + // decls: List[Decl] - for EntityDecl and PEReference + // ent: Map[String, EntityDecl] + // returns only + // decls + assertEquals( + s"""DTD PUBLIC "-//OASIS//DTD DocBook V5.0//EN" "http://www.oasis-open.org/docbook/xml/5.0/docbook.dtd" [ + | + |]""".stripMargin, + document2.dtd.toString) + + // ConstructingParser resolves entities defined in the DTD + assertEquals("""John Doe""", document2.docElem.toString) + } + + @UnitTest + def nodeSeqNs(): Unit = { + val x: NodeBuffer = { + + } + val pp: PrettyPrinter = new PrettyPrinter(80, 2) + val expected: String = """""" + assertEquals(expected, pp.formatNodes(x)) + } + + @UnitTest + def nodeStringBuilder(): Unit = { + val x: Elem = { + + } + val pp: PrettyPrinter = new PrettyPrinter(80, 2) + val expected: String = """""" + val sb: StringBuilder = new StringBuilder + pp.format(x, sb) + assertEquals(expected, sb.toString) + } + + @UnitTest + def issue46(): Unit = { + // val x = + val x: Elem = + // val x = Elem(null, "node", e, sc) + val pp: PrettyPrinter = new PrettyPrinter(80, 2) + // This assertion passed + assertEquals("", x.toString) + // This was the bug, producing + assertEquals("", pp.format(x.copy(minimizeEmpty = true))) + } + + @UnitTest + def issue90(): Unit = { + val pp: PrettyPrinter = new PrettyPrinter(80, 2, minimizeEmpty = true) + val x: Elem = + assertEquals("\n \n", pp.format(x)) + } + + @UnitTest + def issue231(): Unit = { + val pp: PrettyPrinter = new PrettyPrinter(4, 2, minimizeEmpty = true) + val x: Elem = + val formatted: String = pp.format(x) + val expected: String = + """| + |""".stripMargin + assertEquals(x, XML.loadString(formatted)) + assertEquals(expected, formatted) + } + + @UnitTest + def issue231_withoutAttributes(): Unit = { + val pp: PrettyPrinter = new PrettyPrinter(4, 2, minimizeEmpty = true) + val x: Elem = + val expected: String = + """| + |""".stripMargin + val formatted: String = pp.format(x) + assertEquals(x, XML.loadString(formatted)) + assertEquals(expected, formatted) + } + + @UnitTest + def issue231_children(): Unit = { + val pp: PrettyPrinter = new PrettyPrinter(4, 2, minimizeEmpty = true) + val x: Elem = + val formatted: String = pp.format(x) + val expected: String = + """| + | + | + | + | + | + | + |""".stripMargin + assertEquals(expected, formatted) + } + + @UnitTest + def issue231_elementText(): Unit = { + val pp: PrettyPrinter = new PrettyPrinter(4, 2, minimizeEmpty = true) + val x: Elem = xy + val formatted: String = pp.format(x) + val expected: String = + """| + | x + | + | + | y + | + | + |""".stripMargin + assertEquals(expected, formatted) + } + + def toSource(s: String): scala.io.Source = new scala.io.Source { + override val iter: Iterator[Char] = s.iterator + override def reportError(pos: Int, msg: String, out: PrintStream = Console.err): Unit = () + } + + @UnitTest + def xTokenTest(): Unit = { + val x: ConstructingParser = ConstructingParser.fromSource(toSource("a"), preserveWS = false) + assertEquals((): Unit, x.xToken('b')) + } + + @UnitTest(expected = classOf[scala.xml.parsing.FatalError]) + def xCharDataFailure(): Unit = { + val x: ConstructingParser = ConstructingParser.fromSource(toSource(""), preserveWS = false) + + x.xCharData + } + + @UnitTest(expected = classOf[scala.xml.parsing.FatalError]) + def xCommentFailure(): Unit = { + val x: ConstructingParser = ConstructingParser.fromSource(toSource(""), preserveWS = false) + + x.xComment + } + + @UnitTest + def xmlProcInstrTest(): Unit = { + val x: ConstructingParser = ConstructingParser.fromSource(toSource("aa"), preserveWS = false) + + assertEquals(new UnprefixedAttribute("aa", Text(""), Null), x.xmlProcInstr()) + } + + @UnitTest(expected = classOf[scala.xml.parsing.FatalError]) + def notationDeclFailure(): Unit = { + val x: ConstructingParser = ConstructingParser.fromSource(toSource(""), preserveWS = false) + + x.notationDecl() + } + + @UnitTest + def pubidLiteralTest(): Unit = { + val x: ConstructingParser = ConstructingParser.fromSource(toSource(""), preserveWS = false) + + assertEquals("", x.pubidLiteral()) + } + + @UnitTest + def xAttributeValueTest(): Unit = { + val x: ConstructingParser = ConstructingParser.fromSource(toSource("'"), preserveWS = false) + + assertEquals("", x.xAttributeValue()) + } + + @UnitTest + def xEntityValueTest(): Unit = { + val x: ConstructingParser = ConstructingParser.fromSource(toSource(""), preserveWS = false) + + assertEquals("", x.xEntityValue()) + } +} diff --git a/jvm/src/test/scala/scala/xml/parsing/ConstructingParserTest.scala b/jvm/src/test/scala/scala/xml/parsing/ConstructingParserTest.scala new file mode 100644 index 000000000..f796070da --- /dev/null +++ b/jvm/src/test/scala/scala/xml/parsing/ConstructingParserTest.scala @@ -0,0 +1,100 @@ +package scala.xml +package parsing + +import scala.io.Source +import org.junit.Test +import scala.xml.JUnitAssertsForXML.{ assertEquals => assertXml } +import org.junit.Assert.assertEquals + +class ConstructingParserTest { + + @Test + def t9060(): Unit = { + val a: String = """""" + val source: Source = new Source { + override val iter: Iterator[Char] = a.iterator + override def reportError(pos: Int, msg: String, out: java.io.PrintStream = Console.err): Unit = () + } + val doc: NodeSeq = ConstructingParser.fromSource(source, preserveWS = false).content(TopScope) + assertXml(a, doc) + } + + /* Example of using SYSTEM in DOCTYPE */ + @Test + def docbookTest(): Unit = { + val xml: String = + """| + | + | Book + | + | Chapter + | Text + | + |""".stripMargin + + val expected: Elem = + Book + + Chapter + Text + + + + val source: Source = new Source { + override val iter: Iterator[Char] = xml.iterator + override def reportError(pos: Int, msg: String, out: java.io.PrintStream = Console.err): Unit = () + } + + val doc: Document = ConstructingParser.fromSource(source, preserveWS = true).document() + + assertEquals(expected, doc.theSeq) + } + + /* Unsupported use of lowercase DOCTYPE and SYSTEM */ + @Test(expected = classOf[scala.xml.parsing.FatalError]) + def docbookFail(): Unit = { + val xml: String = + """| + | + |Book + | + |Chapter + |Text + | + |""".stripMargin + + val source: Source = new Source { + override val iter: Iterator[Char] = xml.iterator + override def reportError(pos: Int, msg: String, out: java.io.PrintStream = Console.err): Unit = () + } + + ConstructingParser.fromSource(source, preserveWS = true).content(TopScope) + } + + @Test + def SI6341issue65(): Unit = { + val str: String = """""" + val cpa: ConstructingParser = ConstructingParser.fromSource(Source.fromString(str), preserveWS = true) + val cpadoc: Document = cpa.document() + val ppr: PrettyPrinter = new PrettyPrinter(80,5) + val out: String = ppr.format(cpadoc.docElem) + assertEquals(str, out) + } + + // https://github.com/scala/scala-xml/issues/541 + @Test + def issue541(): Unit = { + val xml: String = + """|""".stripMargin + val parser: ConstructingParser = ConstructingParser.fromSource(Source.fromString(xml), preserveWS = true) + parser.document().docElem // shouldn't crash + } + + @Test(expected = classOf[scala.xml.parsing.FatalError]) + def issue656(): Unit = { + // mismatched quotes should not cause an infinite loop + XhtmlParser(Source.fromString("""")) + assertEquals(expected, xml.XML.loadString("")) + } + + @Test + def oneAndHalfAmp(): Unit = { + val expected: String = "" + assertEquals(expected, xml.XML.loadString("")) + assertEquals(expected, parse("")) + } + + @Test + def doubleAmp(): Unit = { + val expected: String = "" + assertEquals(expected, xml.XML.loadString("")) + assertEquals(expected, parse("")) + } +} diff --git a/jvm/src/test/scala/scala/xml/parsing/XhtmlParserTest.scala b/jvm/src/test/scala/scala/xml/parsing/XhtmlParserTest.scala new file mode 100644 index 000000000..b912217a8 --- /dev/null +++ b/jvm/src/test/scala/scala/xml/parsing/XhtmlParserTest.scala @@ -0,0 +1,61 @@ +package scala.xml +package parsing + +import scala.io.Source + +import org.junit.Test +import org.junit.Assert.assertEquals + +class XhtmlParserTest { + + @Test + def issue259(): Unit = { + val xml: String = + """| + | + | + | + | + | + |

Text

+ | + |""".stripMargin + + val expected: Elem = + + + + +

Text

+ + + + assertEquals(expected, XhtmlParser(Source.fromString(xml)).theSeq) + } + + @Test + def html4Strict(): Unit = { + val xml: String = + """| + | + | + | Title + | + | + |

Text

+ | + |""".stripMargin + + val expected: Elem = + + Title + + +

Text

+ + + + assertEquals(expected, XhtmlParser(Source.fromString(xml)).theSeq) + } +} diff --git a/project/build.properties b/project/build.properties index 8cbb5226c..01a16ed14 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=0.13.0 \ No newline at end of file +sbt.version=1.11.7 diff --git a/project/plugins.sbt b/project/plugins.sbt new file mode 100644 index 000000000..c1bb697f9 --- /dev/null +++ b/project/plugins.sbt @@ -0,0 +1,5 @@ +addSbtPlugin("org.scala-lang.modules" % "sbt-scala-module" % "3.4.0") +addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.3.2") +addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % "1.3.2") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.20.1") +addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.9") diff --git a/shared/src/main/scala-2.12/scala/xml/ScalaVersionSpecific.scala b/shared/src/main/scala-2.12/scala/xml/ScalaVersionSpecific.scala new file mode 100644 index 000000000..71f130031 --- /dev/null +++ b/shared/src/main/scala-2.12/scala/xml/ScalaVersionSpecific.scala @@ -0,0 +1,44 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.xml + +import scala.collection.{SeqLike, mutable} +import scala.collection.generic.CanBuildFrom + +private[xml] object ScalaVersionSpecific { + import NodeSeq.Coll + type CBF[-From, -A, +C] = CanBuildFrom[From, A, C] + object NodeSeqCBF extends CanBuildFrom[Coll, Node, NodeSeq] { + override def apply(from: Coll): mutable.Builder[Node, NodeSeq] = NodeSeq.newBuilder + override def apply(): mutable.Builder[Node, NodeSeq] = NodeSeq.newBuilder + } + type SeqOfNode = scala.collection.Seq[Node] + type SeqOfText = scala.collection.Seq[Text] +} + +private[xml] trait ScalaVersionSpecificNodeSeq extends SeqLike[Node, NodeSeq] { self: NodeSeq => + /** Creates a list buffer as builder for this class */ + override protected[this] def newBuilder: mutable.Builder[Node, NodeSeq] = NodeSeq.newBuilder +} + +private[xml] trait ScalaVersionSpecificNodeBuffer { self: NodeBuffer => + override def stringPrefix: String = "NodeBuffer" +} + +private[xml] trait ScalaVersionSpecificNode { self: Node => } + +private[xml] trait ScalaVersionSpecificMetaData { self: MetaData => } + +private[xml] trait ScalaVersionSpecificTextBuffer { self: TextBuffer => } + +private[xml] trait ScalaVersionSpecificUtility { self: Utility.type => } diff --git a/shared/src/main/scala-2.13+/scala/xml/ScalaVersionSpecific.scala b/shared/src/main/scala-2.13+/scala/xml/ScalaVersionSpecific.scala new file mode 100644 index 000000000..49da95b8b --- /dev/null +++ b/shared/src/main/scala-2.13+/scala/xml/ScalaVersionSpecific.scala @@ -0,0 +1,82 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.xml + +import scala.collection.immutable.StrictOptimizedSeqOps +import scala.collection.{View, SeqOps, IterableOnce, immutable, mutable} +import scala.collection.BuildFrom +import scala.collection.mutable.Builder + +private[xml] object ScalaVersionSpecific { + import NodeSeq.Coll + type CBF[-From, -A, +C] = BuildFrom[From, A, C] + object NodeSeqCBF extends BuildFrom[Coll, Node, NodeSeq] { + def newBuilder(from: Coll): Builder[Node, NodeSeq] = NodeSeq.newBuilder + def fromSpecific(from: Coll)(it: IterableOnce[Node]): NodeSeq = (NodeSeq.newBuilder ++= from).result() + } + type SeqOfNode = scala.collection.immutable.Seq[Node] + type SeqOfText = scala.collection.immutable.Seq[Text] +} + +private[xml] trait ScalaVersionSpecificNodeSeq + extends SeqOps[Node, immutable.Seq, NodeSeq] + with StrictOptimizedSeqOps[Node, immutable.Seq, NodeSeq] { self: NodeSeq => + override def fromSpecific(coll: IterableOnce[Node]): NodeSeq = (NodeSeq.newBuilder ++= coll).result() + override def newSpecificBuilder: mutable.Builder[Node, NodeSeq] = NodeSeq.newBuilder + override def empty: NodeSeq = NodeSeq.Empty + def concat(suffix: IterableOnce[Node]): NodeSeq = + fromSpecific(iterator ++ suffix.iterator) + @inline final def ++ (suffix: Seq[Node]): NodeSeq = concat(suffix) + def appended(base: Node): NodeSeq = + fromSpecific(new View.Appended(this, base)) + def appendedAll(suffix: IterableOnce[Node]): NodeSeq = + concat(suffix) + def prepended(base: Node): NodeSeq = + fromSpecific(new View.Prepended(base, this)) + def prependedAll(prefix: IterableOnce[Node]): NodeSeq = + fromSpecific(prefix.iterator ++ iterator) + def map(f: Node => Node): NodeSeq = + fromSpecific(new View.Map(this, f)) + def flatMap(f: Node => IterableOnce[Node]): NodeSeq = + fromSpecific(new View.FlatMap(this, f)) + + def theSeq: scala.collection.Seq[Node] +} + +private[xml] trait ScalaVersionSpecificNodeBuffer { self: NodeBuffer => + override def className: String = "NodeBuffer" +} + +private[xml] trait ScalaVersionSpecificNode { self: Node => + // These methods are overridden in Node with return type `immutable.Seq`. The declarations here result + // in a bridge method in `Node` with result type `collection.Seq` which is needed for binary compatibility. + def child: scala.collection.Seq[Node] + def nonEmptyChildren: scala.collection.Seq[Node] +} + +private[xml] trait ScalaVersionSpecificMetaData { self: MetaData => + def apply(key: String): scala.collection.Seq[Node] + def apply(namespace_uri: String, owner: Node, key: String): scala.collection.Seq[Node] + def apply(namespace_uri: String, scp: NamespaceBinding, k: String): scala.collection.Seq[Node] + + def value: scala.collection.Seq[Node] +} + +private[xml] trait ScalaVersionSpecificTextBuffer { self: TextBuffer => + def toText: scala.collection.Seq[Text] +} + +private[xml] trait ScalaVersionSpecificUtility { self: Utility.type => + def trimProper(x: Node): scala.collection.Seq[Node] + def parseAttributeValue(value: String): scala.collection.Seq[Node] +} diff --git a/shared/src/main/scala-2/scala/xml/ScalaVersionSpecificReturnTypes.scala b/shared/src/main/scala-2/scala/xml/ScalaVersionSpecificReturnTypes.scala new file mode 100644 index 000000000..6d40d40fb --- /dev/null +++ b/shared/src/main/scala-2/scala/xml/ScalaVersionSpecificReturnTypes.scala @@ -0,0 +1,35 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.xml + +/* + Unlike other Scala-version-specific things, this class is not filling any gaps in capabilities + between different versions of Scala; instead, it mostly documents the types that different versions of the + Scala compiler inferred in the unfortunate absence of the explicit type annotations. + What should have been specified explicitly is given in the comments; + next time we break binary compatibility the types should be changed in the code and this class removed. + */ +private[xml] object ScalaVersionSpecificReturnTypes { // should be + type ExternalIDAttribute = MetaData // Null.type + type NoExternalIDId = scala.Null + type NodeNoAttributes = MetaData // Null.type + type NullFilter = MetaData // Null.type + type NullGetNamespace = scala.Null + type NullNext = scala.Null + type NullKey = scala.Null + type NullValue = scala.Null + type NullApply3 = scala.Null + type NullRemove = Null.type + type SpecialNodeChild = Nil.type + type GroupChild = Nothing +} diff --git a/shared/src/main/scala-3/scala/xml/ScalaVersionSpecificReturnTypes.scala b/shared/src/main/scala-3/scala/xml/ScalaVersionSpecificReturnTypes.scala new file mode 100644 index 000000000..e6dce41cc --- /dev/null +++ b/shared/src/main/scala-3/scala/xml/ScalaVersionSpecificReturnTypes.scala @@ -0,0 +1,35 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.xml + +/* + Unlike other Scala-version-specific things, this class is not filling any gaps in capabilities + between different versions of Scala; instead, it mostly documents the types that different versions of the + Scala compiler inferred in the unfortunate absence of the explicit type annotations. + What should have been specified explicitly is given in the comments; + next time we break binary compatibility the types should be changed in the code and this class removed. + */ +private[xml] object ScalaVersionSpecificReturnTypes { // should be + type ExternalIDAttribute = MetaData // Null.type + type NoExternalIDId = String // scala.Null + type NodeNoAttributes = MetaData // Null.type + type NullFilter = MetaData // Null.type + type NullGetNamespace = String // scala.Null + type NullNext = MetaData // scala.Null + type NullKey = String // scala.Null + type NullValue = scala.collection.immutable.Seq[Node] // scala.Null + type NullApply3 = scala.collection.immutable.Seq[Node] // scala.Null + type NullRemove = MetaData // Null.type + type SpecialNodeChild = scala.collection.immutable.Seq[Node] // Nil.type + type GroupChild = scala.collection.immutable.Seq[Node] // Nothing +} diff --git a/shared/src/main/scala/scala/xml/Atom.scala b/shared/src/main/scala/scala/xml/Atom.scala new file mode 100644 index 000000000..b6aa2fb5a --- /dev/null +++ b/shared/src/main/scala/scala/xml/Atom.scala @@ -0,0 +1,54 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml + +import scala.collection.Seq + +/** + * The class `Atom` provides an XML node for text (`PCDATA`). + * It is used in both non-bound and bound XML representations. + * + * @author Burak Emir + * @param data the text contained in this node, may not be `'''null'''`. + */ +class Atom[+A](val data: A) extends SpecialNode with Serializable { + if (data == null) + throw new IllegalArgumentException(s"cannot construct ${getClass.getSimpleName} with null") + + override protected def basisForHashCode: Seq[Any] = Seq(data) + + override def strict_==(other: Equality): Boolean = other match { + case x: Atom[?] => data == x.data + case _ => false + } + + override def canEqual(other: Any): Boolean = other match { + case _: Atom[?] => true + case _ => false + } + + final override def doCollectNamespaces: Boolean = false + final override def doTransform: Boolean = false + + override def label: String = "#PCDATA" + + /** + * Returns text, with some characters escaped according to the XML + * specification. + */ + override def buildString(sb: StringBuilder): StringBuilder = + Utility.escape(data.toString, sb) + + override def text: String = data.toString +} diff --git a/src/main/scala/scala/xml/Attribute.scala b/shared/src/main/scala/scala/xml/Attribute.scala similarity index 56% rename from src/main/scala/scala/xml/Attribute.scala rename to shared/src/main/scala/scala/xml/Attribute.scala index d3a89d6fb..1c7c35750 100644 --- a/src/main/scala/scala/xml/Attribute.scala +++ b/shared/src/main/scala/scala/xml/Attribute.scala @@ -1,23 +1,28 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ package scala package xml +import scala.collection.Seq + /** * This singleton object contains the `apply` and `unapply` methods for * convenient construction and deconstruction. * * @author Burak Emir - * @version 1.0 */ object Attribute { - def unapply(x: Attribute) = x match { + def unapply(x: Attribute): Option[(String, Seq[Node], MetaData)] = x match { case PrefixedAttribute(_, key, value, next) => Some((key, value, next)) case UnprefixedAttribute(key, value, next) => Some((key, value, next)) case _ => None @@ -47,57 +52,54 @@ object Attribute { * [[scala.xml.PrefixedAttribute]] and [[scala.xml.UnprefixedAttribute]]. * * @author Burak Emir - * @version 1.0 */ -abstract trait Attribute extends MetaData { +trait Attribute extends MetaData with ScalaVersionSpecificMetaData { def pre: String // will be null if unprefixed - val key: String - val value: Seq[Node] - val next: MetaData + override val key: String + override val value: ScalaVersionSpecific.SeqOfNode + override val next: MetaData - def apply(key: String): Seq[Node] - def apply(namespace: String, scope: NamespaceBinding, key: String): Seq[Node] - def copy(next: MetaData): Attribute + override def apply(key: String): ScalaVersionSpecific.SeqOfNode + override def apply(namespace: String, scope: NamespaceBinding, key: String): ScalaVersionSpecific.SeqOfNode + override def copy(next: MetaData): Attribute - def remove(key: String) = + override def remove(key: String): MetaData = if (!isPrefixed && this.key == key) next - else copy(next remove key) + else copy(next.remove(key)) - def remove(namespace: String, scope: NamespaceBinding, key: String) = - if (this.key == key && (scope getURI pre) == namespace) next + override def remove(namespace: String, scope: NamespaceBinding, key: String): MetaData = + if (this.key == key && scope.getURI(pre) == namespace) next else copy(next.remove(namespace, scope, key)) - def isPrefixed: Boolean = pre != null + override def isPrefixed: Boolean = pre != null - def getNamespace(owner: Node): String + override def getNamespace(owner: Node): String - def wellformed(scope: NamespaceBinding): Boolean = { - val arg = if (isPrefixed) scope getURI pre else null - (next(arg, scope, key) == null) && (next wellformed scope) + override def wellformed(scope: NamespaceBinding): Boolean = { + val arg: String = if (isPrefixed) scope.getURI(pre) else null + (next(arg, scope, key) == null) && next.wellformed(scope) } /** Returns an iterator on attributes */ - override def iterator: Iterator[MetaData] = { + override def iterator: Iterator[MetaData] = if (value == null) next.iterator else Iterator.single(this) ++ next.iterator - } - override def size: Int = { + override def size: Int = if (value == null) next.size else 1 + next.size - } /** * Appends string representation of only this attribute to stringbuffer. */ - protected def toString1(sb: StringBuilder) { + override protected def toString1(sb: StringBuilder): Unit = { if (value == null) return if (isPrefixed) - sb append pre append ':' + sb.append(s"$pre:") - sb append key append '=' - val sb2 = new StringBuilder() + sb.append(s"$key=") + val sb2: StringBuilder = new StringBuilder() Utility.sequenceToXML(value, TopScope, sb2, stripComments = true) Utility.appendQuoted(sb2.toString, sb) } diff --git a/shared/src/main/scala/scala/xml/Comment.scala b/shared/src/main/scala/scala/xml/Comment.scala new file mode 100644 index 000000000..6c27a0252 --- /dev/null +++ b/shared/src/main/scala/scala/xml/Comment.scala @@ -0,0 +1,43 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml + +/** + * The class `Comment` implements an XML node for comments. + * + * @author Burak Emir + * @param commentText the text contained in this node, may not contain "--" + * and the final character may not be `-` to prevent a closing span of `-->` + * which is invalid. [[https://www.w3.org/TR/xml11//#IDA5CES]] + */ +// Note: used by the Scala compiler. +case class Comment(commentText: String) extends SpecialNode { + + override def label: String = "#REM" + override def text: String = "" + final override def doCollectNamespaces: Boolean = false + final override def doTransform: Boolean = false + + if (commentText.contains("--")) + throw new IllegalArgumentException(s"""text contains "--"""") + + if (commentText.nonEmpty && commentText.charAt(commentText.length - 1) == '-') + throw new IllegalArgumentException("The final character of a XML comment may not be '-'. See https://www.w3.org/TR/xml11//#IDA5CES") + + /** + * Appends "" to this string buffer. + */ + override def buildString(sb: StringBuilder): StringBuilder = + sb.append(s"") +} diff --git a/src/main/scala/scala/xml/Document.scala b/shared/src/main/scala/scala/xml/Document.scala similarity index 78% rename from src/main/scala/scala/xml/Document.scala rename to shared/src/main/scala/scala/xml/Document.scala index 3410b15d7..8fe1c6ae4 100644 --- a/src/main/scala/scala/xml/Document.scala +++ b/shared/src/main/scala/scala/xml/Document.scala @@ -1,14 +1,20 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ package scala package xml +import scala.collection.Seq + /** * A document information item (according to InfoSet spec). The comments * are copied from the Infoset spec, only augmented with some information @@ -16,10 +22,9 @@ package xml * Also plays the role of an `XMLEvent` for pull parsing. * * @author Burak Emir - * @version 1.0, 26/04/2005 */ @SerialVersionUID(-2289320563321795109L) -class Document extends NodeSeq with pull.XMLEvent with Serializable { +class Document extends NodeSeq with Serializable { /** * An ordered list of child information items, in document @@ -31,7 +36,7 @@ class Document extends NodeSeq with pull.XMLEvent with Serializable { * excluded. If there is a document type declaration, the list also * contains a document type declaration information item. */ - var children: Seq[Node] = _ + var children: Seq[Node] = _ // effectively an `immutable.Seq`, not changed due to binary compatibility /** The element information item corresponding to the document element. */ var docElem: Node = _ @@ -87,13 +92,13 @@ class Document extends NodeSeq with pull.XMLEvent with Serializable { * then certain properties (indicated in their descriptions below) may * be unknown. If it is true, those properties are never unknown. */ - var allDeclarationsProcessed = false + var allDeclarationsProcessed: Boolean = false // methods for NodeSeq - def theSeq: Seq[Node] = this.docElem + override def theSeq: ScalaVersionSpecific.SeqOfNode = this.docElem - override def canEqual(other: Any) = other match { + override def canEqual(other: Any): Boolean = other match { case _: Document => true case _ => false } diff --git a/shared/src/main/scala/scala/xml/Elem.scala b/shared/src/main/scala/scala/xml/Elem.scala new file mode 100755 index 000000000..6b77f1642 --- /dev/null +++ b/shared/src/main/scala/scala/xml/Elem.scala @@ -0,0 +1,113 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml + +import scala.collection.Seq + +/** + * This singleton object contains the `apply` and `unapplySeq` methods for + * convenient construction and deconstruction. It is possible to deconstruct + * any `Node` instance (that is not a `SpecialNode` or a `Group`) using the + * syntax `case Elem(prefix, label, attribs, scope, child @ _*) => ...` + */ +// Note: used by the Scala compiler. +object Elem { + + def apply(prefix: String, label: String, attributes: MetaData, scope: NamespaceBinding, minimizeEmpty: Boolean, child: Node*): Elem = + new Elem(prefix, label, attributes, scope, minimizeEmpty, child: _*) + + def unapplySeq(n: Node): Option[(String, String, MetaData, NamespaceBinding, ScalaVersionSpecific.SeqOfNode)] = + n match { + case _: SpecialNode | _: Group => None + case _ => Some((n.prefix, n.label, n.attributes, n.scope, n.child)) + } +} + +/** + * An immutable data object representing an XML element. + * + * Child elements can be other [[Elem]]s or any one of the other [[Node]] types. + * + * XML attributes are implemented with the [[scala.xml.MetaData]] base + * class. + * + * Optional XML namespace scope is represented by + * [[scala.xml.NamespaceBinding]]. + * + * @param prefix namespace prefix (may be null, but not the empty string) + * @param label the element name + * @param attributes1 the attribute map + * @param scope the scope containing the namespace bindings + * @param minimizeEmpty `true` if this element should be serialized as minimized (i.e. "<el/>") when + * empty; `false` if it should be written out in long form. + * @param child the children of this node + */ +// Note: used by the Scala compiler. +class Elem( + override val prefix: String, + override val label: String, + attributes1: MetaData, + override val scope: NamespaceBinding, + val minimizeEmpty: Boolean, + override val child: Node* +) extends Node with Serializable { + + final override def doCollectNamespaces: Boolean = true + final override def doTransform: Boolean = true + + override val attributes: MetaData = MetaData.normalize(attributes1, scope) + + if (prefix == "") + throw new IllegalArgumentException("prefix of zero length, use null instead") + + if (scope == null) + throw new IllegalArgumentException("scope is null, use scala.xml.TopScope for empty scope") + + //@todo: copy the children, + // setting namespace scope if necessary + // cleaning adjacent text nodes if necessary + + override protected def basisForHashCode: Seq[Any] = + prefix :: label :: attributes :: child.toList + + /** + * Returns a new element with updated attributes, resolving namespace uris + * from this element's scope. See MetaData.update for details. + * + * @param updates MetaData with new and updated attributes + * @return a new symbol with updated attributes + */ + final def %(updates: MetaData): Elem = + copy(attributes = MetaData.update(attributes, scope, updates)) + + /** + * Returns a copy of this element with any supplied arguments replacing + * this element's value for that field. + * + * @return a new symbol with updated attributes + */ + def copy( + prefix: String = this.prefix, + label: String = this.label, + attributes: MetaData = this.attributes, + scope: NamespaceBinding = this.scope, + minimizeEmpty: Boolean = this.minimizeEmpty, + child: Seq[Node] = this.child + ): Elem = Elem(prefix, label, attributes, scope, minimizeEmpty, child.toSeq: _*) + + /** + * Returns concatenation of `text(n)` for each child `n`. + */ + override def text: String = child.map(_.text).mkString +} diff --git a/shared/src/main/scala/scala/xml/EntityRef.scala b/shared/src/main/scala/scala/xml/EntityRef.scala new file mode 100644 index 000000000..5b5b1f2a5 --- /dev/null +++ b/shared/src/main/scala/scala/xml/EntityRef.scala @@ -0,0 +1,45 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml + +/** + * The class `EntityRef` implements an XML node for entity references. + * + * @author Burak Emir + * @param entityName the name of the entity reference, for example `amp`. + */ +// Note: used by the Scala compiler. +case class EntityRef(entityName: String) extends SpecialNode { + final override def doCollectNamespaces: Boolean = false + final override def doTransform: Boolean = false + override def label: String = "#ENTITY" + + override def text: String = entityName match { + case "lt" => "<" + case "gt" => ">" + case "amp" => "&" + case "apos" => "'" + case "quot" => "\"" + case _ => Utility.sbToString(buildString) + } + + /** + * Appends `"& entityName;"` to this string buffer. + * + * @param sb the string buffer. + * @return the modified string buffer `sb`. + */ + override def buildString(sb: StringBuilder): StringBuilder = + sb.append(s"&$entityName;") +} diff --git a/src/main/scala/scala/xml/Equality.scala b/shared/src/main/scala/scala/xml/Equality.scala similarity index 66% rename from src/main/scala/scala/xml/Equality.scala rename to shared/src/main/scala/scala/xml/Equality.scala index 41c4352a2..d7d612d4c 100644 --- a/src/main/scala/scala/xml/Equality.scala +++ b/shared/src/main/scala/scala/xml/Equality.scala @@ -1,14 +1,20 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ package scala package xml +import scala.collection.Seq + /** * In an attempt to contain the damage being inflicted on consistency by the * ad hoc `equals` methods spread around `xml`, the logic is centralized and @@ -47,7 +53,7 @@ object Equality { * Note - these functions assume strict equality has already failed. */ def compareBlithely(x1: AnyRef, x2: String): Boolean = x1 match { - case x: Atom[_] => x.data == x2 + case x: Atom[?] => x.data == x2 case x: NodeSeq => x.text == x2 case _ => false } @@ -55,31 +61,26 @@ object Equality { case x: NodeSeq if x.length == 1 => x2 == x(0) case _ => false } - def compareBlithely(x1: AnyRef, x2: AnyRef): Boolean = { - if (x1 == null || x2 == null) - return (x1 eq x2) - - x2 match { + def compareBlithely(x1: AnyRef, x2: AnyRef): Boolean = + if (x1 == null || x2 == null) x1 == null && x2 == null else x2 match { case s: String => compareBlithely(x1, s) case n: Node => compareBlithely(x1, n) case _ => false } - } } -import Equality._ trait Equality extends scala.Equals { protected def basisForHashCode: Seq[Any] def strict_==(other: Equality): Boolean - def strict_!=(other: Equality) = !strict_==(other) + def strict_!=(other: Equality): Boolean = !strict_==(other) /** * We insist we're only equal to other `xml.Equality` implementors, * which heads off a lot of inconsistency up front. */ override def canEqual(other: Any): Boolean = other match { - case x: Equality => true + case _: Equality => true case _ => false } @@ -90,23 +91,23 @@ trait Equality extends scala.Equals { * are final since clearly individual classes cannot be trusted * to maintain a semblance of order. */ - override def hashCode() = basisForHashCode.## - override def equals(other: Any) = doComparison(other, blithe = false) - final def xml_==(other: Any) = doComparison(other, blithe = true) - final def xml_!=(other: Any) = !xml_==(other) + override def hashCode: Int = basisForHashCode.## + override def equals(other: Any): Boolean = doComparison(other, blithe = false) + final def xml_==(other: Any): Boolean = doComparison(other, blithe = true) + final def xml_!=(other: Any): Boolean = !xml_==(other) /** * The "blithe" parameter expresses the caller's unconcerned attitude * regarding the usual constraints on equals. The method is thereby * given carte blanche to declare any two things equal. */ - private def doComparison(other: Any, blithe: Boolean) = { - val strictlyEqual = other match { - case x: AnyRef if this eq x => true - case x: Equality => (x canEqual this) && (this strict_== x) - case _ => false + private def doComparison(other: Any, blithe: Boolean): Boolean = { + val strictlyEqual: Boolean = other match { + case x: AnyRef if this.eq(x) => true + case x: Equality => x.canEqual(this) && this.strict_==(x) + case _ => false } - strictlyEqual || (blithe && compareBlithely(this, asRef(other))) + strictlyEqual || (blithe && Equality.compareBlithely(this, Equality.asRef(other))) } } diff --git a/shared/src/main/scala/scala/xml/Group.scala b/shared/src/main/scala/scala/xml/Group.scala new file mode 100644 index 000000000..4498dbef5 --- /dev/null +++ b/shared/src/main/scala/scala/xml/Group.scala @@ -0,0 +1,54 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml + +import scala.collection.Seq + +/** + * A hack to group XML nodes in one node for output. + * + * @author Burak Emir + */ +// Note: used by the Scala compiler. +final case class Group(nodes: Seq[Node]) extends Node { + // Ideally, the `immutable.Seq` would be stored as a field. + // But evolving the case class and remaining binary compatible is very difficult + // Since `Group` is used rarely, call `toSeq` on the field. + // In practice, it should not matter - the `nodes` field anyway contains an `immutable.Seq`. + override def theSeq: ScalaVersionSpecific.SeqOfNode = nodes.toSeq + + override def canEqual(other: Any): Boolean = other match { + case _: Group => true + case _ => false + } + + override def strict_==(other: Equality): Boolean = other match { + case Group(xs) => nodes.sameElements(xs) + case _ => false + } + + override protected def basisForHashCode: Seq[Node] = nodes + + /** + * Since Group is very much a hack it throws an exception if you + * try to do anything with it. + */ + private def fail(msg: String): Nothing = throw new UnsupportedOperationException(s"class Group does not support method '$msg'") + + override def label: Nothing = fail("label") + override def attributes: Nothing = fail("attributes") + override def namespace: Nothing = fail("namespace") + override def child: ScalaVersionSpecificReturnTypes.GroupChild = fail("child") + def buildString(sb: StringBuilder): Nothing = fail("toString(StringBuilder)") +} diff --git a/shared/src/main/scala/scala/xml/MalformedAttributeException.scala b/shared/src/main/scala/scala/xml/MalformedAttributeException.scala new file mode 100644 index 000000000..5a9fa766d --- /dev/null +++ b/shared/src/main/scala/scala/xml/MalformedAttributeException.scala @@ -0,0 +1,16 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml + +case class MalformedAttributeException(msg: String) extends RuntimeException(msg) diff --git a/src/main/scala/scala/xml/MetaData.scala b/shared/src/main/scala/scala/xml/MetaData.scala similarity index 71% rename from src/main/scala/scala/xml/MetaData.scala rename to shared/src/main/scala/scala/xml/MetaData.scala index cda29f801..6bdd080f0 100644 --- a/src/main/scala/scala/xml/MetaData.scala +++ b/shared/src/main/scala/scala/xml/MetaData.scala @@ -1,22 +1,23 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ package scala package xml import Utility.sbToString import scala.annotation.tailrec -import scala.collection.{ AbstractIterable, Iterator } +import scala.collection.AbstractIterable +import scala.collection.Seq -/** - * Copyright 2008 Google Inc. All Rights Reserved. - * @author Burak Emir - */ object MetaData { /** * appends all attributes from new_tail to attribs, without attempting to @@ -26,29 +27,36 @@ object MetaData { * * Duplicates can be removed with `normalize`. */ - @tailrec // temporarily marked final so it will compile under -Xexperimental - final def concatenate(attribs: MetaData, new_tail: MetaData): MetaData = - if (attribs eq Null) new_tail - else concatenate(attribs.next, attribs copy new_tail) + @tailrec + def concatenate(attribs: MetaData, new_tail: MetaData): MetaData = + if (attribs.isNull) new_tail + else concatenate(attribs.next, attribs.copy(new_tail)) /** * returns normalized MetaData, with all duplicates removed and namespace prefixes resolved to * namespace URIs via the given scope. */ def normalize(attribs: MetaData, scope: NamespaceBinding): MetaData = { - def iterate(md: MetaData, normalized_attribs: MetaData, set: Set[String]): MetaData = { - lazy val key = getUniversalKey(md, scope) - if (md eq Null) normalized_attribs - else if ((md.value eq null) || set(key)) iterate(md.next, normalized_attribs, set) - else md copy iterate(md.next, normalized_attribs, set + key) - } + def iterate(md: MetaData, normalized_attribs: MetaData, set: Set[String]): MetaData = + if (md.isNull) { + normalized_attribs + } else if (md.value == null) + iterate(md.next, normalized_attribs, set) + else { + val key: String = getUniversalKey(md, scope) + if (set(key)) + iterate(md.next, normalized_attribs, set) + else + md.copy(iterate(md.next, normalized_attribs, set + key)) + } + iterate(attribs, Null, Set()) } /** * returns key if md is unprefixed, pre+key is md is prefixed */ - def getUniversalKey(attrib: MetaData, scope: NamespaceBinding) = attrib match { + def getUniversalKey(attrib: MetaData, scope: NamespaceBinding): String = attrib match { case prefixed: PrefixedAttribute => scope.getURI(prefixed.pre) + prefixed.key case unprefixed: UnprefixedAttribute => unprefixed.key } @@ -66,19 +74,20 @@ object MetaData { * attributes. Every instance of this class is either * - an instance of `UnprefixedAttribute key,value` or * - an instance of `PrefixedAttribute namespace_prefix,key,value` or - * - `Null, the empty attribute list. + * - `Null`, the empty attribute list. * * Namespace URIs are obtained by using the namespace scope of the element * owning this attribute (see `getNamespace`). - * - * Copyright 2008 Google Inc. All Rights Reserved. - * @author Burak Emir */ +// Note: used by the Scala compiler. abstract class MetaData extends AbstractIterable[MetaData] with Iterable[MetaData] with Equality - with Serializable { + with Serializable + with ScalaVersionSpecificMetaData +{ + private[xml] def isNull: Boolean = this.eq(Null) /** * Updates this MetaData with the MetaData given as argument. All attributes that occur in updates @@ -98,7 +107,7 @@ abstract class MetaData * @param key * @return value as Seq[Node] if key is found, null otherwise */ - def apply(key: String): Seq[Node] + def apply(key: String): ScalaVersionSpecific.SeqOfNode /** * convenience method, same as `apply(namespace, owner.scope, key)`. @@ -107,7 +116,7 @@ abstract class MetaData * @param owner the element owning this attribute list * @param key the attribute key */ - final def apply(namespace_uri: String, owner: Node, key: String): Seq[Node] = + final def apply(namespace_uri: String, owner: Node, key: String): ScalaVersionSpecific.SeqOfNode = apply(namespace_uri, owner.scope, key) /** @@ -118,7 +127,7 @@ abstract class MetaData * @param k to be looked for * @return value as Seq[Node] if key is found, null otherwise */ - def apply(namespace_uri: String, scp: NamespaceBinding, k: String): Seq[Node] + def apply(namespace_uri: String, scp: NamespaceBinding, k: String): ScalaVersionSpecific.SeqOfNode /** * returns a copy of this MetaData item with next field set to argument. @@ -128,7 +137,7 @@ abstract class MetaData /** if owner is the element of this metadata item, returns namespace */ def getNamespace(owner: Node): String - def hasNext = (Null != next) + def hasNext: Boolean = Null != next def length: Int = length(0) @@ -136,33 +145,38 @@ abstract class MetaData def isPrefixed: Boolean - override def canEqual(other: Any) = other match { + override def canEqual(other: Any): Boolean = other match { case _: MetaData => true case _ => false } - override def strict_==(other: Equality) = other match { + override def strict_==(other: Equality): Boolean = other match { case m: MetaData => this.asAttrMap == m.asAttrMap case _ => false } - protected def basisForHashCode: Seq[Any] = List(this.asAttrMap) + override protected def basisForHashCode: Seq[Any] = List(this.asAttrMap) /** filters this sequence of meta data */ override def filter(f: MetaData => Boolean): MetaData = - if (f(this)) copy(next filter f) - else next filter f + if (f(this)) copy(next.filter(f)) + else next.filter(f) + + def reverse: MetaData = + foldLeft(Null: MetaData) { (x, xs) => + xs.copy(x) + } /** returns key of this MetaData item */ def key: String /** returns value of this MetaData item */ - def value: Seq[Node] + def value: ScalaVersionSpecific.SeqOfNode /** * Returns a String containing "prefix:key" if the first key is * prefixed, and "key" otherwise. */ - def prefixedKey = this match { - case x: Attribute if x.isPrefixed => x.pre + ":" + key + def prefixedKey: String = this match { + case x: Attribute if x.isPrefixed => s"${x.pre}:$key" case _ => key } @@ -170,7 +184,7 @@ abstract class MetaData * Returns a Map containing the attributes stored as key/value pairs. */ def asAttrMap: Map[String, String] = - (iterator map (x => (x.prefixedKey, x.value.text))).toMap + iterator.map(x => (x.prefixedKey, NodeSeq.fromSeq(x.value).text)).toMap /** returns Null or the next MetaData item */ def next: MetaData @@ -181,10 +195,10 @@ abstract class MetaData * @param key * @return value in Some(Seq[Node]) if key is found, None otherwise */ - final def get(key: String): Option[Seq[Node]] = Option(apply(key)) + final def get(key: String): Option[ScalaVersionSpecific.SeqOfNode] = Option(apply(key)) /** same as get(uri, owner.scope, key) */ - final def get(uri: String, owner: Node, key: String): Option[Seq[Node]] = + final def get(uri: String, owner: Node, key: String): Option[ScalaVersionSpecific.SeqOfNode] = get(uri, owner.scope, key) /** @@ -193,22 +207,22 @@ abstract class MetaData * @param uri namespace of key * @param scope a namespace scp (usually of the element owning this attribute list) * @param key to be looked fore - * @return value as Some[Seq[Node]] if key is found, None otherwise + * @return value as `Some[Seq[Node]]` if key is found, None otherwise */ - final def get(uri: String, scope: NamespaceBinding, key: String): Option[Seq[Node]] = + final def get(uri: String, scope: NamespaceBinding, key: String): Option[ScalaVersionSpecific.SeqOfNode] = Option(apply(uri, scope, key)) - protected def toString1(): String = sbToString(toString1) + protected def toString1: String = sbToString(toString1) // appends string representations of single attribute to StringBuilder protected def toString1(sb: StringBuilder): Unit - override def toString(): String = sbToString(buildString) + override def toString: String = sbToString(buildString) def buildString(sb: StringBuilder): StringBuilder = { - sb append ' ' + sb.append(' ') toString1(sb) - next buildString sb + next.buildString(sb) } /** diff --git a/shared/src/main/scala/scala/xml/NamespaceBinding.scala b/shared/src/main/scala/scala/xml/NamespaceBinding.scala new file mode 100644 index 000000000..8efe27d79 --- /dev/null +++ b/shared/src/main/scala/scala/xml/NamespaceBinding.scala @@ -0,0 +1,85 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml + +import scala.collection.Seq + +/** + * The class `NamespaceBinding` represents namespace bindings + * and scopes. The binding for the default namespace is treated as a null + * prefix. the absent namespace is represented with the null uri. Neither + * prefix nor uri may be empty, which is not checked. + * + * @author Burak Emir + */ +// Note: used by the Scala compiler. +@SerialVersionUID(0 - 2518644165573446725L) +case class NamespaceBinding(prefix: String, uri: String, parent: NamespaceBinding) extends AnyRef with Equality { + if (prefix == "") + throw new IllegalArgumentException("zero length prefix not allowed") + + def getURI(prefix: String): String = + if (this.prefix == prefix) uri else parent.getURI(prefix) + + /** + * Returns some prefix that is mapped to the URI. + * + * @param _uri the input URI + * @return the prefix that is mapped to the input URI, or null + * if no prefix is mapped to the URI. + */ + def getPrefix(uri: String): String = + if (uri == this.uri) prefix else parent.getPrefix(uri) + + override def toString: String = Utility.sbToString(buildString(_, TopScope)) + + private def shadowRedefined(stop: NamespaceBinding): NamespaceBinding = { + def prefixList(x: NamespaceBinding): List[String] = + if ((x == null) || x.eq(stop)) Nil + else x.prefix :: prefixList(x.parent) + def fromPrefixList(l: List[String]): NamespaceBinding = l match { + case Nil => stop + case x :: xs => NamespaceBinding(x, this.getURI(x), fromPrefixList(xs)) + } + val ps0: List[String] = prefixList(this).reverse + val ps: List[String] = ps0.distinct + if (ps.size == ps0.size) this + else fromPrefixList(ps) + } + + override def canEqual(other: Any): Boolean = other match { + case _: NamespaceBinding => true + case _ => false + } + + override def strict_==(other: Equality): Boolean = other match { + case x: NamespaceBinding => (prefix == x.prefix) && (uri == x.uri) && (parent == x.parent) + case _ => false + } + + override def basisForHashCode: Seq[Any] = List(prefix, uri, parent) + + def buildString(stop: NamespaceBinding): String = Utility.sbToString(buildString(_, stop)) + + def buildString(sb: StringBuilder, stop: NamespaceBinding): Unit = + shadowRedefined(stop).doBuildString(sb, stop) + + private def doBuildString(sb: StringBuilder, stop: NamespaceBinding): Unit = { + if (List(null, stop, TopScope).contains(this)) return + + val prefixStr: String = if (prefix != null) s":$prefix" else "" + val uriStr: String = if (uri != null) uri else "" + parent.doBuildString(sb.append(s""" xmlns$prefixStr="$uriStr""""), stop) // copy(ignore) + } +} diff --git a/src/main/scala/scala/xml/Node.scala b/shared/src/main/scala/scala/xml/Node.scala similarity index 68% rename from src/main/scala/scala/xml/Node.scala rename to shared/src/main/scala/scala/xml/Node.scala index 737ce2878..d9c41dd1e 100755 --- a/src/main/scala/scala/xml/Node.scala +++ b/shared/src/main/scala/scala/xml/Node.scala @@ -1,39 +1,51 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ package scala package xml +import scala.collection.Seq + /** * This singleton object contains the `unapplySeq` method for * convenient deconstruction. * * @author Burak Emir - * @version 1.0 */ object Node { /** the constant empty attribute sequence */ - final def NoAttributes: MetaData = Null + final def NoAttributes: ScalaVersionSpecificReturnTypes.NodeNoAttributes = Null /** the empty namespace */ - val EmptyNamespace = "" + val EmptyNamespace: String = "" - def unapplySeq(n: Node) = Some((n.label, n.attributes, n.child)) + def unapplySeq(n: Node): Some[(String, MetaData, ScalaVersionSpecific.SeqOfNode)] = + Some((n.label, n.attributes, n.child)) } /** - * An abstract class representing XML with nodes of a labelled tree. + * An abstract class representing XML with nodes of a labeled tree. * This class contains an implementation of a subset of XPath for navigation. * + * - [[scala.xml.Comment]] — XML comment + * - [[scala.xml.Elem]] — XML element + * - [[scala.xml.EntityRef]] — XML entity + * - [[scala.xml.PCData]] — Character data section (CDATA) + * - [[scala.xml.ProcInstr]] — Processing instruction (PI) + * - [[scala.xml.Text]] — Stand-alone parsed character data + * * @author Burak Emir and others - * @version 1.1 */ -abstract class Node extends NodeSeq { +abstract class Node extends NodeSeq with ScalaVersionSpecificNode { /** prefix of this node */ def prefix: String = null @@ -44,11 +56,11 @@ abstract class Node extends NodeSeq { /** * used internally. Atom/Molecule = -1 PI = -2 Comment = -3 EntityRef = -5 */ - def isAtom = this.isInstanceOf[Atom[_]] + def isAtom: Boolean = this.isInstanceOf[Atom[?]] /** The logic formerly found in typeTag$, as best I could infer it. */ - def doCollectNamespaces = true // if (tag >= 0) DO collect namespaces - def doTransform = true // if (tag < 0) DO NOT transform + def doCollectNamespaces: Boolean = true // if (tag >= 0) DO collect namespaces + def doTransform: Boolean = true // if (tag < 0) DO NOT transform /** * method returning the namespace bindings of this node. by default, this @@ -60,7 +72,7 @@ abstract class Node extends NodeSeq { /** * convenience, same as `getNamespace(this.prefix)` */ - def namespace = getNamespace(this.prefix) + def namespace: String = getNamespace(this.prefix) /** * Convenience method, same as `scope.getURI(pre)` but additionally @@ -70,7 +82,7 @@ abstract class Node extends NodeSeq { * @return the namespace if `scope != null` and prefix was * found, else `null` */ - def getNamespace(pre: String): String = if (scope eq null) null else scope.getURI(pre) + def getNamespace(pre: String): String = if (scope == null) null else scope.getURI(pre) /** * Convenience method, looks up an unprefixed attribute in attributes of this node. @@ -80,7 +92,7 @@ abstract class Node extends NodeSeq { * @return value of `UnprefixedAttribute` with given key * in attributes, if it exists, otherwise `null`. */ - final def attribute(key: String): Option[Seq[Node]] = attributes.get(key) + final def attribute(key: String): Option[ScalaVersionSpecific.SeqOfNode] = attributes.get(key) /** * Convenience method, looks up a prefixed attribute in attributes of this node. @@ -91,7 +103,7 @@ abstract class Node extends NodeSeq { * @return value of `PrefixedAttribute` with given namespace * and given key, otherwise `'''null'''`. */ - final def attribute(uri: String, key: String): Option[Seq[Node]] = + final def attribute(uri: String, key: String): Option[ScalaVersionSpecific.SeqOfNode] = attributes.get(uri, this, key) /** @@ -108,12 +120,12 @@ abstract class Node extends NodeSeq { * * @return all children of this node */ - def child: Seq[Node] + def child: ScalaVersionSpecific.SeqOfNode /** * Children which do not stringify to "" (needed for equality) */ - def nonEmptyChildren: Seq[Node] = child filterNot (_.toString == "") + def nonEmptyChildren: ScalaVersionSpecific.SeqOfNode = child.filterNot(_.toString.isEmpty) /** * Descendant axis (all descendants of this node, not including node itself) @@ -123,28 +135,28 @@ abstract class Node extends NodeSeq { child.toList.flatMap { x => x :: x.descendant } /** - * Descendant axis (all descendants of this node, including thisa node) + * Descendant axis (all descendants of this node, including this node) * includes all text nodes, element nodes, comments and processing instructions. */ def descendant_or_self: List[Node] = this :: descendant - override def canEqual(other: Any) = other match { - case x: Group => false - case x: Node => true + override def canEqual(other: Any): Boolean = other match { + case _: Group => false + case _: Node => true case _ => false } override protected def basisForHashCode: Seq[Any] = prefix :: label :: attributes :: nonEmptyChildren.toList - override def strict_==(other: Equality) = other match { + override def strict_==(other: Equality): Boolean = other match { case _: Group => false case x: Node => (prefix == x.prefix) && (label == x.label) && (attributes == x.attributes) && // (scope == x.scope) // note - original code didn't compare scopes so I left it as is. - (nonEmptyChildren sameElements x.nonEmptyChildren) + nonEmptyChildren.sameElements(x.nonEmptyChildren) case _ => false } @@ -154,7 +166,7 @@ abstract class Node extends NodeSeq { /** * returns a sequence consisting of only this node */ - def theSeq: Seq[Node] = this :: Nil + override def theSeq: ScalaVersionSpecific.SeqOfNode = this :: Nil /** * String representation of this node @@ -167,23 +179,18 @@ abstract class Node extends NodeSeq { /** * Same as `toString('''false''')`. */ - override def toString(): String = buildString(stripComments = false) + override def toString: String = buildString(stripComments = false) /** * Appends qualified name of this node to `StringBuilder`. */ - def nameToString(sb: StringBuilder): StringBuilder = { - if (null != prefix) { - sb append prefix - sb append ':' - } - sb append label - } + def nameToString(sb: StringBuilder): StringBuilder = + sb.append(s"${if (prefix == null) "" else s"$prefix:"}$label") /** * Returns a type symbol (e.g. DTD, XSD), default `'''null'''`. */ - def xmlType(): TypeSymbol = null + def xmlType: TypeSymbol = null /** * Returns a text representation of this node. Note that this is not equivalent to diff --git a/src/main/scala/scala/xml/NodeBuffer.scala b/shared/src/main/scala/scala/xml/NodeBuffer.scala similarity index 60% rename from src/main/scala/scala/xml/NodeBuffer.scala rename to shared/src/main/scala/scala/xml/NodeBuffer.scala index 39da2a291..7a26caa67 100644 --- a/src/main/scala/scala/xml/NodeBuffer.scala +++ b/shared/src/main/scala/scala/xml/NodeBuffer.scala @@ -1,10 +1,14 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ package scala package xml @@ -18,10 +22,9 @@ package xml * Calling the hashcode function will result in a runtime error. * * @author Burak Emir - * @version 1.0 */ -class NodeBuffer extends scala.collection.mutable.ArrayBuffer[Node] { - +// Note: used by the Scala compiler. +class NodeBuffer extends scala.collection.mutable.ArrayBuffer[Node] with ScalaVersionSpecificNodeBuffer { /** * Append given object to this buffer, returns reference on this * `NodeBuffer` for convenience. Some rules apply: @@ -33,13 +36,13 @@ class NodeBuffer extends scala.collection.mutable.ArrayBuffer[Node] { * @param o converts to an xml node and adds to this node buffer * @return this nodebuffer */ - def &+(o: Any): NodeBuffer = { + def &+(o: Any): this.type = { o match { case null | _: Unit | Text("") => // ignore - case it: Iterator[_] => it foreach &+ + case it: Iterator[?] => it.foreach(&+) case n: Node => super.+=(n) - case ns: Iterable[_] => this &+ ns.iterator - case ns: Array[_] => this &+ ns.iterator + case ns: Iterable[?] => this &+ ns.iterator + case ns: Array[?] => this &+ ns.iterator case d => super.+=(new Atom(d)) } this diff --git a/shared/src/main/scala/scala/xml/NodeSeq.scala b/shared/src/main/scala/scala/xml/NodeSeq.scala new file mode 100644 index 000000000..39fdbf865 --- /dev/null +++ b/shared/src/main/scala/scala/xml/NodeSeq.scala @@ -0,0 +1,168 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml + +import scala.collection.{mutable, immutable, AbstractSeq} +import ScalaVersionSpecific.CBF +import scala.language.implicitConversions +import scala.collection.Seq + +/** + * This object ... + * + * @author Burak Emir + */ +object NodeSeq { + final val Empty: NodeSeq = fromSeq(Nil) + def fromSeq(s: Seq[Node]): NodeSeq = new NodeSeq { + override val theSeq: ScalaVersionSpecific.SeqOfNode = s match { + case ns: ScalaVersionSpecific.SeqOfNode => ns + case _ => s.toVector + } + } + + // --- + // For 2.11 / 2.12 only. Moving the implicit to a parent trait of `object NodeSeq` and keeping it + // in ScalaVersionSpecific doesn't work because the implicit becomes less specific, which leads to + // ambiguities. + type Coll = NodeSeq + implicit def canBuildFrom: CBF[Coll, Node, NodeSeq] = ScalaVersionSpecific.NodeSeqCBF + // --- + + def newBuilder: mutable.Builder[Node, NodeSeq] = new mutable.ListBuffer[Node].mapResult(fromSeq) + implicit def seqToNodeSeq(s: Seq[Node]): NodeSeq = fromSeq(s) +} + +/** + * This class implements a wrapper around `Seq[Node]` that adds XPath + * and comprehension methods. + * + * @author Burak Emir + */ +abstract class NodeSeq extends AbstractSeq[Node] with immutable.Seq[Node] with ScalaVersionSpecificNodeSeq with Equality with Serializable { + def theSeq: ScalaVersionSpecific.SeqOfNode + override def length: Int = theSeq.length + override def iterator: Iterator[Node] = theSeq.iterator + + override def apply(i: Int): Node = theSeq(i) + def apply(f: Node => Boolean): NodeSeq = filter(f) + + def xml_sameElements[A](that: Iterable[A]): Boolean = { + val these: Iterator[Node] = this.iterator + val those: Iterator[A] = that.iterator + while (these.hasNext && those.hasNext) + if (these.next().xml_!=(those.next())) + return false + + !these.hasNext && !those.hasNext + } + + override protected def basisForHashCode: Seq[Any] = theSeq + + override def canEqual(other: Any): Boolean = other match { + case _: NodeSeq => true + case _ => false + } + + override def strict_==(other: Equality): Boolean = other match { + case x: NodeSeq => (length == x.length) && theSeq.sameElements(x.theSeq) + case _ => false + } + + /** + * Projection function, which returns elements of `this` sequence based + * on the string `that`. Use: + * - `this \ "foo"` to get a list of all children that are labelled with `"foo"`; + * - `this \ "_"` to get a list of all child elements (wildcard); + * - `this \ "@foo"` to get the unprefixed attribute `"foo"` of `this`; + * - `this \ "@{uri}foo"` to get the prefixed attribute `"pre:foo"` whose + * prefix `"pre"` is resolved to the namespace `"uri"`. + * + * For attribute projections, the resulting [[scala.xml.NodeSeq]] attribute + * values are wrapped in a [[scala.xml.Group]]. + * + * There is no support for searching a prefixed attribute by its literal prefix. + * + * The document order is preserved. + */ + def \(that: String): NodeSeq = { + def fail: Nothing = throw new IllegalArgumentException(that) + def atResult: NodeSeq = { + lazy val y: Node = this(0) + val attr: Option[Seq[Node]] = + if (that.length == 1) fail + else if (that(1) == '{') { + val i: Int = that.indexOf('}') + if (i == -1) fail + val (uri: String, key: String) = (that.substring(2, i), that.substring(i + 1, that.length)) + if (uri.isEmpty || key.isEmpty) fail + else y.attribute(uri, key) + } else y.attribute(that.drop(1)) + + attr match { + case Some(x) => Group(x) + case _ => NodeSeq.Empty + } + } + + def makeSeq(cond: Node => Boolean): NodeSeq = + NodeSeq.fromSeq(this.flatMap(_.child).filter(cond)) + + that match { + case "" => fail + case "_" => makeSeq(!_.isAtom) + case "@" => fail + case _ if that(0) == '@' && this.length == 1 => atResult + case _ => makeSeq(_.label == that) + } + } + + /** + * Projection function, which returns elements of `this` sequence and of + * all its subsequences, based on the string `that`. Use: + * - `this \\ "foo"` to get a list of all elements that are labelled with "foo"`, + * including `this`; + * - `this \\ "_"` to get a list of all elements (wildcard), including `this`; + * - `this \\ "@foo"` to get all unprefixed attributes `"foo"`; + * - `this \\ "@{uri}foo"` to get all prefixed attribute `"pre:foo"` whose + * prefix `"pre"` is resolved to the namespace `"uri"`. + * + * For attribute projections, the resulting [[scala.xml.NodeSeq]] attribute + * values are wrapped in a [[scala.xml.Group]]. + * + * There is no support for searching a prefixed attribute by its literal prefix. + * + * The document order is preserved. + */ + def \\(that: String): NodeSeq = { + def fail: Nothing = throw new IllegalArgumentException(that) + def filt(cond: Node => Boolean): NodeSeq = this.flatMap(_.descendant_or_self).filter(cond) + that match { + case "" => fail + case "_" => filt(!_.isAtom) + case _ if that(0) == '@' => filt(!_.isAtom).flatMap(_ \ that) + case _ => filt(x => !x.isAtom && x.label == that) + } + } + + /** + * Convenience method which returns string text of the named attribute. Use: + * - `that \@ "foo"` to get the string text of attribute `"foo"`; + */ + def \@(attributeName: String): String = (this \ s"@$attributeName").text + + override def toString: String = theSeq.mkString + + def text: String = this.map(_.text).mkString +} diff --git a/shared/src/main/scala/scala/xml/Null.scala b/shared/src/main/scala/scala/xml/Null.scala new file mode 100644 index 000000000..c8e35640c --- /dev/null +++ b/shared/src/main/scala/scala/xml/Null.scala @@ -0,0 +1,67 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml + +import scala.collection.Iterator +import scala.collection.Seq + +/** + * Essentially, every method in here is a dummy, returning Zero[T]. + * It provides a backstop for the unusual collection defined by MetaData, + * sort of a linked list of tails. + * + * @author Burak Emir + */ +// Note: used by the Scala compiler. +case object Null extends MetaData { + override def iterator: Iterator[Nothing] = Iterator.empty + override def size: Int = 0 + override def append(m: MetaData, scope: NamespaceBinding = TopScope): MetaData = m + override def filter(f: MetaData => Boolean): ScalaVersionSpecificReturnTypes.NullFilter = this + + override def copy(next: MetaData): MetaData = next + override def getNamespace(owner: Node): ScalaVersionSpecificReturnTypes.NullGetNamespace = null + + override def hasNext: Boolean = false + override def next: ScalaVersionSpecificReturnTypes.NullNext = null + override def key: ScalaVersionSpecificReturnTypes.NullKey = null + override def value: ScalaVersionSpecificReturnTypes.NullValue = null + override def isPrefixed: Boolean = false + + override def length: Int = 0 + override def length(i: Int): Int = i + + override def strict_==(other: Equality): Boolean = other match { + case x: MetaData => x.length == 0 + case _ => false + } + override protected def basisForHashCode: Seq[Any] = Nil + + override def apply(namespace: String, scope: NamespaceBinding, key: String): ScalaVersionSpecificReturnTypes.NullApply3 = null + override def apply(key: String): ScalaVersionSpecific.SeqOfNode = + if (Utility.isNameStart(key.head)) null + else throw new IllegalArgumentException(s"not a valid attribute name '$key', so can never match !") + + override protected def toString1(sb: StringBuilder): Unit = () + override protected def toString1: String = "" + + override def toString: String = "" + + override def buildString(sb: StringBuilder): StringBuilder = sb + + override def wellformed(scope: NamespaceBinding): Boolean = true + + override def remove(key: String): ScalaVersionSpecificReturnTypes.NullRemove = this + override def remove(namespace: String, scope: NamespaceBinding, key: String): ScalaVersionSpecificReturnTypes.NullRemove = this +} diff --git a/src/main/scala/scala/xml/PCData.scala b/shared/src/main/scala/scala/xml/PCData.scala similarity index 55% rename from src/main/scala/scala/xml/PCData.scala rename to shared/src/main/scala/scala/xml/PCData.scala index 864bfa564..ee91aa240 100644 --- a/src/main/scala/scala/xml/PCData.scala +++ b/shared/src/main/scala/scala/xml/PCData.scala @@ -1,10 +1,14 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ package scala package xml @@ -15,8 +19,8 @@ package xml * sections in the input and is to be preserved as CDATA section in the output. * * @author Burak Emir - * @version 1.0 */ +// Note: used by the Scala compiler (before Scala 3). class PCData(data: String) extends Atom[String](data) { /** @@ -26,8 +30,10 @@ class PCData(data: String) extends Atom[String](data) { * @param sb the input string buffer associated to some XML element * @return the input string buffer with the formatted CDATA section */ - override def buildString(sb: StringBuilder): StringBuilder = - sb append "".format(data) + override def buildString(sb: StringBuilder): StringBuilder = { + val dataStr: String = data.replaceAll("]]>", "]]]]>") + sb.append(s"") + } } /** @@ -35,10 +41,10 @@ class PCData(data: String) extends Atom[String](data) { * convenient construction and deconstruction. * * @author Burak Emir - * @version 1.0 */ +// Note: used by the Scala compiler (before Scala 3). object PCData { - def apply(data: String) = new PCData(data) + def apply(data: String): PCData = new PCData(data) def unapply(other: Any): Option[String] = other match { case x: PCData => Some(x.data) case _ => None diff --git a/shared/src/main/scala/scala/xml/PrefixedAttribute.scala b/shared/src/main/scala/scala/xml/PrefixedAttribute.scala new file mode 100644 index 000000000..1dc6ba124 --- /dev/null +++ b/shared/src/main/scala/scala/xml/PrefixedAttribute.scala @@ -0,0 +1,75 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml + +import scala.collection.Seq + +/** + * prefixed attributes always have a non-null namespace. + * + * @param pre + * @param key + * @param value the attribute value + * @param next1 + */ +// Note: used by the Scala compiler. +class PrefixedAttribute( + override val pre: String, + override val key: String, + _value: Seq[Node], + val next1: MetaData +) + extends Attribute +{ + override val value: ScalaVersionSpecific.SeqOfNode = if (_value == null) null else _value match { + case ns: ScalaVersionSpecific.SeqOfNode => ns + case _ => _value.toVector + } + + override val next: MetaData = if (value != null) next1 else next1.remove(key) + + /** same as this(pre, key, Text(value), next), or no attribute if value is null */ + def this(pre: String, key: String, value: String, next: MetaData) = + this(pre, key, if (value != null) Text(value) else null: NodeSeq, next) + + /** same as this(pre, key, value.get, next), or no attribute if value is None */ + def this(pre: String, key: String, value: Option[Seq[Node]], next: MetaData) = + this(pre, key, value.orNull, next) + + /** + * Returns a copy of this unprefixed attribute with the given + * next field. + */ + override def copy(next: MetaData): PrefixedAttribute = + new PrefixedAttribute(pre, key, value, next) + + override def getNamespace(owner: Node): String = + owner.getNamespace(pre) + + /** forwards the call to next (because caller looks for unprefixed attribute */ + override def apply(key: String): ScalaVersionSpecific.SeqOfNode = next(key) + + /** + * gets attribute value of qualified (prefixed) attribute with given key + */ + override def apply(namespace: String, scope: NamespaceBinding, key: String): ScalaVersionSpecific.SeqOfNode = + if (key == this.key && scope.getURI(pre) == namespace) + value + else + next(namespace, scope, key) +} + +object PrefixedAttribute { + def unapply(x: PrefixedAttribute): Some[(String, String, Seq[Node], MetaData)] = Some((x.pre, x.key, x.value, x.next)) +} diff --git a/src/main/scala/scala/xml/PrettyPrinter.scala b/shared/src/main/scala/scala/xml/PrettyPrinter.scala similarity index 57% rename from src/main/scala/scala/xml/PrettyPrinter.scala rename to shared/src/main/scala/scala/xml/PrettyPrinter.scala index 515c9c088..f7fe76194 100755 --- a/src/main/scala/scala/xml/PrettyPrinter.scala +++ b/shared/src/main/scala/scala/xml/PrettyPrinter.scala @@ -1,14 +1,19 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ package scala package xml +import scala.collection.Seq import Utility.sbToString /** @@ -18,27 +23,31 @@ import Utility.sbToString * XML nodes. * * @author Burak Emir - * @version 1.0 - * - * @param width the width to fit the output into - * @param step indentation + * @param width the width to fit the output into + * @param step indentation + * @param minimizeEmpty self-close empty tags + * @note This class is not threadsafe and should not be accessed by + * multiple threads at the same time. */ -class PrettyPrinter(width: Int, step: Int) { +class PrettyPrinter(width: Int, step: Int, minimizeEmpty: Boolean) { + + def this(width: Int, step: Int) = this(width, step, minimizeEmpty = false) + val minimizeMode: MinimizeMode.Value = if (minimizeEmpty) MinimizeMode.Always else MinimizeMode.Default class BrokenException() extends java.lang.Exception class Item case object Break extends Item { - override def toString() = "\\" + override def toString: String = "\\" } case class Box(col: Int, s: String) extends Item case class Para(s: String) extends Item protected var items: List[Item] = Nil - protected var cur = 0 + protected var cur: Int = 0 - protected def reset() = { + protected def reset(): Unit = { cur = 0 items = Nil } @@ -47,10 +56,10 @@ class PrettyPrinter(width: Int, step: Int) { * Try to cut at whitespace. */ protected def cut(s: String, ind: Int): List[Item] = { - val tmp = width - cur + val tmp: Int = width - cur if (s.length <= tmp) return List(Box(ind, s)) - var i = s indexOf ' ' + var i: Int = s.indexOf(' ') if (i > tmp || i == -1) throw new BrokenException() // cannot break var last: List[Int] = Nil @@ -60,7 +69,7 @@ class PrettyPrinter(width: Int, step: Int) { } var res: List[Item] = Nil while (Nil != last) try { - val b = Box(ind, s.substring(0, last.head)) + val b: Box = Box(ind, s.substring(0, last.head)) cur = ind res = b :: Break :: cut(s.substring(last.head, s.length), ind) // backtrack @@ -74,91 +83,98 @@ class PrettyPrinter(width: Int, step: Int) { /** * Try to make indented box, if possible, else para. */ - protected def makeBox(ind: Int, s: String) = + protected def makeBox(ind: Int, s: String): Unit = if (cur + s.length > width) { // fits in this line items ::= Box(ind, s) cur += s.length - } else try cut(s, ind) foreach (items ::= _) // break it up + } else try cut(s, ind).foreach(items ::= _) // break it up catch { case _: BrokenException => makePara(ind, s) } // give up, para // dont respect indent in para, but afterwards - protected def makePara(ind: Int, s: String) = { + protected def makePara(ind: Int, s: String): Unit = { items = Break :: Para(s) :: Break :: items cur = ind } // respect indent - protected def makeBreak() = { // using wrapping here... + protected def makeBreak(): Unit = { // using wrapping here... items = Break :: items cur = 0 } - protected def leafTag(n: Node) = { - def mkLeaf(sb: StringBuilder) { - sb append '<' - n nameToString sb - n.attributes buildString sb - sb append "/>" + protected def leafTag(n: Node): String = { + def mkLeaf(sb: StringBuilder): Unit = { + sb.append('<') + n.nameToString(sb) + n.attributes.buildString(sb) + sb.append("/>") } sbToString(mkLeaf) } protected def startTag(n: Node, pscope: NamespaceBinding): (String, Int) = { - var i = 0 - def mkStart(sb: StringBuilder) { - sb append '<' - n nameToString sb + var i: Int = 0 + def mkStart(sb: StringBuilder): Unit = { + sb.append('<') + n.nameToString(sb) i = sb.length + 1 - n.attributes buildString sb + n.attributes.buildString(sb) n.scope.buildString(sb, pscope) - sb append '>' + sb.append('>') } (sbToString(mkStart), i) } - protected def endTag(n: Node) = { - def mkEnd(sb: StringBuilder) { - sb append "' + protected def endTag(n: Node): String = { + def mkEnd(sb: StringBuilder): Unit = { + sb.append("') } sbToString(mkEnd) } protected def childrenAreLeaves(n: Node): Boolean = { - def isLeaf(l: Node) = l match { - case _: Atom[_] | _: Comment | _: EntityRef | _: ProcInstr => true + def isLeaf(l: Node): Boolean = l match { + case _: Atom[?] | _: Comment | _: EntityRef | _: ProcInstr => true case _ => false } - n.child forall isLeaf + n.child.forall(isLeaf) } - protected def fits(test: String) = + protected def fits(test: String): Boolean = test.length < width - cur - private def doPreserve(node: Node) = - node.attribute(XML.namespace, XML.space).map(_.toString == XML.preserve) getOrElse false + private def doPreserve(node: Node): Boolean = + node.attribute(XML.namespace, XML.space).exists(_.toString == XML.preserve) protected def traverse(node: Node, pscope: NamespaceBinding, ind: Int): Unit = node match { - case Text(s) if s.trim() == "" => - ; - case _: Atom[_] | _: Comment | _: EntityRef | _: ProcInstr => - makeBox(ind, node.toString().trim()) - case g@Group(xs) => + case Text(s) if s.trim.isEmpty => + + case _: Atom[?] | _: Comment | _: EntityRef | _: ProcInstr => + makeBox(ind, node.toString.trim) + case Group(xs) => traverse(xs.iterator, pscope, ind) case _ => - val test = { - val sb = new StringBuilder() - Utility.serialize(node, pscope, sb, stripComments = false) + val test: String = { + val sb: StringBuilder = new StringBuilder() + Utility.serialize(node, pscope, sb, stripComments = false, minimizeTags = minimizeMode) if (doPreserve(node)) sb.toString else TextBuffer.fromString(sb.toString).toText(0).data } - if (childrenAreLeaves(node) && fits(test)) { + if (childrenAreLeaves(node) && fits(test)) makeBox(ind, test) - } else { - val (stg, len2) = startTag(node, pscope) - val etg = endTag(node) + else { + val ((stg: String, len2: Int), etg: String) = + if (node.child.isEmpty && minimizeEmpty) { + // force the tag to be self-closing + val firstAttribute: Int = test.indexOf(' ') + val firstBreak: Int = if (firstAttribute != -1) firstAttribute else test.lastIndexOf('/') + ((test, firstBreak), "") + } else + (startTag(node, pscope), endTag(node)) + if (stg.length < width - cur) { // start tag fits makeBox(ind, stg) makeBreak() @@ -169,18 +185,20 @@ class PrettyPrinter(width: Int, step: Int) { makeBox(ind, stg.substring(0, len2)) makeBreak() // todo: break the rest in pieces /*{ //@todo - val sq:Seq[String] = stg.split(" "); - val it = sq.iterator; - it.next; + val sq:Seq[String] = stg.split(" ") + val it = sq.iterator + it.next for (c <- it) { makeBox(ind+len2-2, c) makeBreak() } }*/ - makeBox(ind, stg.substring(len2, stg.length)) - makeBreak() - traverse(node.child.iterator, node.scope, ind + step) - makeBox(cur, etg) + makeBox(ind, stg.substring(len2, stg.length).trim) + if (etg.nonEmpty) { + makeBreak() + traverse(node.child.iterator, node.scope, ind + step) + makeBox(cur, etg) + } makeBreak() } else { // give up makeBox(ind, test) @@ -202,35 +220,33 @@ class PrettyPrinter(width: Int, step: Int) { * @param n the node to be serialized * @param sb the stringbuffer to append to */ - def format(n: Node, sb: StringBuilder) { // entry point - format(n, null, sb) - } + def format(n: Node, sb: StringBuilder): Unit = format(n, TopScope, sb) // entry point - def format(n: Node, pscope: NamespaceBinding, sb: StringBuilder) { // entry point - var lastwasbreak = false + def format(n: Node, pscope: NamespaceBinding, sb: StringBuilder): Unit = { // entry point + var lastwasbreak: Boolean = false reset() traverse(n, pscope, 0) - var cur = 0 + var cur: Int = 0 for (b <- items.reverse) b match { case Break => if (!lastwasbreak) sb.append('\n') // on windows: \r\n ? lastwasbreak = true cur = 0 // while (cur < last) { - // sb append ' ' + // sb.append(' ') // cur += 1 // } case Box(i, s) => lastwasbreak = false while (cur < i) { - sb append ' ' + sb.append(' ') cur += 1 } sb.append(s) case Para(s) => lastwasbreak = false - sb append s + sb.append(s) } } @@ -244,7 +260,7 @@ class PrettyPrinter(width: Int, step: Int) { * @param pscope the namespace to prefix mapping * @return the formatted string */ - def format(n: Node, pscope: NamespaceBinding = null): String = + def format(n: Node, pscope: NamespaceBinding = TopScope): String = sbToString(format(n, pscope, _)) /** @@ -253,7 +269,7 @@ class PrettyPrinter(width: Int, step: Int) { * @param nodes the sequence of nodes to be serialized * @param pscope the namespace to prefix mapping */ - def formatNodes(nodes: Seq[Node], pscope: NamespaceBinding = null): String = + def formatNodes(nodes: Seq[Node], pscope: NamespaceBinding = TopScope): String = sbToString(formatNodes(nodes, pscope, _)) /** @@ -265,5 +281,5 @@ class PrettyPrinter(width: Int, step: Int) { * @param sb the string buffer to which to append to */ def formatNodes(nodes: Seq[Node], pscope: NamespaceBinding, sb: StringBuilder): Unit = - nodes foreach (n => sb append format(n, pscope)) + nodes.foreach(n => sb.append(format(n, pscope))) } diff --git a/shared/src/main/scala/scala/xml/ProcInstr.scala b/shared/src/main/scala/scala/xml/ProcInstr.scala new file mode 100644 index 000000000..f12bc4a65 --- /dev/null +++ b/shared/src/main/scala/scala/xml/ProcInstr.scala @@ -0,0 +1,44 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml + +/** + * an XML node for processing instructions (PI) + * + * @author Burak Emir + * @param target target name of this PI + * @param proctext text contained in this node, may not contain "?>" + */ +// Note: used by the Scala compiler. +case class ProcInstr(target: String, proctext: String) extends SpecialNode { + if (!Utility.isName(target)) + throw new IllegalArgumentException(s"$target must be an XML Name") + if (proctext.contains("?>")) + throw new IllegalArgumentException(s"""$proctext may not contain "?>"""") + if (target.toLowerCase == "xml") + throw new IllegalArgumentException(s"$target is reserved") + + final override def doCollectNamespaces: Boolean = false + final override def doTransform: Boolean = false + + final override def label: String = "#PI" + override def text: String = "" + + /** + * appends "<?" target (" "+text)?+"?>" + * to this stringbuffer. + */ + override def buildString(sb: StringBuilder): StringBuilder = + sb.append(s"") +} diff --git a/shared/src/main/scala/scala/xml/QNode.scala b/shared/src/main/scala/scala/xml/QNode.scala new file mode 100644 index 000000000..ddf7818c4 --- /dev/null +++ b/shared/src/main/scala/scala/xml/QNode.scala @@ -0,0 +1,25 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml + +/** + * This object provides an extractor method to match a qualified node with + * its namespace URI + * + * @author Burak Emir + */ +object QNode { + def unapplySeq(n: Node): Some[(String, String, MetaData, ScalaVersionSpecific.SeqOfNode)] = + Some((n.scope.getURI(n.prefix), n.label, n.attributes, n.child)) +} diff --git a/shared/src/main/scala/scala/xml/SpecialNode.scala b/shared/src/main/scala/scala/xml/SpecialNode.scala new file mode 100644 index 000000000..abb151de8 --- /dev/null +++ b/shared/src/main/scala/scala/xml/SpecialNode.scala @@ -0,0 +1,35 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml + +/** + * `SpecialNode` is a special XML node which represents either text + * `(PCDATA)`, a comment, a `PI`, or an entity ref. + * + * @author Burak Emir + */ +abstract class SpecialNode extends Node { + + /** always empty */ + final override def attributes: Null.type = Null + + /** always Node.EmptyNamespace - TODO not really: Node.EmptyNamespace is "", but this is null. */ + final override def namespace: scala.Null = null + + /** always empty */ + final override def child: ScalaVersionSpecificReturnTypes.SpecialNodeChild = Nil + + /** Append string representation to the given string buffer argument. */ + def buildString(sb: StringBuilder): StringBuilder +} diff --git a/src/main/scala/scala/xml/Text.scala b/shared/src/main/scala/scala/xml/Text.scala similarity index 57% rename from src/main/scala/scala/xml/Text.scala rename to shared/src/main/scala/scala/xml/Text.scala index 9da658a98..118cb48e2 100644 --- a/src/main/scala/scala/xml/Text.scala +++ b/shared/src/main/scala/scala/xml/Text.scala @@ -1,10 +1,14 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ package scala package xml @@ -16,6 +20,7 @@ package xml * @author Burak Emir * @param data the text contained in this node, may not be null. */ +// Note: used by the Scala compiler. class Text(data: String) extends Atom[String](data) { /** @@ -31,10 +36,10 @@ class Text(data: String) extends Atom[String](data) { * convenient construction and deconstruction. * * @author Burak Emir - * @version 1.0 */ +// Note: used by the Scala compiler. object Text { - def apply(data: String) = new Text(data) + def apply(data: String): Text = new Text(data) def unapply(other: Any): Option[String] = other match { case x: Text => Some(x.data) case _ => None diff --git a/shared/src/main/scala/scala/xml/TextBuffer.scala b/shared/src/main/scala/scala/xml/TextBuffer.scala new file mode 100644 index 000000000..5492790a3 --- /dev/null +++ b/shared/src/main/scala/scala/xml/TextBuffer.scala @@ -0,0 +1,53 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml + +import scala.collection.Seq +import scala.collection.immutable.{Seq => ISeq} +import Utility.isSpace + +object TextBuffer { + def fromString(str: String): TextBuffer = new TextBuffer().append(str) +} + +/** + * The class `TextBuffer` is for creating text nodes without surplus + * whitespace. All occurrences of one or more whitespace in strings + * appended with the `append` method will be replaced by a single space + * character, and leading and trailing space will be removed completely. + */ +class TextBuffer extends ScalaVersionSpecificTextBuffer { + val sb: StringBuilder = new StringBuilder() + + /** + * Appends this string to the text buffer, trimming whitespaces as needed. + */ + def append(cs: Seq[Char]): this.type = { + cs.foreach { c => + if (!isSpace(c)) sb.append(c) + else if (sb.isEmpty || !isSpace(sb.last)) sb.append(' ') + } + this + } + + /** + * Returns an empty sequence if text is only whitespace. + * + * @return the text without whitespaces. + */ + def toText: ScalaVersionSpecific.SeqOfText = sb.toString.trim match { + case "" => Nil + case s => ISeq(Text(s)) + } +} diff --git a/shared/src/main/scala/scala/xml/TopScope.scala b/shared/src/main/scala/scala/xml/TopScope.scala new file mode 100644 index 000000000..0b82e4b63 --- /dev/null +++ b/shared/src/main/scala/scala/xml/TopScope.scala @@ -0,0 +1,33 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml + +/** + * top level namespace scope. only contains the predefined binding + * for the "xml" prefix which is bound to + * "http://www.w3.org/XML/1998/namespace" + */ +object TopScope extends NamespaceBinding(null, null, null) { + + override def getURI(prefix1: String): String = + if (prefix1 == XML.xml) XML.namespace else null + + override def getPrefix(uri1: String): String = + if (uri1 == XML.namespace) XML.xml else null + + override def toString: String = "" + + override def buildString(stop: NamespaceBinding): String = "" + override def buildString(sb: StringBuilder, ignore: NamespaceBinding): Unit = () +} diff --git a/shared/src/main/scala/scala/xml/TypeSymbol.scala b/shared/src/main/scala/scala/xml/TypeSymbol.scala new file mode 100644 index 000000000..5d1f2590a --- /dev/null +++ b/shared/src/main/scala/scala/xml/TypeSymbol.scala @@ -0,0 +1,16 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml + +abstract class TypeSymbol diff --git a/shared/src/main/scala/scala/xml/Unparsed.scala b/shared/src/main/scala/scala/xml/Unparsed.scala new file mode 100644 index 000000000..fc477313d --- /dev/null +++ b/shared/src/main/scala/scala/xml/Unparsed.scala @@ -0,0 +1,43 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml + +/** + * An XML node for unparsed content. It will be output verbatim, all bets + * are off regarding wellformedness etc. + * + * @author Burak Emir + * @param data content in this node, may not be null. + */ +// Note: used by the Scala compiler. +class Unparsed(data: String) extends Atom[String](data) { + + /** + * Returns text, with some characters escaped according to XML + * specification. + */ + override def buildString(sb: StringBuilder): StringBuilder = + sb.append(data) +} + +/** + * This singleton object contains the `apply`and `unapply` methods for + * convenient construction and deconstruction. + * + * @author Burak Emir + */ +object Unparsed { + def apply(data: String): Unparsed = new Unparsed(data) + def unapply(x: Unparsed): Some[String] = Some(x.data) +} diff --git a/shared/src/main/scala/scala/xml/UnprefixedAttribute.scala b/shared/src/main/scala/scala/xml/UnprefixedAttribute.scala new file mode 100644 index 000000000..877a59b25 --- /dev/null +++ b/shared/src/main/scala/scala/xml/UnprefixedAttribute.scala @@ -0,0 +1,74 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml + +import scala.collection.Seq + +/** + * Unprefixed attributes have the null namespace, and no prefix field + * + * @author Burak Emir + */ +// Note: used by the Scala compiler. +class UnprefixedAttribute( + override val key: String, + _value: Seq[Node], + next1: MetaData +) + extends Attribute +{ + override val value: ScalaVersionSpecific.SeqOfNode = if (_value == null) null else _value match { + case ns: ScalaVersionSpecific.SeqOfNode => ns + case _ => _value.toVector + } + + final override val pre: scala.Null = null + override val next: MetaData = if (value != null) next1 else next1.remove(key) + + /** same as this(key, Text(value), next), or no attribute if value is null */ + def this(key: String, value: String, next: MetaData) = + this(key, if (value != null) Text(value) else null: NodeSeq, next) + + /** same as this(key, value.get, next), or no attribute if value is None */ + def this(key: String, value: Option[Seq[Node]], next: MetaData) = + this(key, value.orNull, next) + + /** returns a copy of this unprefixed attribute with the given next field*/ + override def copy(next: MetaData): UnprefixedAttribute = new UnprefixedAttribute(key, value, next) + + final override def getNamespace(owner: Node): String = null + + /** + * Gets value of unqualified (unprefixed) attribute with given key, null if not found + * + * @param key + * @return value as Seq[Node] if key is found, null otherwise + */ + override def apply(key: String): ScalaVersionSpecific.SeqOfNode = + if (key == this.key) value else next(key) + + /** + * Forwards the call to next (because caller looks for prefixed attribute). + * + * @param namespace + * @param scope + * @param key + * @return .. + */ + override def apply(namespace: String, scope: NamespaceBinding, key: String): ScalaVersionSpecific.SeqOfNode = + next(namespace, scope, key) +} +object UnprefixedAttribute { + def unapply(x: UnprefixedAttribute): Some[(String, Seq[Node], MetaData)] = Some((x.key, x.value, x.next)) +} diff --git a/src/main/scala/scala/xml/Utility.scala b/shared/src/main/scala/scala/xml/Utility.scala similarity index 53% rename from src/main/scala/scala/xml/Utility.scala rename to shared/src/main/scala/scala/xml/Utility.scala index aea4f45fc..cf85be14f 100755 --- a/src/main/scala/scala/xml/Utility.scala +++ b/shared/src/main/scala/scala/xml/Utility.scala @@ -1,17 +1,23 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ package scala package xml +import scala.annotation.tailrec import scala.collection.mutable -import parsing.XhtmlEntities import scala.language.implicitConversions +import scala.collection.Seq +import scala.collection.immutable.{Seq => ISeq} /** * The `Utility` object provides utility functions for processing instances @@ -19,21 +25,21 @@ import scala.language.implicitConversions * * @author Burak Emir */ -object Utility extends AnyRef with parsing.TokenTests { - final val SU = '\u001A' +object Utility extends AnyRef with parsing.TokenTests with ScalaVersionSpecificUtility { + final val SU: Char = '\u001A' // [Martin] This looks dubious. We don't convert StringBuilders to // Strings anywhere else, why do it here? - implicit def implicitSbToString(sb: StringBuilder) = sb.toString() + implicit def implicitSbToString(sb: StringBuilder): String = sb.toString // helper for the extremely oft-repeated sequence of creating a // StringBuilder, passing it around, and then grabbing its String. - private[xml] def sbToString(f: (StringBuilder) => Unit): String = { - val sb = new StringBuilder + private[xml] def sbToString(f: StringBuilder => Unit): String = { + val sb: StringBuilder = new StringBuilder f(sb) sb.toString } - private[xml] def isAtomAndNotText(x: Node) = x.isAtom && !x.isInstanceOf[Text] + private[xml] def isAtomAndNotText(x: Node): Boolean = x.isAtom && !x.isInstanceOf[Text] /** * Trims an element - call this method, when you know that it is an @@ -46,17 +52,23 @@ object Utility extends AnyRef with parsing.TokenTests { */ def trim(x: Node): Node = x match { case Elem(pre, lab, md, scp, child@_*) => - val children = child flatMap trimProper + val children = combineAdjacentTextNodes(child).flatMap(trimProper) Elem(pre, lab, md, scp, children.isEmpty, children: _*) } + private def combineAdjacentTextNodes(children: ScalaVersionSpecific.SeqOfNode): ScalaVersionSpecific.SeqOfNode = + children.foldRight(ISeq.empty[Node]) { + case (Text(left), Text(right) +: nodes) => Text(left + right) +: nodes + case (n, nodes) => n +: nodes + } + /** * trim a child of an element. `Attribute` values and `Atom` nodes that * are not `Text` nodes are unaffected. */ - def trimProper(x: Node): Seq[Node] = x match { + def trimProper(x: Node): ScalaVersionSpecific.SeqOfNode = x match { case Elem(pre, lab, md, scp, child@_*) => - val children = child flatMap trimProper + val children = combineAdjacentTextNodes(child).flatMap(trimProper) Elem(pre, lab, md, scp, children.isEmpty, children: _*) case Text(s) => new TextBuffer().append(s).toText @@ -65,11 +77,11 @@ object Utility extends AnyRef with parsing.TokenTests { } /** returns a sorted attribute list */ - def sort(md: MetaData): MetaData = if ((md eq Null) || (md.next eq Null)) md else { - val key = md.key - val smaller = sort(md.filter { m => m.key < key }) - val greater = sort(md.filter { m => m.key > key }) - smaller.foldRight (md copy greater) ((x, xs) => x copy xs) + def sort(md: MetaData): MetaData = if (md.isNull || md.next.isNull) md else { + val key: String = md.key + val smaller: MetaData = sort(md.filter { m => m.key < key }) + val greater: MetaData = sort(md.filter { m => m.key > key }) + smaller.foldRight(md.copy(greater)) ((x, xs) => x.copy(xs)) } /** @@ -78,7 +90,7 @@ object Utility extends AnyRef with parsing.TokenTests { */ def sort(n: Node): Node = n match { case Elem(pre, lab, md, scp, child@_*) => - val children = child map sort + val children = child.map(sort) Elem(pre, lab, sort(md), scp, children.isEmpty, children: _*) case _ => n } @@ -93,46 +105,31 @@ object Utility extends AnyRef with parsing.TokenTests { * For reasons unclear escape and unescape are a long ways from * being logical inverses. */ - val pairs = Map( + val pairs: Map[String, Char] = Map( "lt" -> '<', "gt" -> '>', "amp" -> '&', - "quot" -> '"' - // enigmatic comment explaining why this isn't escaped -- - // is valid xhtml but not html, and IE doesn't know it, says jweb - // "apos" -> '\'' + "quot" -> '"', + "apos" -> '\'' ) - val escMap = pairs map { case (s, c) => c -> ("&%s;" format s) } - val unescMap = pairs ++ Map("apos" -> '\'') + val escMap: Map[Char, String] = (pairs - "apos").map { case (s, c) => c -> s"&$s;" } + val unescMap: Map[String, Char] = pairs } import Escapes.{ escMap, unescMap } /** * Appends escaped string to `s`. */ - final def escape(text: String, s: StringBuilder): StringBuilder = { + final def escape(text: String, s: StringBuilder): StringBuilder = // Implemented per XML spec: // http://www.w3.org/International/questions/qa-controls - // imperative code 3x-4x faster than current implementation - // dpp (David Pollak) 2010/02/03 - val len = text.length - var pos = 0 - while (pos < len) { - text.charAt(pos) match { - case '<' => s.append("<") - case '>' => s.append(">") - case '&' => s.append("&") - case '"' => s.append(""") - case '\n' => s.append('\n') - case '\r' => s.append('\r') - case '\t' => s.append('\t') - case c => if (c >= ' ') s.append(c) + text.iterator.foldLeft(s) { (s, c) => + escMap.get(c) match { + case Some(str) => s ++= str + case _ if c >= ' ' || "\n\r\t".contains(c) => s += c + case _ => s // noop } - - pos += 1 } - s - } /** * Appends unescaped string to `s`, `amp` becomes `&`, @@ -141,7 +138,7 @@ object Utility extends AnyRef with parsing.TokenTests { * @return `'''null'''` if `ref` was not a predefined entity. */ final def unescape(ref: String, s: StringBuilder): StringBuilder = - ((unescMap get ref) map (s append _)).orNull + unescMap.get(ref).map(s.append).orNull /** * Returns a set of all namespaces used in a sequence of nodes @@ -153,7 +150,7 @@ object Utility extends AnyRef with parsing.TokenTests { /** * Adds all namespaces in node to set. */ - def collectNamespaces(n: Node, set: mutable.Set[String]) { + def collectNamespaces(n: Node, set: mutable.Set[String]): Unit = if (n.doCollectNamespaces) { set += n.namespace for (a <- n.attributes) a match { @@ -164,7 +161,6 @@ object Utility extends AnyRef with parsing.TokenTests { for (i <- n.child) collectNamespaces(i, set) } - } // def toXML( // x: Node, @@ -176,7 +172,7 @@ object Utility extends AnyRef with parsing.TokenTests { // minimizeTags: Boolean = false): String = // { // toXMLsb(x, pscope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags) - // sb.toString() + // sb.toString // } /** @@ -193,10 +189,9 @@ object Utility extends AnyRef with parsing.TokenTests { stripComments: Boolean = false, decodeEntities: Boolean = true, preserveWhitespace: Boolean = false, - minimizeTags: Boolean = false): StringBuilder = - { - serialize(x, pscope, sb, stripComments, decodeEntities, preserveWhitespace, if (minimizeTags) MinimizeMode.Always else MinimizeMode.Never) - } + minimizeTags: Boolean = false + ): StringBuilder = + serialize(x, pscope, sb, stripComments, decodeEntities, preserveWhitespace, if (minimizeTags) MinimizeMode.Always else MinimizeMode.Never) /** * Serialize an XML Node to a StringBuilder. @@ -213,35 +208,66 @@ object Utility extends AnyRef with parsing.TokenTests { stripComments: Boolean = false, decodeEntities: Boolean = true, preserveWhitespace: Boolean = false, - minimizeTags: MinimizeMode.Value = MinimizeMode.Default): StringBuilder = - { - x match { - case c: Comment if !stripComments => c buildString sb - case s: SpecialNode => s buildString sb - case g: Group => - for (c <- g.nodes) serialize(c, g.scope, sb, minimizeTags = minimizeTags); sb - case el: Elem => - // print tag with namespace declarations - sb.append('<') - el.nameToString(sb) - if (el.attributes ne null) el.attributes.buildString(sb) - el.scope.buildString(sb, pscope) - if (el.child.isEmpty && - (minimizeTags == MinimizeMode.Always || - (minimizeTags == MinimizeMode.Default && el.minimizeEmpty))) { - // no children, so use short form: - sb.append("/>") - } else { - // children, so use long form: ... - sb.append('>') - sequenceToXML(el.child, el.scope, sb, stripComments) - sb.append("') - } - case _ => throw new IllegalArgumentException("Don't know how to serialize a " + x.getClass.getName) - } + minimizeTags: MinimizeMode.Value = MinimizeMode.Default + ): StringBuilder = { + serializeImpl(List(x), pscope, false, stripComments, minimizeTags, sb) + sb + } + + private def serializeImpl( + ns: Seq[Node], + pscope: NamespaceBinding, + spaced: Boolean, + stripComments: Boolean, + minimizeTags: MinimizeMode.Value, + sb: StringBuilder + ): Unit = { + @tailrec def ser(nss: List[List[Node]], pscopes: List[NamespaceBinding], spaced: List[Boolean], toClose: List[Node]): Unit = nss match { + case List(Nil) => + case Nil :: rests => + if (toClose.head != null) { + sb.append("') + } + ser(rests, pscopes.tail, spaced.tail, toClose.tail) + case (n :: ns) :: r => + def sp(): Unit = if (ns.nonEmpty && spaced.head) sb.append(' ') + n match { + case c: Comment => + if (!stripComments) { + c.buildString(sb) + sp() + } + ser(ns :: r, pscopes, spaced, toClose) + case s: SpecialNode => + s.buildString(sb) + sp() + ser(ns :: r, pscopes, spaced, toClose) + case g: Group => + ser(g.nodes.toList :: ns :: r, g.scope :: pscopes, false :: spaced, null :: toClose) + case e: Elem => + sb.append('<') + e.nameToString(sb) + if (e.attributes != null) e.attributes.buildString(sb) + e.scope.buildString(sb, pscopes.head) + if (e.child.isEmpty && + (minimizeTags == MinimizeMode.Always || + (minimizeTags == MinimizeMode.Default && e.minimizeEmpty))) { + // no children, so use short form: + sb.append("/>") + sp() + ser(ns :: r, pscopes, spaced, toClose) + } else { + sb.append('>') + val csp = e.child.forall(isAtomAndNotText) + ser(e.child.toList :: ns :: r, e.scope :: pscopes, csp :: spaced, e :: toClose) + } + case n => throw new IllegalArgumentException(s"Don't know how to serialize a ${n.getClass.getName}") + } } + ser(List(ns.toList), List(pscope), List(spaced), Nil) + } def sequenceToXML( children: Seq[Node], @@ -250,33 +276,27 @@ object Utility extends AnyRef with parsing.TokenTests { stripComments: Boolean = false, decodeEntities: Boolean = true, preserveWhitespace: Boolean = false, - minimizeTags: MinimizeMode.Value = MinimizeMode.Default): Unit = - { - if (children.isEmpty) return - else if (children forall isAtomAndNotText) { // add space - val it = children.iterator - val f = it.next() - serialize(f, pscope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags) - while (it.hasNext) { - val x = it.next() - sb.append(' ') - serialize(x, pscope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags) - } - } else children foreach { serialize(_, pscope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags) } - } + minimizeTags: MinimizeMode.Value = MinimizeMode.Default + ): Unit = if (children.nonEmpty) { + val spaced = children.forall(isAtomAndNotText) + serializeImpl(children, pscope, spaced, stripComments, minimizeTags, sb) + } + + def splitName(name: String): (Option[String], String) = { + val colon: Int = name.indexOf(':') + if (colon < 0) (None, name) + else (Some(name.take(colon)), name.drop(colon + 1)) + } /** * Returns prefix of qualified name if any. */ - final def prefix(name: String): Option[String] = (name indexOf ':') match { - case -1 => None - case i => Some(name.substring(0, i)) - } + final def prefix(name: String): Option[String] = splitName(name)._1 /** * Returns a hashcode for the given constituents of a node */ - def hashCode(pre: String, label: String, attribHashCode: Int, scpeHash: Int, children: Seq[Node]) = + def hashCode(pre: String, label: String, attribHashCode: Int, scpeHash: Int, children: Seq[Node]): Int = scala.util.hashing.MurmurHash3.orderedHash(label +: attribHashCode +: scpeHash +: children, pre.##) def appendQuoted(s: String): String = sbToString(appendQuoted(s, _)) @@ -285,9 +305,9 @@ object Utility extends AnyRef with parsing.TokenTests { * Appends "s" if string `s` does not contain ", * 's' otherwise. */ - def appendQuoted(s: String, sb: StringBuilder) = { - val ch = if (s contains '"') '\'' else '"' - sb.append(ch).append(s).append(ch) + def appendQuoted(s: String, sb: StringBuilder): StringBuilder = { + val ch: Char = if (s.contains('"')) '\'' else '"' + sb.append(s"$ch$s$ch") } /** @@ -303,32 +323,30 @@ object Utility extends AnyRef with parsing.TokenTests { sb.append('"') } - def getName(s: String, index: Int): String = { - if (index >= s.length) null - else { - val xs = s drop index - if (xs.nonEmpty && isNameStart(xs.head)) xs takeWhile isNameChar + def getName(s: String, index: Int): String = + if (index >= s.length) null else { + val xs: String = s.drop(index) + if (xs.nonEmpty && isNameStart(xs.head)) xs.takeWhile(isNameChar) else "" } - } /** * Returns `'''null'''` if the value is a correct attribute value, * error message if it isn't. */ def checkAttributeValue(value: String): String = { - var i = 0 + var i: Int = 0 while (i < value.length) { value.charAt(i) match { case '<' => return "< not allowed in attribute value" case '&' => - val n = getName(value, i + 1) - if (n eq null) - return "malformed entity reference in attribute value [" + value + "]" + val n: String = getName(value, i + 1) + if (n == null) + return s"malformed entity reference in attribute value [$value]" i = i + n.length + 1 if (i >= value.length || value.charAt(i) != ';') - return "malformed entity reference in attribute value [" + value + "]" + return s"malformed entity reference in attribute value [$value]" case _ => } i = i + 1 @@ -336,51 +354,52 @@ object Utility extends AnyRef with parsing.TokenTests { null } - def parseAttributeValue(value: String): Seq[Node] = { - val sb = new StringBuilder + // unused, untested + def parseAttributeValue(value: String): ScalaVersionSpecific.SeqOfNode = { + val sb: StringBuilder = new StringBuilder var rfb: StringBuilder = null - val nb = new NodeBuffer() + val nb: NodeBuffer = new NodeBuffer() - val it = value.iterator + val it: Iterator[Char] = value.iterator while (it.hasNext) { - var c = it.next() + var c: Char = it.next() // entity! flush buffer into text node if (c == '&') { c = it.next() if (c == '#') { c = it.next() - val theChar = parseCharRef ({ () => c }, { () => c = it.next() }, { s => throw new RuntimeException(s) }, { s => throw new RuntimeException(s) }) + val theChar: String = parseCharRef ({ () => c }, { () => c = it.next() }, { s => throw new RuntimeException(s) }, { s => throw new RuntimeException(s) }) sb.append(theChar) } else { - if (rfb eq null) rfb = new StringBuilder() - rfb append c + if (rfb == null) rfb = new StringBuilder() + rfb.append(c) c = it.next() while (c != ';') { rfb.append(c) c = it.next() } - val ref = rfb.toString() + val ref: String = rfb.toString rfb.clear() unescape(ref, sb) match { case null => - if (sb.length > 0) { // flush buffer - nb += Text(sb.toString()) + if (sb.nonEmpty) { // flush buffer + nb += Text(sb.toString) sb.clear() } nb += EntityRef(ref) // add entityref case _ => } } - } else sb append c + } else sb.append(c) } - if (sb.length > 0) { // flush buffer - val x = Text(sb.toString()) - if (nb.length == 0) + if (sb.nonEmpty) { // flush buffer + val x: Text = Text(sb.toString) + if (nb.isEmpty) return x else nb += x } - nb + nb.toVector } /** @@ -391,27 +410,26 @@ object Utility extends AnyRef with parsing.TokenTests { * See [66] */ def parseCharRef(ch: () => Char, nextch: () => Unit, reportSyntaxError: String => Unit, reportTruncatedError: String => Unit): String = { - val hex = (ch() == 'x') && { nextch(); true } - val base = if (hex) 16 else 10 - var i = 0 - while (ch() != ';') { + val hex: Boolean = (ch() == 'x') && { nextch(); true } + val base: Int = if (hex) 16 else 10 + var i: Int = 0 + while (ch() != ';' && ch() != 0) { ch() match { case '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => i = i * base + ch().asDigit case 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' => if (!hex) - reportSyntaxError("hex char not allowed in decimal char ref\n" + - "Did you mean to write &#x ?") + reportSyntaxError("hex char not allowed in decimal char ref\nDid you mean to write &#x ?") else i = i * base + ch().asDigit case SU => reportTruncatedError("") case _ => - reportSyntaxError("character '" + ch() + "' not allowed in char ref\n") + reportSyntaxError(s"character '${ch()}' not allowed in char ref\n") } nextch() } - new String(Array(i), 0, 1) + if (i != 0) new String(Array(i), 0, 1) else "" } } diff --git a/shared/src/main/scala/scala/xml/XML.scala b/shared/src/main/scala/scala/xml/XML.scala new file mode 100755 index 000000000..3dfe36b27 --- /dev/null +++ b/shared/src/main/scala/scala/xml/XML.scala @@ -0,0 +1,132 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml + +import factory.XMLLoader +import java.io.{File, FileDescriptor, FileInputStream, FileOutputStream, InputStream, Reader, StringReader, Writer} +import java.nio.channels.Channels +import scala.util.control.Exception + +object Source { + def fromFile(name: String): InputSource = fromFile(new File(name)) + def fromFile(file: File): InputSource = fromUrl(file.toURI.toURL) + def fromUrl(url: java.net.URL): InputSource = fromSysId(url.toString) + def fromSysId(sysID: String): InputSource = new InputSource(sysID) + def fromFile(fd: FileDescriptor): InputSource = fromInputStream(new FileInputStream(fd)) + def fromInputStream(is: InputStream): InputSource = new InputSource(is) + def fromString(string: String): InputSource = fromReader(new StringReader(string)) + def fromReader(reader: Reader): InputSource = new InputSource(reader) +} + +/** + * Governs how empty elements (i.e. those without child elements) should be serialized. + */ +object MinimizeMode extends Enumeration { + /** + * Minimize empty tags if they were originally empty when parsed, or if they were constructed + * with [[scala.xml.Elem]]`#minimizeEmpty` == true + */ + val Default: Value = Value + + /** + * Always minimize empty tags. Note that this may be problematic for XHTML, in which + * case [[scala.xml.Xhtml]]`#toXhtml` should be used instead. + */ + val Always: Value = Value + + /** + * Never minimize empty tags. + */ + val Never: Value = Value +} + +/** + * The object `XML` provides constants, and functions to load + * and save XML elements. Use this when data binding is not desired, i.e. + * when XML is handled using `Symbol` nodes. + * + * @author Burak Emir + */ +object XML extends XMLLoader[Elem] { + val xml: String = "xml" + val xmlns: String = "xmlns" + val namespace: String = "http://www.w3.org/XML/1998/namespace" + val preserve: String = "preserve" + val space: String = "space" + val lang: String = "lang" + val encoding: String = "UTF-8" + + /** Returns an XMLLoader whose load* methods will use the supplied SAXParser. */ + def withSAXParser(p: SAXParser): XMLLoader[Elem] = new XMLLoader[Elem] { + override val parser: SAXParser = p + } + + /** Returns an XMLLoader whose load* methods will use the supplied XMLReader. */ + def withXMLReader(r: XMLReader): XMLLoader[Elem] = new XMLLoader[Elem] { + override val reader: XMLReader = r + } + + /** + * Saves a node to a file with given filename using given encoding + * optionally with xmldecl and doctype declaration. + * + * Note: Before scala-xml 1.1.0, the default encoding was ISO-8859-1 (latin1). + * If your code depends on characters in non-ASCII latin1 range, specify + * ISO-8859-1 encoding explicitly. + * + * @param filename the filename + * @param node the xml node we want to write + * @param enc encoding to use + * @param xmlDecl if true, write xml declaration + * @param doctype if not null, write doctype declaration + */ + final def save( + filename: String, + node: Node, + enc: String = "UTF-8", + xmlDecl: Boolean = false, + doctype: dtd.DocType = null + ): Unit = { + val fos: FileOutputStream = new FileOutputStream(filename) + val w: Writer = Channels.newWriter(fos.getChannel, enc) + + Exception.ultimately(w.close())( + write(w, node, enc, xmlDecl, doctype) + ) + } + + /** + * Writes the given node using writer, optionally with xml decl and doctype. + * It's the caller's responsibility to close the writer. + * + * @param w the writer + * @param node the xml node we want to write + * @param enc the string to be used in `xmlDecl` + * @param xmlDecl if true, write xml declaration + * @param doctype if not null, write doctype declaration + */ + final def write( + w: Writer, + node: Node, + enc: String, + xmlDecl: Boolean, + doctype: dtd.DocType, + minimizeTags: MinimizeMode.Value = MinimizeMode.Default + ): Unit = { + /* TODO: optimize by giving writer parameter to toXML*/ + if (xmlDecl) w.write(s"\n") + if (doctype != null) w.write(s"$doctype\n") + w.write(Utility.serialize(node, minimizeTags = minimizeTags).toString) + } +} diff --git a/shared/src/main/scala/scala/xml/Xhtml.scala b/shared/src/main/scala/scala/xml/Xhtml.scala new file mode 100644 index 000000000..a7ce99d06 --- /dev/null +++ b/shared/src/main/scala/scala/xml/Xhtml.scala @@ -0,0 +1,106 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml + +import parsing.XhtmlEntities +import Utility.{ sbToString, isAtomAndNotText } +import scala.collection.Seq + +/* (c) David Pollak 2007 WorldWide Conferencing, LLC */ + +object Xhtml { + /** + * Convenience function: same as toXhtml(node, false, false) + * + * @param node the node + */ + def toXhtml(node: Node): String = sbToString(sb => toXhtml(x = node, sb = sb)) + + /** + * Convenience function: amounts to calling toXhtml(node) on each + * node in the sequence. + * + * @param nodeSeq the node sequence + */ + def toXhtml(nodeSeq: NodeSeq): String = sbToString(sb => sequenceToXML(nodeSeq: Seq[Node], sb = sb)) + + /** + * Elements which we believe are safe to minimize if minimizeTags is true. + * See http://www.w3.org/TR/xhtml1/guidelines.html#C_3 + */ + private val minimizableElements: List[String] = + List("base", "meta", "link", "hr", "br", "param", "img", "area", "input", "col") + + def toXhtml( + x: Node, + pscope: NamespaceBinding = TopScope, + sb: StringBuilder = new StringBuilder, + stripComments: Boolean = false, + decodeEntities: Boolean = false, + preserveWhitespace: Boolean = false, + minimizeTags: Boolean = true + ): Unit = { + def decode(er: EntityRef): StringBuilder = XhtmlEntities.entMap.get(er.entityName) match { + case Some(chr) if chr.toInt >= 128 => sb.append(chr) + case _ => er.buildString(sb) + } + def shortForm: Boolean = + minimizeTags && + (x.child == null || x.child.isEmpty) && + minimizableElements.contains(x.label) + + x match { + case c: Comment => if (!stripComments) c.buildString(sb) + case er: EntityRef if decodeEntities => decode(er) + case x: SpecialNode => x.buildString(sb) + case g: Group => + g.nodes.foreach { toXhtml(_, x.scope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags) } + + case _ => + sb.append('<') + x.nameToString(sb) + if (x.attributes != null) x.attributes.buildString(sb) + x.scope.buildString(sb, pscope) + + if (shortForm) sb.append(" />") + else { + sb.append('>') + sequenceToXML(x.child, x.scope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags) + sb.append("') + } + } + } + + /** + * Amounts to calling toXhtml(node, ...) with the given parameters on each node. + */ + def sequenceToXML( + children: Seq[Node], + pscope: NamespaceBinding = TopScope, + sb: StringBuilder = new StringBuilder, + stripComments: Boolean = false, + decodeEntities: Boolean = false, + preserveWhitespace: Boolean = false, + minimizeTags: Boolean = true + ): Unit = if (children.nonEmpty) { + val doSpaces: Boolean = children.forall(isAtomAndNotText) // interleave spaces + for (c <- children.take(children.length - 1)) { + toXhtml(c, pscope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags) + if (doSpaces) sb.append(' ') + } + toXhtml(children.last, pscope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags) + } +} diff --git a/src/main/scala/scala/xml/dtd/ContentModel.scala b/shared/src/main/scala/scala/xml/dtd/ContentModel.scala similarity index 56% rename from src/main/scala/scala/xml/dtd/ContentModel.scala rename to shared/src/main/scala/scala/xml/dtd/ContentModel.scala index 73dcb8fc2..1c6b0a00d 100644 --- a/src/main/scala/scala/xml/dtd/ContentModel.scala +++ b/shared/src/main/scala/scala/xml/dtd/ContentModel.scala @@ -1,41 +1,55 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ package scala package xml package dtd +import scala.collection.Seq import scala.xml.dtd.impl._ import scala.xml.Utility.sbToString import PartialFunction._ +/* +@deprecated("Avoidance", since="2.10") +trait ContentModelLaundry extends WordExp +object ContentModelLaundry extends ContentModelLaundry { +} +*/ + object ContentModel extends WordExp { - type _labelT = ElemName - type _regexpT = RegExp - object Translator extends WordBerrySethi { + override type _labelT = ElemName + override type _regexpT = RegExp + + @deprecated("Avoidance", since="2.10") + trait Translator extends WordBerrySethi + object Translator extends Translator { override val lang: ContentModel.this.type = ContentModel.this } case class ElemName(name: String) extends Label { - override def toString() = """ElemName("%s")""" format name + override def toString: String = s"""ElemName("$name")""" } - def isMixed(cm: ContentModel) = cond(cm) { case _: MIXED => true } - def containsText(cm: ContentModel) = (cm == PCDATA) || isMixed(cm) - def parse(s: String): ContentModel = ContentModelParser.parse(s) + def isMixed(cm: ContentModel): Boolean = cond(cm) { case _: MIXED => true } + def containsText(cm: ContentModel): Boolean = (cm == PCDATA) || isMixed(cm) def getLabels(r: RegExp): Set[String] = { def traverse(r: RegExp): Set[String] = r match { // !!! check for match translation problem case Letter(ElemName(name)) => Set(name) case Star(x@_) => traverse(x) // bug if x@_* - case Sequ(xs@_*) => Set(xs flatMap traverse: _*) - case Alt(xs@_*) => Set(xs flatMap traverse: _*) + case Sequ(xs@_*) => Set(xs.flatMap(traverse): _*) + case Alt(xs@_*) => Set(xs.flatMap(traverse): _*) } traverse(r) @@ -44,19 +58,19 @@ object ContentModel extends WordExp { def buildString(r: RegExp): String = sbToString(buildString(r, _)) /* precond: rs.length >= 1 */ - private def buildString(rs: Seq[RegExp], sb: StringBuilder, sep: Char) { + private def buildString(rs: Seq[RegExp], sb: StringBuilder, sep: Char): Unit = { buildString(rs.head, sb) for (z <- rs.tail) { - sb append sep + sb.append(sep) buildString(z, sb) } } def buildString(c: ContentModel, sb: StringBuilder): StringBuilder = c match { - case ANY => sb append "ANY" - case EMPTY => sb append "EMPTY" - case PCDATA => sb append "(#PCDATA)" - case ELEMENTS(_) | MIXED(_) => c buildString sb + case ANY => sb.append("ANY") + case EMPTY => sb.append("EMPTY") + case PCDATA => sb.append("(#PCDATA)") + case ELEMENTS(_) | MIXED(_) => c.buildString(sb) } def buildString(r: RegExp, sb: StringBuilder): StringBuilder = @@ -72,14 +86,15 @@ object ContentModel extends WordExp { case Letter(ElemName(name)) => sb.append(name) } - } sealed abstract class ContentModel { - override def toString(): String = sbToString(buildString) + override def toString: String = sbToString(buildString) def buildString(sb: StringBuilder): StringBuilder } +import ContentModel.RegExp + case object PCDATA extends ContentModel { override def buildString(sb: StringBuilder): StringBuilder = sb.append("(#PCDATA)") } @@ -90,28 +105,28 @@ case object ANY extends ContentModel { override def buildString(sb: StringBuilder): StringBuilder = sb.append("ANY") } sealed abstract class DFAContentModel extends ContentModel { - import ContentModel.{ ElemName, Translator } - def r: ContentModel.RegExp + import ContentModel.{ElemName, Translator} + def r: RegExp lazy val dfa: DetWordAutom[ElemName] = { - val nfa = Translator.automatonFrom(r, 1) + val nfa: NondetWordAutom[ElemName] = Translator.automatonFrom(r, 1) new SubsetConstruction(nfa).determinize } } -case class MIXED(r: ContentModel.RegExp) extends DFAContentModel { - import ContentModel.{ Alt, RegExp } +case class MIXED(override val r: RegExp) extends DFAContentModel { + import ContentModel.Alt override def buildString(sb: StringBuilder): StringBuilder = { - val newAlt = r match { case Alt(rs@_*) => Alt(rs drop 1: _*) } + val newAlt: Alt = r match { case Alt(rs@_*) => Alt(rs.drop(1): _*) } - sb append "(#PCDATA|" + sb.append("(#PCDATA|") ContentModel.buildString(newAlt: RegExp, sb) - sb append ")*" + sb.append(")*") } } -case class ELEMENTS(r: ContentModel.RegExp) extends DFAContentModel { +case class ELEMENTS(override val r: RegExp) extends DFAContentModel { override def buildString(sb: StringBuilder): StringBuilder = ContentModel.buildString(r, sb) } diff --git a/shared/src/main/scala/scala/xml/dtd/DTD.scala b/shared/src/main/scala/scala/xml/dtd/DTD.scala new file mode 100644 index 000000000..9b334a8dc --- /dev/null +++ b/shared/src/main/scala/scala/xml/dtd/DTD.scala @@ -0,0 +1,37 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml +package dtd + +import scala.collection.mutable +import scala.collection.Seq + +/** + * A document type declaration. + * + * @author Burak Emir + */ +abstract class DTD { + var externalID: ExternalID = _ + var decls: List[Decl] = Nil + def notations: Seq[NotationDecl] = Nil + def unparsedEntities: Seq[EntityDecl] = Nil + + var elem: mutable.Map[String, ElemDecl] = new mutable.HashMap[String, ElemDecl]() + var attr: mutable.Map[String, AttListDecl] = new mutable.HashMap[String, AttListDecl]() + var ent: mutable.Map[String, EntityDecl] = new mutable.HashMap[String, EntityDecl]() + + override def toString: String = + s"DTD ${Option(externalID).getOrElse("")} [\n${decls.mkString("\n")}\n]" +} diff --git a/src/main/scala/scala/xml/dtd/Decl.scala b/shared/src/main/scala/scala/xml/dtd/Decl.scala similarity index 54% rename from src/main/scala/scala/xml/dtd/Decl.scala rename to shared/src/main/scala/scala/xml/dtd/Decl.scala index 6cb2a2210..c2cf53e18 100644 --- a/src/main/scala/scala/xml/dtd/Decl.scala +++ b/shared/src/main/scala/scala/xml/dtd/Decl.scala @@ -1,10 +1,14 @@ -/* __ *\ - ** ________ ___ / / ___ Scala API ** - ** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** - ** __\ \/ /__/ __ |/ /__/ __ | http://www.scala-lang.org/ ** - ** /____/\___/_/ |_/____/_/ | | ** - ** |/ ** - \* */ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ package scala package xml @@ -12,30 +16,38 @@ package dtd import Utility.sbToString +/** + * XML declarations + * + * - [[scala.xml.dtd.AttListDecl]] — Attribute list declaration (ATTLIST) + * - [[scala.xml.dtd.AttrDecl]] — Attribute declaration + * - [[scala.xml.dtd.ElemDecl]] — Element declaration (ELEMENT) + * - [[scala.xml.dtd.ParameterEntityDecl]] — Parameter entity list (ENTITY %) + * - [[scala.xml.dtd.ParsedEntityDecl]] — Parsed general entity list (ENTITY) + * - [[scala.xml.dtd.PEReference]] — Parsed entity reference + * - [[scala.xml.dtd.UnparsedEntityDecl]] — Unparsed entity list (ENTITY NDATA) + */ sealed abstract class Decl sealed abstract class MarkupDecl extends Decl { + override def toString: String = sbToString(buildString) def buildString(sb: StringBuilder): StringBuilder } /** * an element declaration */ -case class ElemDecl(name: String, contentModel: ContentModel) - extends MarkupDecl { +case class ElemDecl(name: String, contentModel: ContentModel) extends MarkupDecl { override def buildString(sb: StringBuilder): StringBuilder = { - sb append "' + sb.append('>') } } -case class AttListDecl(name: String, attrs: List[AttrDecl]) - extends MarkupDecl { - override def buildString(sb: StringBuilder): StringBuilder = { - sb append "") - } +case class AttListDecl(name: String, attrs: List[AttrDecl]) extends MarkupDecl { + override def buildString(sb: StringBuilder): StringBuilder = + sb.append(s"") } /** @@ -44,13 +56,12 @@ case class AttListDecl(name: String, attrs: List[AttrDecl]) * directly. */ case class AttrDecl(name: String, tpe: String, default: DefaultDecl) { - override def toString(): String = sbToString(buildString) + override def toString: String = sbToString(buildString) def buildString(sb: StringBuilder): StringBuilder = { - sb append " " append name append ' ' append tpe append ' ' - default buildString sb + sb.append(s" $name $tpe ") + default.buildString(sb) } - } /** an entity declaration */ @@ -59,31 +70,32 @@ sealed abstract class EntityDecl extends MarkupDecl /** a parsed general entity declaration */ case class ParsedEntityDecl(name: String, entdef: EntityDef) extends EntityDecl { override def buildString(sb: StringBuilder): StringBuilder = { - sb append "' + sb.append(s"') } } /** a parameter entity declaration */ case class ParameterEntityDecl(name: String, entdef: EntityDef) extends EntityDecl { override def buildString(sb: StringBuilder): StringBuilder = { - sb append "' + sb.append(s"') } } /** an unparsed entity declaration */ case class UnparsedEntityDecl(name: String, extID: ExternalID, notation: String) extends EntityDecl { override def buildString(sb: StringBuilder): StringBuilder = { - sb append "' + sb.append(s"") } } + /** a notation declaration */ case class NotationDecl(name: String, extID: ExternalID) extends MarkupDecl { override def buildString(sb: StringBuilder): StringBuilder = { - sb append "') } } @@ -92,21 +104,21 @@ sealed abstract class EntityDef { } case class IntDef(value: String) extends EntityDef { - private def validateValue() { - var tmp = value - var ix = tmp indexOf '%' + private def validateValue(): Unit = { + var tmp: String = value + var ix: Int = tmp.indexOf('%') while (ix != -1) { - val iz = tmp.indexOf(';', ix) + val iz: Int = tmp.indexOf(';', ix) if (iz == -1 && iz == ix + 1) throw new IllegalArgumentException("no % allowed in entity value, except for parameter-entity-references") else { - val n = tmp.substring(ix, iz) + val n: String = tmp.substring(ix, iz) if (!Utility.isName(n)) - throw new IllegalArgumentException("internal entity def: \"" + n + "\" must be an XML Name") + throw new IllegalArgumentException(s"""internal entity def: "$n" must be an XML Name""") tmp = tmp.substring(iz + 1, tmp.length) - ix = tmp indexOf '%' + ix = tmp.indexOf('%') } } } @@ -119,7 +131,7 @@ case class IntDef(value: String) extends EntityDef { case class ExtDef(extID: ExternalID) extends EntityDef { override def buildString(sb: StringBuilder): StringBuilder = - extID buildString sb + extID.buildString(sb) } /** a parsed entity reference */ @@ -128,30 +140,30 @@ case class PEReference(ent: String) extends MarkupDecl { throw new IllegalArgumentException("ent must be an XML Name") override def buildString(sb: StringBuilder): StringBuilder = - sb append '%' append ent append ';' + sb.append(s"%$ent;") } // default declarations for attributes sealed abstract class DefaultDecl { - override def toString(): String + def toString: String def buildString(sb: StringBuilder): StringBuilder } case object REQUIRED extends DefaultDecl { - override def toString(): String = "#REQUIRED" - override def buildString(sb: StringBuilder) = sb append "#REQUIRED" + override def toString: String = "#REQUIRED" + override def buildString(sb: StringBuilder): StringBuilder = sb.append("#REQUIRED") } case object IMPLIED extends DefaultDecl { - override def toString(): String = "#IMPLIED" - override def buildString(sb: StringBuilder) = sb append "#IMPLIED" + override def toString: String = "#IMPLIED" + override def buildString(sb: StringBuilder): StringBuilder = sb.append("#IMPLIED") } case class DEFAULT(fixed: Boolean, attValue: String) extends DefaultDecl { - override def toString(): String = sbToString(buildString) + override def toString: String = sbToString(buildString) override def buildString(sb: StringBuilder): StringBuilder = { - if (fixed) sb append "#FIXED " + if (fixed) sb.append("#FIXED ") Utility.appendEscapedQuoted(attValue, sb) } } diff --git a/src/main/scala/scala/xml/dtd/DocType.scala b/shared/src/main/scala/scala/xml/dtd/DocType.scala similarity index 50% rename from src/main/scala/scala/xml/dtd/DocType.scala rename to shared/src/main/scala/scala/xml/dtd/DocType.scala index af48110b9..1318e3b21 100644 --- a/src/main/scala/scala/xml/dtd/DocType.scala +++ b/shared/src/main/scala/scala/xml/dtd/DocType.scala @@ -1,15 +1,21 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ package scala package xml package dtd +import scala.collection.Seq + /** * An XML node for document type declaration. * @@ -21,15 +27,15 @@ package dtd */ case class DocType(name: String, extID: ExternalID, intSubset: Seq[dtd.Decl]) { if (!Utility.isName(name)) - throw new IllegalArgumentException(name + " must be an XML Name") + throw new IllegalArgumentException(s"$name must be an XML Name") /** returns "<!DOCTYPE + name + extID? + ("["+intSubSet+"]")? >" */ - final override def toString() = { - def intString = + final override def toString: String = { + def intString: String = if (intSubset.isEmpty) "" else intSubset.mkString("[", "", "]") - """""".format(name, extID.toString(), intString) + s"" } } diff --git a/src/main/scala/scala/xml/dtd/ExternalID.scala b/shared/src/main/scala/scala/xml/dtd/ExternalID.scala similarity index 51% rename from src/main/scala/scala/xml/dtd/ExternalID.scala rename to shared/src/main/scala/scala/xml/dtd/ExternalID.scala index 7baa46677..80b9da867 100644 --- a/src/main/scala/scala/xml/dtd/ExternalID.scala +++ b/shared/src/main/scala/scala/xml/dtd/ExternalID.scala @@ -1,10 +1,14 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://www.scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ package scala package xml @@ -16,23 +20,20 @@ package dtd * @author Burak Emir */ sealed abstract class ExternalID extends parsing.TokenTests { - def quoted(s: String) = { - val c = if (s contains '"') '\'' else '"' - c + s + c + def quoted(s: String): String = { + val c: Char = if (s.contains('"')) '\'' else '"' + s"$c$s$c" } // public != null: PUBLIC " " publicLiteral " " [systemLiteral] // public == null: SYSTEM " " systemLiteral - override def toString(): String = { - lazy val quotedSystemLiteral = quoted(systemId) - lazy val quotedPublicLiteral = quoted(publicId) + override def toString: String = + if (publicId == null) s"SYSTEM ${quoted(systemId)}" else + if (systemId == null) s"PUBLIC ${quoted(publicId)}" else + s"PUBLIC ${quoted(publicId)} ${quoted(systemId)}" - if (publicId == null) "SYSTEM " + quotedSystemLiteral - else "PUBLIC " + quotedPublicLiteral + - (if (systemId == null) "" else " " + quotedSystemLiteral) - } def buildString(sb: StringBuilder): StringBuilder = - sb.append(this.toString()) + sb.append(this.toString) def systemId: String def publicId: String @@ -44,8 +45,8 @@ sealed abstract class ExternalID extends parsing.TokenTests { * @author Burak Emir * @param systemId the system identifier literal */ -case class SystemID(systemId: String) extends ExternalID { - val publicId = null +case class SystemID(override val systemId: String) extends ExternalID { + override val publicId: scala.Null = null if (!checkSysID(systemId)) throw new IllegalArgumentException("can't use both \" and ' in systemId") @@ -58,7 +59,7 @@ case class SystemID(systemId: String) extends ExternalID { * @param publicId the public identifier literal * @param systemId (can be null for notation pubIDs) the system identifier literal */ -case class PublicID(publicId: String, systemId: String) extends ExternalID { +case class PublicID(override val publicId: String, override val systemId: String) extends ExternalID { if (!checkPubID(publicId)) throw new IllegalArgumentException("publicId must consist of PubidChars") @@ -66,13 +67,13 @@ case class PublicID(publicId: String, systemId: String) extends ExternalID { throw new IllegalArgumentException("can't use both \" and ' in systemId") /** the constant "#PI" */ - def label = "#PI" + def label: String = "#PI" /** always empty */ - def attribute = Node.NoAttributes + def attribute: ScalaVersionSpecificReturnTypes.ExternalIDAttribute = Node.NoAttributes /** always empty */ - def child = Nil + def child: Nil.type = Nil } /** @@ -81,8 +82,8 @@ case class PublicID(publicId: String, systemId: String) extends ExternalID { * @author Michael Bayne */ object NoExternalID extends ExternalID { - val publicId = null - val systemId = null + override val publicId: ScalaVersionSpecificReturnTypes.NoExternalIDId = null + override val systemId: ScalaVersionSpecificReturnTypes.NoExternalIDId = null - override def toString = "" + override def toString: String = "" } diff --git a/shared/src/main/scala/scala/xml/dtd/Tokens.scala b/shared/src/main/scala/scala/xml/dtd/Tokens.scala new file mode 100644 index 000000000..48696af58 --- /dev/null +++ b/shared/src/main/scala/scala/xml/dtd/Tokens.scala @@ -0,0 +1,46 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml +package dtd + +class Tokens { + + // Tokens + + final val TOKEN_PCDATA: Int = 0 + final val NAME: Int = 1 + final val LPAREN: Int = 3 + final val RPAREN: Int = 4 + final val COMMA: Int = 5 + final val STAR: Int = 6 + final val PLUS: Int = 7 + final val OPT: Int = 8 + final val CHOICE: Int = 9 + final val END: Int = 10 + final val S: Int = 13 + + final def token2string(i: Int): String = i match { + case 0 => "#PCDATA" + case 1 => "NAME" + case 3 => "(" + case 4 => ")" + case 5 => "," + case 6 => "*" + case 7 => "+" + case 8 => "?" + case 9 => "|" + case 10 => "END" + case 13 => " " + } +} diff --git a/shared/src/main/scala/scala/xml/dtd/ValidationException.scala b/shared/src/main/scala/scala/xml/dtd/ValidationException.scala new file mode 100644 index 000000000..01d4cad02 --- /dev/null +++ b/shared/src/main/scala/scala/xml/dtd/ValidationException.scala @@ -0,0 +1,44 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml +package dtd + +case class ValidationException(e: String) extends Exception(e) + +/** + * @author Burak Emir + */ +object MakeValidationException { + def fromFixedAttribute(k: String, value: String, actual: String): ValidationException = + ValidationException(s"""value of attribute $k FIXED to "$value", but document tries "$actual"""") + + def fromNonEmptyElement(): ValidationException = + ValidationException("element should be *empty*") + + def fromUndefinedElement(label: String): ValidationException = + ValidationException(s"""element "$label" not allowed here""") + + def fromUndefinedAttribute(key: String): ValidationException = + ValidationException(s"attribute $key not allowed here") + + def fromMissingAttribute(allKeys: Set[String]): ValidationException = { + val sb: StringBuilder = new StringBuilder("missing value for REQUIRED attribute") + if (allKeys.size > 1) sb.append('s') + allKeys.foreach(k => sb.append(s"'$k'")) + ValidationException(sb.toString) + } + + def fromMissingAttribute(key: String, tpe: String): ValidationException = + ValidationException(s"missing value for REQUIRED attribute $key of type $tpe") +} diff --git a/shared/src/main/scala/scala/xml/dtd/impl/Base.scala b/shared/src/main/scala/scala/xml/dtd/impl/Base.scala new file mode 100644 index 000000000..fa4e64929 --- /dev/null +++ b/shared/src/main/scala/scala/xml/dtd/impl/Base.scala @@ -0,0 +1,69 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml.dtd.impl + +/** + * Basic regular expressions. + * + * @author Burak Emir + */ + +@deprecated("This class will be removed", "2.10.0") +private[dtd] abstract class Base { + type _regexpT <: RegExp + + abstract class RegExp { + def isNullable: Boolean + } + + object Alt { + /** `Alt( R,R,R* )`. */ + def apply(rs: _regexpT*): Alt = + if (rs.size < 2) throw new SyntaxError("need at least 2 branches in Alt") + else new Alt(rs: _*) + // Can't enforce that statically without changing the interface + // def apply(r1: _regexpT, r2: _regexpT, rs: _regexpT*) = new Alt(Seq(r1, r2) ++ rs: _*) + def unapplySeq(x: Alt): Some[Seq[_regexpT]] = Some(x.rs) + } + + class Alt private (val rs: _regexpT*) extends RegExp { + final override val isNullable: Boolean = rs.exists(_.isNullable) + } + + object Sequ { + /** Sequ( R,R* ) */ + def apply(rs: _regexpT*): RegExp = if (rs.isEmpty) Eps else new Sequ(rs: _*) + def unapplySeq(x: Sequ): Some[Seq[_regexpT]] = Some(x.rs) + } + + class Sequ private (val rs: _regexpT*) extends RegExp { + final override val isNullable: Boolean = rs.forall(_.isNullable) + } + + case class Star(r: _regexpT) extends RegExp { + final override lazy val isNullable: Boolean = true + } + + // The empty Sequ. + case object Eps extends RegExp { + final override lazy val isNullable: Boolean = true + override def toString: String = "Eps" + } + + /** this class can be used to add meta information to regexps. */ + class Meta(r1: _regexpT) extends RegExp { + final override val isNullable: Boolean = r1.isNullable + def r: _regexpT = r1 + } +} diff --git a/src/main/scala/scala/xml/dtd/impl/BaseBerrySethi.scala b/shared/src/main/scala/scala/xml/dtd/impl/BaseBerrySethi.scala similarity index 65% rename from src/main/scala/scala/xml/dtd/impl/BaseBerrySethi.scala rename to shared/src/main/scala/scala/xml/dtd/impl/BaseBerrySethi.scala index fe3159c0f..267277e0e 100644 --- a/src/main/scala/scala/xml/dtd/impl/BaseBerrySethi.scala +++ b/shared/src/main/scala/scala/xml/dtd/impl/BaseBerrySethi.scala @@ -1,21 +1,26 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ package scala package xml.dtd.impl import scala.collection.{ mutable, immutable } +import scala.collection.Seq // todo: replace global variable pos with acc /** * This class turns a regular expression over `A` into a - * [[scala.util.automata.NondetWordAutom]] over `A` using the celebrated + * [[scala.xml.dtd.impl.NondetWordAutom]] over `A` using the celebrated * position automata construction (also called ''Berry-Sethi'' or ''Glushkov''). */ @deprecated("This class will be removed", "2.10.0") @@ -23,7 +28,7 @@ private[dtd] abstract class BaseBerrySethi { val lang: Base import lang.{ Alt, Eps, Meta, RegExp, Sequ, Star } - protected var pos = 0 + protected var pos: Int = 0 // results which hold all info for the NondetWordAutomaton protected var follow: mutable.HashMap[Int, Set[Int]] = _ @@ -36,15 +41,15 @@ private[dtd] abstract class BaseBerrySethi { final val emptySet: Set[Int] = Set() - private def doComp(r: RegExp, compFunction: RegExp => Set[Int]) = r match { - case x: Alt => (x.rs map compFirst).foldLeft(emptySet)(_ ++ _) + private def doComp(r: RegExp, compFunction: RegExp => Set[Int]): Set[Int] = r match { + case x: Alt => x.rs.map(compFirst).foldLeft(emptySet)(_ ++ _) case Eps => emptySet case x: Meta => compFunction(x.r) case x: Sequ => - val (l1, l2) = x.rs span (_.isNullable) - ((l1 ++ (l2 take 1)) map compFunction).foldLeft(emptySet)(_ ++ _) + val (l1: Seq[lang._regexpT], l2: Seq[lang._regexpT]) = x.rs.span(_.isNullable) + (l1 ++ l2.take(1)).map(compFunction).foldLeft(emptySet)(_ ++ _) case Star(t) => compFunction(t) - case _ => throw new IllegalArgumentException("unexpected pattern " + r.getClass) + case _ => throw new IllegalArgumentException(s"unexpected pattern ${r.getClass}") } /** Computes `first(r)` for the word regexp `r`. */ @@ -62,7 +67,7 @@ private[dtd] abstract class BaseBerrySethi { follow(0) = if (rs.isEmpty) emptySet else rs.foldRight(Set(pos))((p, fol) => { - val first = compFollow1(fol, p) + val first: Set[Int] = compFollow1(fol, p) if (p.isNullable) fol ++ first else first @@ -80,12 +85,12 @@ private[dtd] abstract class BaseBerrySethi { case x: Star => compFollow1(fol1 ++ compFirst(x.r), x.r) case x: Sequ => x.rs.foldRight(fol1) { (p, fol) => - val first = compFollow1(fol, p) + val first: Set[Int] = compFollow1(fol, p) if (p.isNullable) fol ++ first else first } - case _ => throw new IllegalArgumentException("unexpected pattern: " + r.getClass) + case _ => throw new IllegalArgumentException(s"unexpected pattern: ${r.getClass}") } /** @@ -93,10 +98,10 @@ private[dtd] abstract class BaseBerrySethi { */ protected def traverse(r: RegExp): Unit = r match { // (is tree automaton stuff, more than Berry-Sethi) - case x: Alt => x.rs foreach traverse - case x: Sequ => x.rs foreach traverse + case x: Alt => x.rs.foreach(traverse) + case x: Sequ => x.rs.foreach(traverse) case x: Meta => traverse(x.r) case Star(t) => traverse(t) - case _ => throw new IllegalArgumentException("unexp pattern " + r.getClass) + case _ => throw new IllegalArgumentException(s"unexp pattern ${r.getClass}") } } diff --git a/shared/src/main/scala/scala/xml/dtd/impl/DetWordAutom.scala b/shared/src/main/scala/scala/xml/dtd/impl/DetWordAutom.scala new file mode 100644 index 000000000..944024518 --- /dev/null +++ b/shared/src/main/scala/scala/xml/dtd/impl/DetWordAutom.scala @@ -0,0 +1,48 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml.dtd.impl + +/** + * A deterministic automaton. States are integers, where + * 0 is always the only initial state. Transitions are represented + * in the delta function. A default transitions is one that + * is taken when no other transition can be taken. + * All states are reachable. Accepting states are those for which + * the partial function 'finals' is defined. + * + * @author Burak Emir + */ +// TODO: still used in ContentModel -- @deprecated("This class will be removed", "2.10.0") +private[dtd] abstract class DetWordAutom[T <: AnyRef] { + val nstates: Int + val finals: Array[Int] + val delta: Array[scala.collection.mutable.Map[T, Int]] + val default: Array[Int] + + def isFinal(q: Int): Boolean = finals(q) != 0 + def isSink(q: Int): Boolean = delta(q).isEmpty && default(q) == q + def next(q: Int, label: T): Int = delta(q).getOrElse(label, default(q)) + + override def toString: String = { + val map: Map[Int, Int] = finals.zipWithIndex.map(_.swap).toMap + val sb: StringBuilder = new StringBuilder(s"[DetWordAutom nstates=$nstates finals=$map delta=\n") + + for (i <- 0.until(nstates)) { + sb.append(s"$i->${delta(i)}\n") + if (i < default.length) + sb.append(s"_>${default(i)}\n") + } + sb.toString + } +} diff --git a/shared/src/main/scala/scala/xml/dtd/impl/Inclusion.scala b/shared/src/main/scala/scala/xml/dtd/impl/Inclusion.scala new file mode 100644 index 000000000..67ea19e4b --- /dev/null +++ b/shared/src/main/scala/scala/xml/dtd/impl/Inclusion.scala @@ -0,0 +1,74 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml.dtd.impl + +import scala.collection.Seq + +/** + * A fast test of language inclusion between minimal automata. + * inspired by the ''AMoRE automata library''. + * + * @author Burak Emir + */ +@deprecated("This class will be removed", "2.10.0") +private[dtd] trait Inclusion[A <: AnyRef] { + + val labels: Seq[A] + + /** + * Returns true if `dfa1` is included in `dfa2`. + */ + def inclusion(dfa1: DetWordAutom[A], dfa2: DetWordAutom[A]): Boolean = { + + def encode(q1: Int, q2: Int): Int = 1 + q1 + q2 * dfa1.nstates + def decode2(c: Int): Int = (c - 1) / dfa1.nstates //integer division + def decode1(c: Int): Int = (c - 1) % dfa1.nstates + + var q1: Int = 0 //dfa1.initstate; // == 0 + var q2: Int = 0 //dfa2.initstate; // == 0 + + val max: Int = 1 + dfa1.nstates * dfa2.nstates + val mark: Array[Int] = new Array[Int](max) + + var result: Boolean = true + var current: Int = encode(q1, q2) + var last: Int = current + mark(last) = max // mark (q1,q2) + while (current != 0 && result) { + //Console.println("current = [["+q1+" "+q2+"]] = "+current); + for (letter <- labels) { + val r1: Int = dfa1.next(q1, letter) + val r2: Int = dfa2.next(q2, letter) + if (dfa1.isFinal(r1) && !dfa2.isFinal(r2)) + result = false + val test: Int = encode(r1, r2) + //Console.println("test = [["+r1+" "+r2+"]] = "+test); + if (mark(test) == 0) { + mark(last) = test + mark(test) = max + last = test + } + } + val ncurrent: Int = mark(current) + if (ncurrent != max) { + q1 = decode1(ncurrent) + q2 = decode2(ncurrent) + current = ncurrent + } else { + current = 0 + } + } + result + } +} diff --git a/src/main/scala/scala/xml/dtd/impl/NondetWordAutom.scala b/shared/src/main/scala/scala/xml/dtd/impl/NondetWordAutom.scala similarity index 50% rename from src/main/scala/scala/xml/dtd/impl/NondetWordAutom.scala rename to shared/src/main/scala/scala/xml/dtd/impl/NondetWordAutom.scala index 5079d932d..1bfefcc35 100644 --- a/src/main/scala/scala/xml/dtd/impl/NondetWordAutom.scala +++ b/shared/src/main/scala/scala/xml/dtd/impl/NondetWordAutom.scala @@ -1,15 +1,20 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ package scala package xml.dtd.impl -import scala.collection.{ immutable, mutable } +import scala.collection.{immutable, mutable} +import scala.collection.Seq /** * A nondeterministic automaton. States are integers, where @@ -28,16 +33,16 @@ private[dtd] abstract class NondetWordAutom[T <: AnyRef] { val default: Array[immutable.BitSet] /** @return true if the state is final */ - final def isFinal(state: Int) = finals(state) > 0 + final def isFinal(state: Int): Boolean = finals(state) > 0 /** @return tag of final state */ - final def finalTag(state: Int) = finals(state) + final def finalTag(state: Int): Int = finals(state) /** @return true if the set of states contains at least one final state */ - final def containsFinal(Q: immutable.BitSet): Boolean = Q exists isFinal + final def containsFinal(Q: immutable.BitSet): Boolean = Q.exists(isFinal) /** @return true if there are no accepting states */ - final def isEmpty = (0 until nstates) forall (x => !isFinal(x)) + final def isEmpty: Boolean = 0.until(nstates).forall(x => !isFinal(x)) /** @return a immutable.BitSet with the next states for given state and label */ def next(q: Int, a: T): immutable.BitSet = delta(q).getOrElse(a, default(q)) @@ -46,16 +51,15 @@ private[dtd] abstract class NondetWordAutom[T <: AnyRef] { def next(Q: immutable.BitSet, a: T): immutable.BitSet = next(Q, next(_, a)) def nextDefault(Q: immutable.BitSet): immutable.BitSet = next(Q, default) - private def next(Q: immutable.BitSet, f: (Int) => immutable.BitSet): immutable.BitSet = - (Q map f).foldLeft(immutable.BitSet.empty)(_ ++ _) - - private def finalStates = 0 until nstates filter isFinal - override def toString = { + private def next(Q: immutable.BitSet, f: Int => immutable.BitSet): immutable.BitSet = + Q.toSet.map(f).foldLeft(immutable.BitSet.empty)(_ ++ _) - val finalString = Map(finalStates map (j => j -> finals(j)): _*).toString - val deltaString = (0 until nstates) - .map(i => " %d->%s\n _>%s\n".format(i, delta(i), default(i))).mkString + private def finalStates: immutable.Seq[Int] = 0.until(nstates).filter(isFinal) + override def toString: String = { + val finalString: String = Map(finalStates.map(j => j -> finals(j)): _*).toString + val deltaString: String = 0.until(nstates) + .map(i => s" $i->${delta(i)}\n _>${default(i)}\n").mkString - "[NondetWordAutom nstates=%d finals=%s delta=\n%s".format(nstates, finalString, deltaString) + s"[NondetWordAutom nstates=$nstates finals=$finalString delta=\n$deltaString" } } diff --git a/shared/src/main/scala/scala/xml/dtd/impl/SubsetConstruction.scala b/shared/src/main/scala/scala/xml/dtd/impl/SubsetConstruction.scala new file mode 100644 index 000000000..37eaea1ca --- /dev/null +++ b/shared/src/main/scala/scala/xml/dtd/impl/SubsetConstruction.scala @@ -0,0 +1,114 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml.dtd.impl + +import scala.collection.{immutable, mutable} + +// TODO: still used in ContentModel -- @deprecated("This class will be removed", "2.10.0") +private[dtd] class SubsetConstruction[T <: AnyRef](val nfa: NondetWordAutom[T]) { + import nfa.labels + + def selectTag(Q: immutable.BitSet, finals: Array[Int]): Int = + Q.map(finals).filter(_ > 0).min + + def determinize: DetWordAutom[T] = { + // for assigning numbers to bitsets + val indexMap: mutable.Map[immutable.BitSet, Int] = mutable.Map[immutable.BitSet, Int]() + val invIndexMap: mutable.Map[Int, immutable.BitSet] = mutable.Map[Int, immutable.BitSet]() + var ix: Int = 0 + + // we compute the dfa with states = bitsets + val q0: immutable.BitSet = immutable.BitSet(0) // the set { 0 } + val sink: immutable.BitSet = immutable.BitSet.empty // the set { } + + var states: Set[immutable.BitSet] = Set(q0, sink) // initial set of sets + val delta: mutable.HashMap[immutable.BitSet, mutable.HashMap[T, immutable.BitSet]] = + new mutable.HashMap[immutable.BitSet, mutable.HashMap[T, immutable.BitSet]] + val deftrans: mutable.Map[immutable.BitSet, immutable.BitSet] = mutable.Map(q0 -> sink, sink -> sink) // initial transitions + val finals: mutable.Map[immutable.BitSet, Int] = mutable.Map() + var rest: immutable.List[immutable.BitSet] = immutable.List.empty[immutable.BitSet] + + rest = q0 :: sink :: rest + + def addFinal(q: immutable.BitSet): Unit = { + if (nfa.containsFinal(q)) + finals(q) = selectTag(q, nfa.finals) + } + def add(Q: immutable.BitSet): Unit = { + if (!states(Q)) { + states += Q + rest = Q :: rest + addFinal(Q) + } + } + + addFinal(q0) // initial state may also be a final state + + while (rest.nonEmpty) { + val P: immutable.BitSet = rest.head + rest = rest.tail + // assign a number to this bitset + indexMap(P) = ix + invIndexMap(ix) = P + ix += 1 + + // make transition map + val Pdelta: mutable.HashMap[T, immutable.BitSet] = new mutable.HashMap[T, immutable.BitSet] + delta.update(P, Pdelta) + + labels.foreach { label => + val Q: immutable.BitSet = nfa.next(P, label) + Pdelta.update(label, Q) + add(Q) + } + + // collect default transitions + val Pdef: immutable.BitSet = nfa nextDefault P + deftrans(P) = Pdef + add(Pdef) + } + + // create DetWordAutom, using indices instead of sets + val nstatesR: Int = states.size + val deltaR: Array[mutable.Map[T, Int]] = new Array[mutable.Map[T, Int]](nstatesR) + val defaultR: Array[Int] = new Array[Int](nstatesR) + val finalsR: Array[Int] = new Array[Int](nstatesR) + + for (Q <- states) { + val q: Int = indexMap(Q) + val trans: mutable.Map[T, immutable.BitSet] = delta(Q) + val transDef: immutable.BitSet = deftrans(Q) + val qDef: Int = indexMap(transDef) + val ntrans: mutable.Map[T, Int] = new mutable.HashMap[T, Int]() + + for ((label, value) <- trans) { + val p: Int = indexMap(value) + if (p != qDef) + ntrans.update(label, p) + } + + deltaR(q) = ntrans + defaultR(q) = qDef + } + + finals.foreach { case (k, v) => finalsR(indexMap(k)) = v } + + new DetWordAutom[T] { + override val nstates: Int = nstatesR + override val delta: Array[mutable.Map[T, Int]] = deltaR + override val default: Array[Int] = defaultR + override val finals: Array[Int] = finalsR + } + } +} diff --git a/shared/src/main/scala/scala/xml/dtd/impl/SyntaxError.scala b/shared/src/main/scala/scala/xml/dtd/impl/SyntaxError.scala new file mode 100644 index 000000000..46d7c4713 --- /dev/null +++ b/shared/src/main/scala/scala/xml/dtd/impl/SyntaxError.scala @@ -0,0 +1,23 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml.dtd.impl + +/** + * This runtime exception is thrown if an attempt to instantiate a + * syntactically incorrect expression is detected. + * + * @author Burak Emir + */ +@deprecated("This class will be removed", "2.10.0") +private[dtd] class SyntaxError(e: String) extends RuntimeException(e) diff --git a/src/main/scala/scala/xml/dtd/impl/WordBerrySethi.scala b/shared/src/main/scala/scala/xml/dtd/impl/WordBerrySethi.scala similarity index 70% rename from src/main/scala/scala/xml/dtd/impl/WordBerrySethi.scala rename to shared/src/main/scala/scala/xml/dtd/impl/WordBerrySethi.scala index 3acc06435..3aee10a30 100644 --- a/src/main/scala/scala/xml/dtd/impl/WordBerrySethi.scala +++ b/shared/src/main/scala/scala/xml/dtd/impl/WordBerrySethi.scala @@ -1,28 +1,33 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ package scala package xml.dtd.impl -import scala.collection.{ immutable, mutable } +import scala.collection.{immutable, mutable} +import scala.collection.Seq /** - * This class turns a regular expression into a [[scala.util.automata.NondetWordAutom]] + * This class turns a regular expression into a [[scala.xml.dtd.impl.NondetWordAutom]] * celebrated position automata construction (also called ''Berry-Sethi'' or ''Glushkov''). * * @author Burak Emir - * @version 1.0 */ // TODO: still used in ContentModel -- @deprecated("This class will be removed", "2.10.0") +@deprecated("This class will be removed", "2.10.0") private[dtd] abstract class WordBerrySethi extends BaseBerrySethi { override val lang: WordExp - import lang.{ Alt, Eps, Letter, RegExp, Sequ, Star, _labelT } + import lang.{ Eps, Letter, RegExp, Sequ, _labelT } protected var labels: mutable.HashSet[_labelT] = _ // don't let this fool you, only labelAt is a real, surjective mapping @@ -73,7 +78,7 @@ private[dtd] abstract class WordBerrySethi extends BaseBerrySethi { */ /** Called at the leaves of the regexp */ - protected def seenLabel(r: RegExp, i: Int, label: _labelT) { + protected def seenLabel(r: RegExp, i: Int, label: _labelT): Unit = { labelAt = labelAt.updated(i, label) this.labels += label } @@ -92,8 +97,8 @@ private[dtd] abstract class WordBerrySethi extends BaseBerrySethi { case _ => super.traverse(r) } - protected def makeTransition(src: Int, dest: Int, label: _labelT) { - val q = deltaq(src) + protected def makeTransition(src: Int, dest: Int, label: _labelT): Unit = { + val q: mutable.Map[lang._labelT, List[Int]] = deltaq(src) q.update(label, dest :: q.getOrElse(label, Nil)) } @@ -104,24 +109,24 @@ private[dtd] abstract class WordBerrySethi extends BaseBerrySethi { this.pos = 0 // determine "Sethi-length" of the regexp - subexpr foreach traverse + subexpr.foreach(traverse) this.initials = Set(0) } - protected def initializeAutom() { + protected def initializeAutom(): Unit = { finals = immutable.Map.empty[Int, Int] // final states deltaq = new Array[mutable.HashMap[_labelT, List[Int]]](pos) // delta defaultq = new Array[List[Int]](pos) // default transitions - for (j <- 0 until pos) { + for (j <- 0.until(pos)) { deltaq(j) = mutable.HashMap[_labelT, List[Int]]() defaultq(j) = Nil } } protected def collectTransitions(): Unit = // make transitions - for (j <- 0 until pos; fol = follow(j); k <- fol) { + for (j <- 0.until(pos); fol = follow(j); k <- fol) { if (pos == k) finals = finals.updated(j, finalTag) else makeTransition(j, k, labelAt(k)) } @@ -143,22 +148,22 @@ private[dtd] abstract class WordBerrySethi extends BaseBerrySethi { if (x.isNullable) // initial state is final finals = finals.updated(0, finalTag) - val delta1 = immutable.Map(deltaq.zipWithIndex map (_.swap): _*) - val finalsArr = (0 until pos map (k => finals.getOrElse(k, 0))).toArray // 0 == not final + val delta1: immutable.Map[Int, mutable.HashMap[lang._labelT, List[Int]]] = deltaq.zipWithIndex.map(_.swap).toMap + val finalsArr: Array[Int] = 0.until(pos).map(k => finals.getOrElse(k, 0)).toArray // 0 == not final val deltaArr: Array[mutable.Map[_labelT, immutable.BitSet]] = - (0 until pos map { x => - mutable.HashMap(delta1(x).toSeq map { case (k, v) => k -> immutable.BitSet(v: _*) }: _*) - }).toArray + 0.until(pos).map { x => + mutable.HashMap(delta1(x).toSeq.map { case (k, v) => k -> immutable.BitSet(v: _*) }: _*) + }.toArray - val defaultArr = (0 until pos map (k => immutable.BitSet(defaultq(k): _*))).toArray + val defaultArr: Array[immutable.BitSet] = 0.until(pos).map(k => immutable.BitSet(defaultq(k): _*)).toArray new NondetWordAutom[_labelT] { - val nstates = pos - val labels = WordBerrySethi.this.labels.toList - val finals = finalsArr - val delta = deltaArr - val default = defaultArr + override val nstates: Int = pos + override val labels: Seq[lang._labelT] = WordBerrySethi.this.labels.toList + override val finals: Array[Int] = finalsArr + override val delta: Array[mutable.Map[lang._labelT, immutable.BitSet]] = deltaArr + override val default: Array[immutable.BitSet] = defaultArr } case z => automatonFrom(Sequ(z.asInstanceOf[this.lang._regexpT]), finalTag) diff --git a/src/main/scala/scala/xml/dtd/impl/WordExp.scala b/shared/src/main/scala/scala/xml/dtd/impl/WordExp.scala similarity index 61% rename from src/main/scala/scala/xml/dtd/impl/WordExp.scala rename to shared/src/main/scala/scala/xml/dtd/impl/WordExp.scala index 86e99b8fb..c5b07d7ff 100644 --- a/src/main/scala/scala/xml/dtd/impl/WordExp.scala +++ b/shared/src/main/scala/scala/xml/dtd/impl/WordExp.scala @@ -1,10 +1,14 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ package scala package xml.dtd.impl @@ -35,23 +39,22 @@ package xml.dtd.impl * }}} * * @author Burak Emir - * @version 1.0 */ // TODO: still used in ContentModel -- @deprecated("This class will be removed", "2.10.0") private[dtd] abstract class WordExp extends Base { abstract class Label - type _regexpT <: RegExp + override type _regexpT <: RegExp type _labelT <: Label case class Letter(a: _labelT) extends RegExp { - final lazy val isNullable = false - var pos = -1 + final override lazy val isNullable: Boolean = false + var pos: Int = -1 } case class Wildcard() extends RegExp { - final lazy val isNullable = false - var pos = -1 + final override lazy val isNullable: Boolean = false + var pos: Int = -1 } } diff --git a/src/main/scala/scala/xml/factory/NodeFactory.scala b/shared/src/main/scala/scala/xml/factory/NodeFactory.scala similarity index 51% rename from src/main/scala/scala/xml/factory/NodeFactory.scala rename to shared/src/main/scala/scala/xml/factory/NodeFactory.scala index dfb2cc0bb..7f7a376fa 100644 --- a/src/main/scala/scala/xml/factory/NodeFactory.scala +++ b/shared/src/main/scala/scala/xml/factory/NodeFactory.scala @@ -1,37 +1,40 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ package scala package xml package factory -import parsing.{ FactoryAdapter, NoBindingFactoryAdapter } -import java.io.{ InputStream, Reader, StringReader, File, FileDescriptor, FileInputStream } +import scala.collection.Seq trait NodeFactory[A <: Node] { - val ignoreComments = false - val ignoreProcInstr = false + val ignoreComments: Boolean = false + val ignoreProcInstr: Boolean = false /* default behaviour is to use hash-consing */ - val cache = new scala.collection.mutable.HashMap[Int, List[A]] + val cache: scala.collection.mutable.HashMap[Int, List[A]] = new scala.collection.mutable.HashMap[Int, List[A]] protected def create(pre: String, name: String, attrs: MetaData, scope: NamespaceBinding, children: Seq[Node]): A protected def construct(hash: Int, old: List[A], pre: String, name: String, attrSeq: MetaData, scope: NamespaceBinding, children: Seq[Node]): A = { - val el = create(pre, name, attrSeq, scope, children) + val el: A = create(pre, name, attrSeq, scope, children) cache.update(hash, el :: old) el } def eqElements(ch1: Seq[Node], ch2: Seq[Node]): Boolean = - ch1.view.zipAll(ch2.view, null, null) forall { case (x, y) => x eq y } + ch1.view.zipAll(ch2.view, null, null).forall { case (x, y) => x.eq(y) } - def nodeEquals(n: Node, pre: String, name: String, attrSeq: MetaData, scope: NamespaceBinding, children: Seq[Node]) = + def nodeEquals(n: Node, pre: String, name: String, attrSeq: MetaData, scope: NamespaceBinding, children: Seq[Node]): Boolean = n.prefix == pre && n.label == name && n.attributes == attrSeq && @@ -39,10 +42,10 @@ trait NodeFactory[A <: Node] { eqElements(n.child, children) def makeNode(pre: String, name: String, attrSeq: MetaData, scope: NamespaceBinding, children: Seq[Node]): A = { - val hash = Utility.hashCode(pre, name, attrSeq.##, scope.##, children) - def cons(old: List[A]) = construct(hash, old, pre, name, attrSeq, scope, children) + val hash: Int = Utility.hashCode(pre, name, attrSeq.##, scope.##, children) + def cons(old: List[A]): A = construct(hash, old, pre, name, attrSeq, scope, children) - (cache get hash) match { + cache.get(hash) match { case Some(list) => // find structurally equal list.find(nodeEquals(_, pre, name, attrSeq, scope, children)) match { case Some(x) => x @@ -52,7 +55,9 @@ trait NodeFactory[A <: Node] { } } - def makeText(s: String) = Text(s) + def makeText(s: String): Text = Text(s) + def makePCData(s: String): PCData = + PCData(s) def makeComment(s: String): Seq[Comment] = if (ignoreComments) Nil else List(Comment(s)) def makeProcInstr(t: String, s: String): Seq[ProcInstr] = diff --git a/shared/src/main/scala/scala/xml/factory/XMLLoader.scala b/shared/src/main/scala/scala/xml/factory/XMLLoader.scala new file mode 100644 index 000000000..068b5cd37 --- /dev/null +++ b/shared/src/main/scala/scala/xml/factory/XMLLoader.scala @@ -0,0 +1,101 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml +package factory + +import org.xml.sax.XMLReader +import scala.xml.Source +import javax.xml.parsers.SAXParserFactory +import java.io.{File, FileDescriptor, InputStream, Reader} +import java.net.URL + +/** + * Presents collection of XML loading methods which use the parser + * created by "def parser" or the reader created by "def reader". + */ +trait XMLLoader[T <: Node] { + private def setSafeDefaults(parserFactory: SAXParserFactory): Unit = { + parserFactory.setFeature("http://javax.xml.XMLConstants/feature/secure-processing", true) + parserFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false) + parserFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true) + parserFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false) + parserFactory.setFeature("http://xml.org/sax/features/external-general-entities", false) + parserFactory.setFeature("http://xml.org/sax/features/resolve-dtd-uris", false) + parserFactory.setXIncludeAware(false) + parserFactory.setNamespaceAware(false) + } + + private lazy val parserInstance: ThreadLocal[SAXParser] = new ThreadLocal[SAXParser] { + override def initialValue: SAXParser = { + val parserFactory: SAXParserFactory = SAXParserFactory.newInstance + setSafeDefaults(parserFactory) + parserFactory.newSAXParser + } + } + + /* Override this to use a different SAXParser. */ + def parser: SAXParser = { + val p = parserInstance.get + try { p.reset() } catch { case _: UnsupportedOperationException => } + p + } + + /* Override this to use a different XMLReader. */ + def reader: XMLReader = parser.getXMLReader + + /** + * Loads XML from the given InputSource, using the supplied parser or reader. + * The methods available in scala.xml.XML use the XML parser in the JDK + * (unless another parser is present on the classpath). + */ + + // TODO remove + def loadXML(inputSource: InputSource, parser: SAXParser): T = getDocElem(adapter.loadDocument(inputSource, parser.getXMLReader)) + def loadXMLNodes(inputSource: InputSource, parser: SAXParser): Seq[Node] = adapter.loadDocument(inputSource, parser.getXMLReader).children.toSeq + def adapter: parsing.FactoryAdapter = new parsing.NoBindingFactoryAdapter() + + /** Loads XML Document. */ + def loadDocument(inputSource: InputSource): Document = adapter.loadDocument(inputSource, reader) + def loadFileDocument(fileName: String): Document = loadDocument(Source.fromFile(fileName)) + def loadFileDocument(file: File): Document = loadDocument(Source.fromFile(file)) + def loadDocument(url: URL): Document = loadDocument(Source.fromUrl(url)) + def loadDocument(sysId: String): Document = loadDocument(Source.fromSysId(sysId)) + def loadFileDocument(fileDescriptor: FileDescriptor): Document = loadDocument(Source.fromFile(fileDescriptor)) + def loadDocument(inputStream: InputStream): Document = loadDocument(Source.fromInputStream(inputStream)) + def loadDocument(reader: Reader): Document = loadDocument(Source.fromReader(reader)) + def loadStringDocument(string: String): Document = loadDocument(Source.fromString(string)) + + /** Loads XML element. */ + private def getDocElem(document: Document): T = document.docElem.asInstanceOf[T] + def load(inputSource: InputSource): T = getDocElem(loadDocument(inputSource)) + def loadFile(fileName: String): T = getDocElem(loadFileDocument(fileName)) + def loadFile(file: File): T = getDocElem(loadFileDocument(file)) + def load(url: URL): T = getDocElem(loadDocument(url)) + def load(sysId: String): T = getDocElem(loadDocument(sysId)) + def loadFile(fileDescriptor: FileDescriptor): T = getDocElem(loadFileDocument(fileDescriptor)) + def load(inputStream: InputStream): T = getDocElem(loadDocument(inputStream)) + def load(reader: Reader): T = getDocElem(loadDocument(reader)) + def loadString(string: String): T = getDocElem(loadStringDocument(string)) + + /** Load XML nodes, including comments and processing instructions that precede and follow the root element. */ + def loadNodes(inputSource: InputSource): Seq[Node] = loadDocument(inputSource).children.toSeq + def loadFileNodes(fileName: String): Seq[Node] = loadFileDocument(fileName).children.toSeq + def loadFileNodes(file: File): Seq[Node] = loadFileDocument(file).children.toSeq + def loadNodes(url: URL): Seq[Node] = loadDocument(url).children.toSeq + def loadNodes(sysId: String): Seq[Node] = loadDocument(sysId).children.toSeq + def loadFileNodes(fileDescriptor: FileDescriptor): Seq[Node] = loadFileDocument(fileDescriptor).children.toSeq + def loadNodes(inputStream: InputStream): Seq[Node] = loadDocument(inputStream).children.toSeq + def loadNodes(reader: Reader): Seq[Node] = loadDocument(reader).children.toSeq + def loadStringNodes(string: String): Seq[Node] = loadStringDocument(string).children.toSeq +} diff --git a/shared/src/main/scala/scala/xml/include/CircularIncludeException.scala b/shared/src/main/scala/scala/xml/include/CircularIncludeException.scala new file mode 100644 index 000000000..ecff559df --- /dev/null +++ b/shared/src/main/scala/scala/xml/include/CircularIncludeException.scala @@ -0,0 +1,28 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml +package include + +/** + * A `CircularIncludeException` is thrown when an included document attempts + * to include itself or one of its ancestor documents. + */ +class CircularIncludeException(message: String) extends XIncludeException { + + /** + * Constructs a `CircularIncludeException` with `'''null'''`. + * as its error detail message. + */ + def this() = this(null) +} diff --git a/shared/src/main/scala/scala/xml/include/UnavailableResourceException.scala b/shared/src/main/scala/scala/xml/include/UnavailableResourceException.scala new file mode 100644 index 000000000..7499a3f14 --- /dev/null +++ b/shared/src/main/scala/scala/xml/include/UnavailableResourceException.scala @@ -0,0 +1,24 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml +package include + +/** + * An `UnavailableResourceException` is thrown when an included document + * cannot be found or loaded. + */ +class UnavailableResourceException(message: String) + extends XIncludeException(message) { + def this() = this(null) +} diff --git a/src/main/scala/scala/xml/include/XIncludeException.scala b/shared/src/main/scala/scala/xml/include/XIncludeException.scala similarity index 70% rename from src/main/scala/scala/xml/include/XIncludeException.scala rename to shared/src/main/scala/scala/xml/include/XIncludeException.scala index 64672b070..4d2a809cf 100644 --- a/src/main/scala/scala/xml/include/XIncludeException.scala +++ b/shared/src/main/scala/scala/xml/include/XIncludeException.scala @@ -1,10 +1,14 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ package scala package xml @@ -28,7 +32,7 @@ class XIncludeException(message: String) extends Exception(message) { */ def this() = this(null) - private var rootCause: Throwable = null + private var rootCause: Throwable = _ /** * When an `IOException`, `MalformedURLException` or other generic @@ -39,7 +43,7 @@ class XIncludeException(message: String) extends Exception(message) { * @param nestedException the underlying exception which * caused the XIncludeException to be thrown */ - def setRootCause(nestedException: Throwable) { + def setRootCause(nestedException: Throwable): Unit = { this.rootCause = nestedException } @@ -53,6 +57,5 @@ class XIncludeException(message: String) extends Exception(message) { * @return Throwable the underlying exception which caused the * `XIncludeException` to be thrown */ - def getRootCause(): Throwable = this.rootCause - + def getRootCause: Throwable = this.rootCause } diff --git a/src/main/scala/scala/xml/include/sax/EncodingHeuristics.scala b/shared/src/main/scala/scala/xml/include/sax/EncodingHeuristics.scala similarity index 66% rename from src/main/scala/scala/xml/include/sax/EncodingHeuristics.scala rename to shared/src/main/scala/scala/xml/include/sax/EncodingHeuristics.scala index b12b9ac82..fe8b0e323 100644 --- a/src/main/scala/scala/xml/include/sax/EncodingHeuristics.scala +++ b/shared/src/main/scala/scala/xml/include/sax/EncodingHeuristics.scala @@ -1,10 +1,14 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ package scala package xml @@ -26,13 +30,13 @@ import scala.util.matching.Regex object EncodingHeuristics { object EncodingNames { // UCS-4 isn't yet implemented in java releases anyway... - val bigUCS4 = "UCS-4" - val littleUCS4 = "UCS-4" - val unusualUCS4 = "UCS-4" - val bigUTF16 = "UTF-16BE" - val littleUTF16 = "UTF-16LE" - val utf8 = "UTF-8" - val default = utf8 + val bigUCS4: String = "UCS-4" + val littleUCS4: String = "UCS-4" + val unusualUCS4: String = "UCS-4" + val bigUTF16: String = "UTF-16BE" + val littleUTF16: String = "UTF-16LE" + val utf8: String = "UTF-8" + val default: String = utf8 } import EncodingNames._ @@ -42,18 +46,18 @@ object EncodingHeuristics { * [[http://www.w3.org/TR/xml/#sec-guessing w3]]. * * @param in `InputStream` to read from. - * @throws IOException if the stream cannot be reset + * @throws java.io.IOException if the stream cannot be reset * @return the name of the encoding. */ def readEncodingFromStream(in: InputStream): String = { var ret: String = null - val bytesToRead = 1024 // enough to read most XML encoding declarations - def resetAndRet = { in.reset; ret } + val bytesToRead: Int = 1024 // enough to read most XML encoding declarations + def resetAndRet: String = { in.reset(); ret } // This may fail if there are a lot of space characters before the end // of the encoding declaration in mark bytesToRead - val bytes = (in.read, in.read, in.read, in.read) + val bytes: (Int, Int, Int, Int) = (in.read, in.read, in.read, in.read) // first look for byte order mark ret = bytes match { @@ -70,13 +74,13 @@ object EncodingHeuristics { return resetAndRet def readASCIIEncoding: String = { - val data = new Array[Byte](bytesToRead - 4) - val length = in.read(data, 0, bytesToRead - 4) + val data: Array[Byte] = new Array[Byte](bytesToRead - 4) + val length: Int = in.read(data, 0, bytesToRead - 4) // Use Latin-1 (ISO-8859-1) because all byte sequences are legal. - val declaration = new String(data, 0, length, "ISO-8859-1") - val regexp = """(?m).*?encoding\s*=\s*["'](.+?)['"]""".r - (regexp findFirstMatchIn declaration) match { + val declaration: String = new String(data, 0, length, "ISO-8859-1") + val regexp: Regex = """(?m).*?encoding\s*=\s*["'](.+?)['"]""".r + regexp.findFirstMatchIn(declaration) match { case None => default case Some(md) => md.subgroups(0) } diff --git a/src/main/scala/scala/xml/include/sax/XIncludeFilter.scala b/shared/src/main/scala/scala/xml/include/sax/XIncludeFilter.scala similarity index 62% rename from src/main/scala/scala/xml/include/sax/XIncludeFilter.scala rename to shared/src/main/scala/scala/xml/include/sax/XIncludeFilter.scala index dc18dd412..b82414d61 100644 --- a/src/main/scala/scala/xml/include/sax/XIncludeFilter.scala +++ b/shared/src/main/scala/scala/xml/include/sax/XIncludeFilter.scala @@ -1,23 +1,25 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ package scala package xml package include.sax import scala.xml.include._ +import org.xml.sax.{Attributes, Locator, XMLReader} +import org.xml.sax.helpers.{AttributesImpl, NamespaceSupport, XMLFilterImpl, XMLReaderFactory} -import org.xml.sax.{ Attributes, XMLReader, Locator } -import org.xml.sax.helpers.{ XMLReaderFactory, XMLFilterImpl, NamespaceSupport, AttributesImpl } - -import java.io.{ InputStream, BufferedInputStream, InputStreamReader, IOException, UnsupportedEncodingException } -import java.util.Stack -import java.net.{ URL, MalformedURLException } +import java.io.{BufferedInputStream, IOException, InputStreamReader, UnsupportedEncodingException} +import java.net.{MalformedURLException, URL, URLConnection} /** * This is a SAX filter which resolves all XInclude include elements before @@ -68,10 +70,10 @@ import java.net.{ URL, MalformedURLException } */ class XIncludeFilter extends XMLFilterImpl { - final val XINCLUDE_NAMESPACE = "http://www.w3.org/2001/XInclude" + final val XINCLUDE_NAMESPACE: String = "http://www.w3.org/2001/XInclude" - private val bases = new Stack[URL]() - private val locators = new Stack[Locator]() + private val bases: java.util.Stack[URL] = new java.util.Stack[URL]() + private val locators: java.util.Stack[Locator] = new java.util.Stack[Locator]() /* private EntityResolver resolver; @@ -86,20 +88,20 @@ class XIncludeFilter extends XMLFilterImpl { // what if this isn't called???? // do I need to check this in startDocument() and push something // there???? - override def setDocumentLocator(locator: Locator) { + override def setDocumentLocator(locator: Locator): Unit = { locators push locator - val base = locator.getSystemId() + val base: String = locator.getSystemId try { bases.push(new URL(base)) } catch { - case e: MalformedURLException => - throw new UnsupportedOperationException("Unrecognized SYSTEM ID: " + base) + case _: MalformedURLException => + throw new UnsupportedOperationException(s"Unrecognized SYSTEM ID: $base") } super.setDocumentLocator(locator) } // necessary to throw away contents of non-empty XInclude elements - private var level = 0 + private var level: Int = 0 /** * This utility method returns true if and only if this reader is @@ -111,56 +113,52 @@ class XIncludeFilter extends XMLFilterImpl { * * @return boolean */ - def insideIncludeElement(): Boolean = level != 0 + def insideIncludeElement: Boolean = level != 0 - override def startElement(uri: String, localName: String, qName: String, atts1: Attributes) { - var atts = atts1 + override def startElement(uri: String, localName: String, qName: String, atts1: Attributes): Unit = { + var atts: Attributes = atts1 if (level == 0) { // We're not inside an xi:include element // Adjust bases stack by pushing either the new // value of xml:base or the base of the parent - val base = atts.getValue(NamespaceSupport.XMLNS, "base") - val parentBase = bases.peek().asInstanceOf[URL] - var currentBase = parentBase + val base: String = atts.getValue(NamespaceSupport.XMLNS, "base") + val parentBase: URL = bases.peek() + var currentBase: URL = parentBase if (base != null) { try { currentBase = new URL(parentBase, base) } catch { case e: MalformedURLException => - throw new SAXException("Malformed base URL: " - + currentBase, e) + throw new SAXException(s"Malformed base URL: $currentBase", e) } } bases push currentBase - if (uri.equals(XINCLUDE_NAMESPACE) && localName.equals("include")) { + if (uri == XINCLUDE_NAMESPACE && localName == "include") { // include external document - val href = atts.getValue("href") + val href: String = atts.getValue("href") // Verify that there is an href attribute if (href == null) { throw new SAXException("Missing href attribute") } - var parse = atts getValue "parse" + var parse: String = atts.getValue("parse") if (parse == null) parse = "xml" - if (parse equals "text") { - val encoding = atts getValue "encoding" - includeTextDocument(href, encoding) - } else if (parse equals "xml") { + if (parse == "text") + includeTextDocument(href, atts.getValue("encoding")) + else if (parse == "xml") includeXMLDocument(href) - } // Need to check this also in DOM and JDOM???? - else { - throw new SAXException( - "Illegal value for parse attribute: " + parse) - } + // Need to check this also in DOM and JDOM???? + else + throw new SAXException(s"Illegal value for parse attribute: $parse") level += 1 } else { if (atRoot) { // add xml:base attribute if necessary - val attsImpl = new AttributesImpl(atts) + val attsImpl: AttributesImpl = new AttributesImpl(atts) attsImpl.addAttribute(NamespaceSupport.XMLNS, "base", - "xml:base", "CDATA", currentBase.toExternalForm()) + "xml:base", "CDATA", currentBase.toExternalForm) atts = attsImpl atRoot = false } @@ -169,9 +167,8 @@ class XIncludeFilter extends XMLFilterImpl { } } - override def endElement(uri: String, localName: String, qName: String) { - if (uri.equals(XINCLUDE_NAMESPACE) - && localName.equals("include")) { + override def endElement(uri: String, localName: String, qName: String): Unit = { + if (uri == XINCLUDE_NAMESPACE && localName == "include") { level -= 1 } else if (level == 0) { bases.pop() @@ -179,15 +176,15 @@ class XIncludeFilter extends XMLFilterImpl { } } - private var depth = 0 + private var depth: Int = 0 - override def startDocument() { + override def startDocument(): Unit = { level = 0 if (depth == 0) super.startDocument() depth += 1 } - override def endDocument() { + override def endDocument(): Unit = { locators.pop() bases.pop() // pop the URL for the document itself depth -= 1 @@ -195,47 +192,45 @@ class XIncludeFilter extends XMLFilterImpl { } // how do prefix mappings move across documents???? - override def startPrefixMapping(prefix: String, uri: String) { + override def startPrefixMapping(prefix: String, uri: String): Unit = { if (level == 0) super.startPrefixMapping(prefix, uri) } - override def endPrefixMapping(prefix: String) { + override def endPrefixMapping(prefix: String): Unit = { if (level == 0) super.endPrefixMapping(prefix) } - override def characters(ch: Array[Char], start: Int, length: Int) { + override def characters(ch: Array[Char], start: Int, length: Int): Unit = { if (level == 0) super.characters(ch, start, length) } - override def ignorableWhitespace(ch: Array[Char], start: Int, length: Int) { + override def ignorableWhitespace(ch: Array[Char], start: Int, length: Int): Unit = { if (level == 0) super.ignorableWhitespace(ch, start, length) } - override def processingInstruction(target: String, data: String) { + override def processingInstruction(target: String, data: String): Unit = { if (level == 0) super.processingInstruction(target, data) } - override def skippedEntity(name: String) { + override def skippedEntity(name: String): Unit = { if (level == 0) super.skippedEntity(name) } // convenience method for error messages - private def getLocation(): String = { - var locationString = "" - val locator = locators.peek().asInstanceOf[Locator] - var publicID = "" - var systemID = "" - var column = -1 - var line = -1 + private def getLocation: String = { + var locationString: String = "" + val locator: Locator = locators.peek + var publicID: String = "" + var systemID: String = "" + var column: Int = -1 + var line: Int = -1 if (locator != null) { - publicID = locator.getPublicId() - systemID = locator.getSystemId() - line = locator.getLineNumber() - column = locator.getColumnNumber() + publicID = locator.getPublicId + systemID = locator.getSystemId + line = locator.getLineNumber + column = locator.getColumnNumber } - locationString = (" in document included from " + publicID - + " at " + systemID - + " at line " + line + ", column " + column) + locationString = s" in document included from $publicID at $systemID at line $line, column $column" locationString } @@ -252,26 +247,25 @@ class XIncludeFilter extends XMLFilterImpl { * be downloaded from the specified URL * or if the encoding is not recognized */ - private def includeTextDocument(url: String, encoding1: String) { - var encoding = encoding1 - if (encoding == null || encoding.trim().equals("")) encoding = "UTF-8" + private def includeTextDocument(url: String, encoding1: String): Unit = { + var encoding: String = encoding1 + if (encoding == null || encoding.trim.isEmpty) encoding = "UTF-8" var source: URL = null try { - val base = bases.peek().asInstanceOf[URL] + val base: URL = bases.peek source = new URL(base, url) } catch { case e: MalformedURLException => - val ex = new UnavailableResourceException("Unresolvable URL " + url - + getLocation()) + val ex: UnavailableResourceException = new UnavailableResourceException(s"Unresolvable URL $url$getLocation") ex.setRootCause(e) - throw new SAXException("Unresolvable URL " + url + getLocation(), ex) + throw new SAXException(s"Unresolvable URL $url$getLocation", ex) } try { - val uc = source.openConnection() - val in = new BufferedInputStream(uc.getInputStream()) - val encodingFromHeader = uc.getContentEncoding() - var contentType = uc.getContentType() + val uc: URLConnection = source.openConnection + val in: BufferedInputStream = new BufferedInputStream(uc.getInputStream) + val encodingFromHeader: String = uc.getContentEncoding + var contentType: String = uc.getContentType if (encodingFromHeader != null) encoding = encodingFromHeader else { @@ -279,34 +273,31 @@ class XIncludeFilter extends XMLFilterImpl { // MIME types are case-insensitive // Java may be picking this up from file URL if (contentType != null) { - contentType = contentType.toLowerCase() - if (contentType.equals("text/xml") - || contentType.equals("application/xml") + contentType = contentType.toLowerCase + if (contentType == "text/xml" + || contentType == "application/xml" || (contentType.startsWith("text/") && contentType.endsWith("+xml")) || (contentType.startsWith("application/") && contentType.endsWith("+xml"))) { encoding = EncodingHeuristics.readEncodingFromStream(in) } } } - val reader = new InputStreamReader(in, encoding) - val c = new Array[Char](1024) + val reader: InputStreamReader = new InputStreamReader(in, encoding) + val c: Array[Char] = new Array[Char](1024) var charsRead: Int = 0 // bogus init value - do { + while ({ { charsRead = reader.read(c, 0, 1024) if (charsRead > 0) this.characters(c, 0, charsRead) - } while (charsRead != -1) + } ; charsRead != -1}) () } catch { case e: UnsupportedEncodingException => - throw new SAXException("Unsupported encoding: " - + encoding + getLocation(), e) + throw new SAXException(s"Unsupported encoding: $encoding$getLocation", e) case e: IOException => - throw new SAXException("Document not found: " - + source.toExternalForm() + getLocation(), e) + throw new SAXException(s"Document not found: ${source.toExternalForm}$getLocation", e) } - } - private var atRoot = false + private var atRoot: Boolean = false /** * This utility method reads a document at a specified URL @@ -318,49 +309,49 @@ class XIncludeFilter extends XMLFilterImpl { * @throws SAXException if the requested document cannot * be downloaded from the specified URL. */ - private def includeXMLDocument(url: String) { - val source = - try new URL(bases.peek(), url) + private def includeXMLDocument(url: String): Unit = { + val source: URL = + try new URL(bases.peek, url) catch { case e: MalformedURLException => - val ex = new UnavailableResourceException("Unresolvable URL " + url + getLocation()) + val ex: UnavailableResourceException = new UnavailableResourceException(s"Unresolvable URL $url$getLocation") ex setRootCause e - throw new SAXException("Unresolvable URL " + url + getLocation(), ex) + throw new SAXException(s"Unresolvable URL $url$getLocation", ex) } try { val parser: XMLReader = try XMLReaderFactory.createXMLReader() catch { - case e: SAXException => + case _: SAXException => try XMLReaderFactory.createXMLReader(XercesClassName) catch { case _: SAXException => return System.err.println("Could not find an XML parser") } } parser setContentHandler this - val resolver = this.getEntityResolver() + val resolver: EntityResolver = this.getEntityResolver if (resolver != null) parser setEntityResolver resolver // save old level and base - val previousLevel = level + val previousLevel: Int = level this.level = 0 - if (bases contains source) + if (bases.contains(source)) throw new SAXException( "Circular XInclude Reference", - new CircularIncludeException("Circular XInclude Reference to " + source + getLocation()) + new CircularIncludeException(s"Circular XInclude Reference to $source$getLocation") ) bases push source atRoot = true - parser parse source.toExternalForm() + parser parse source.toExternalForm // restore old level and base this.level = previousLevel bases.pop() } catch { case e: IOException => - throw new SAXException("Document not found: " + source.toExternalForm() + getLocation(), e) + throw new SAXException(s"Document not found: ${source.toExternalForm}$getLocation", e) } } } diff --git a/shared/src/main/scala/scala/xml/include/sax/XIncluder.scala b/shared/src/main/scala/scala/xml/include/sax/XIncluder.scala new file mode 100644 index 000000000..ec94be793 --- /dev/null +++ b/shared/src/main/scala/scala/xml/include/sax/XIncluder.scala @@ -0,0 +1,180 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml +package include.sax + +import org.xml.sax.{ ContentHandler, Locator, Attributes } +import org.xml.sax.ext.LexicalHandler +import java.io.{ OutputStream, OutputStreamWriter, IOException } + +/** + * XIncluder is a SAX `ContentHandler` that writes its XML document onto + * an output stream after resolving all `xinclude:include` elements. + * + * Based on Eliotte Rusty Harold's SAXXIncluder. + */ +class XIncluder(outs: OutputStream, encoding: String) extends ContentHandler with LexicalHandler { + + var out: OutputStreamWriter = new OutputStreamWriter(outs, encoding) + + override def setDocumentLocator(locator: Locator): Unit = () + + override def startDocument(): Unit = { + try { + out.write(s"\r\n") + } catch { + case e: IOException => + throw new SAXException("Write failed", e) + } + } + + override def endDocument(): Unit = { + try { + out.flush() + } catch { + case e: IOException => + throw new SAXException("Flush failed", e) + } + } + + override def startPrefixMapping(prefix: String, uri: String): Unit = () + + override def endPrefixMapping(prefix: String): Unit = () + + override def startElement(namespaceURI: String, localName: String, qualifiedName: String, atts: Attributes): Unit = { + try { + out.write(s"<$qualifiedName") + var i: Int = 0 + while (i < atts.getLength) { + val value: String = atts.getValue(i) + // @todo Need to use character references if the encoding + // can't support the character + val valueStr: String = scala.xml.Utility.escape(value) + out.write(s" ${atts.getQName(i)}='$valueStr'") + i += 1 + } + out.write(">") + } catch { + case e: IOException => + throw new SAXException("Write failed", e) + } + } + + override def endElement(namespaceURI: String, localName: String, qualifiedName: String): Unit = { + try { + out.write(s"") + } catch { + case e: IOException => + throw new SAXException("Write failed", e) + } + } + + // need to escape characters that are not in the given + // encoding using character references???? + override def characters(ch: Array[Char], start: Int, length: Int): Unit = { + try { + var i: Int = 0 + while (i < length) { + val c: Char = ch(start + i) + if (c == '&') out.write("&") + else if (c == '<') out.write("<") + // This next fix is normally not necessary. + // However, it is required if text contains ]]> + // (The end CDATA section delimiter) + else if (c == '>') out.write(">") + else out.write(c.toInt) + i += 1 + } + } catch { + case e: IOException => + throw new SAXException("Write failed", e) + } + } + + override def ignorableWhitespace(ch: Array[Char], start: Int, length: Int): Unit = { + this.characters(ch, start, length) + } + + // do I need to escape text in PI???? + override def processingInstruction(target: String, data: String): Unit = { + try { + out.write(s"") + } catch { + case e: IOException => + throw new SAXException("Write failed", e) + } + } + + override def skippedEntity(name: String): Unit = { + try { + out.write(s"&$name;") + } catch { + case e: IOException => + throw new SAXException("Write failed", e) + } + } + + // LexicalHandler methods + private var inDTD: Boolean = false + private var entities = List.empty[String] + + override def startDTD(name: String, publicID: String, systemID: String): Unit = { + inDTD = true + // if this is the source document, output a DOCTYPE declaration + if (entities.isEmpty) { + var id: String = "" + if (publicID != null) id = s""" PUBLIC "$publicID" "$systemID"""" + else if (systemID != null) id = s""" SYSTEM "$systemID"""" + try { + out.write(s"\r\n") + } catch { + case e: IOException => + throw new SAXException("Error while writing DOCTYPE", e) + } + } + } + override def endDTD(): Unit = () + + override def startEntity(name: String): Unit = { + entities = name :: entities + } + + override def endEntity(name: String): Unit = { + entities = entities.tail + } + + override def startCDATA(): Unit = () + override def endCDATA(): Unit = () + + // Just need this reference so we can ask if a comment is + // inside an include element or not + private var filter: XIncludeFilter = _ + + def setFilter(filter: XIncludeFilter): Unit = { + this.filter = filter + } + + override def comment(ch: Array[Char], start: Int, length: Int): Unit = { + if (!inDTD && !filter.insideIncludeElement) { + try { + out.write("") + } catch { + case e: IOException => + throw new SAXException("Write failed", e) + } + } + } +} diff --git a/shared/src/main/scala/scala/xml/package.scala b/shared/src/main/scala/scala/xml/package.scala new file mode 100644 index 000000000..80fbb5f15 --- /dev/null +++ b/shared/src/main/scala/scala/xml/package.scala @@ -0,0 +1,85 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +/** + * This library provides support for the XML literal syntax in Scala programs. + * {{{ + * val planets: scala.xml.Elem = + * + * Earth + * 5.9742e24 + * 6378.14e3 + * + * + * Mars + * 0.64191e24 + * 3397.0e3 + * + * + * }}} + * + * Additionally, you can mix Scala expressions in your XML elements by + * using the curly brace notation: + * + * {{{ + * val sunMass = 1.99e30 + * val sunRadius = 6.96e8 + * val star = + * Sun + * { sunMass } + * { sunRadius } + * { 4 * Math.PI * Math.pow(sunRadius, 2) } + * { 4/3 * Math.PI * Math.pow(sunRadius, 3) } + * + * }}} + * + * An XML element, for example `` and ``, is + * represented in this library as a case class, [[scala.xml.Elem]]. + * + * The sub-elements of XML values share a common base class, + * [[scala.xml.Node]]. + * + * However, the non-element declarations found in XML files share a + * different common base class, [[scala.xml.dtd.Decl]]. Additionally, + * document type declarations are represented by a different trait, + * [[scala.xml.dtd.DTD]]. + * + * For reading and writing XML data to and from files, see + * [[scala.xml.XML]]. The default parser of XML data is the + * [[http://xerces.apache.org/ Xerces]] parser and is provided in Java + * by [[javax.xml.parsers.SAXParser]]. + * + * For more control of the input, use the parser written in Scala that + * is provided, [[scala.xml.parsing.ConstructingParser]]. + * + * For working with XHTML input, use [[scala.xml.parsing.XhtmlParser]]. + * + * For more control of the output, use the [[scala.xml.PrettyPrinter]]. + * + * Utility methods for working with XML data are provided in + * [[scala.xml.Utility]]. + * + * XML values in Scala are immutable, but you can traverse and + * transform XML data with a [[scala.xml.transform.RuleTransformer]]. + */ +package object xml { + val XercesClassName: String = "org.apache.xerces.parsers.SAXParser" + + type SAXException = org.xml.sax.SAXException + type SAXParseException = org.xml.sax.SAXParseException + type EntityResolver = org.xml.sax.EntityResolver + type InputSource = org.xml.sax.InputSource + type XMLReader = org.xml.sax.XMLReader + type SAXParser = javax.xml.parsers.SAXParser +} diff --git a/shared/src/main/scala/scala/xml/parsing/ConstructingHandler.scala b/shared/src/main/scala/scala/xml/parsing/ConstructingHandler.scala new file mode 100755 index 000000000..226668219 --- /dev/null +++ b/shared/src/main/scala/scala/xml/parsing/ConstructingHandler.scala @@ -0,0 +1,33 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml +package parsing + +/** + * Implementation of MarkupHandler that constructs nodes. + * + * @author Burak Emir + */ +abstract class ConstructingHandler extends MarkupHandler { + val preserveWS: Boolean + + override def elem(pos: Int, pre: String, label: String, attrs: MetaData, + pscope: NamespaceBinding, empty: Boolean, nodes: NodeSeq): NodeSeq = + Elem(pre, label, attrs, pscope, empty, nodes: _*) + + override def procInstr(pos: Int, target: String, txt: String): ProcInstr = ProcInstr(target, txt) + override def comment(pos: Int, txt: String): Comment = Comment(txt) + override def entityRef(pos: Int, n: String): EntityRef = EntityRef(n) + override def text(pos: Int, txt: String): Text = Text(txt) +} diff --git a/src/main/scala/scala/xml/parsing/ConstructingParser.scala b/shared/src/main/scala/scala/xml/parsing/ConstructingParser.scala similarity index 61% rename from src/main/scala/scala/xml/parsing/ConstructingParser.scala rename to shared/src/main/scala/scala/xml/parsing/ConstructingParser.scala index b5162c82a..994ec87b5 100644 --- a/src/main/scala/scala/xml/parsing/ConstructingParser.scala +++ b/shared/src/main/scala/scala/xml/parsing/ConstructingParser.scala @@ -1,10 +1,14 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ package scala package xml @@ -14,10 +18,10 @@ import java.io.File import scala.io.Source object ConstructingParser { - def fromFile(inp: File, preserveWS: Boolean) = + def fromFile(inp: File, preserveWS: Boolean): ConstructingParser = new ConstructingParser(Source.fromFile(inp), preserveWS).initialize - def fromSource(inp: Source, preserveWS: Boolean) = + def fromSource(inp: Source, preserveWS: Boolean): ConstructingParser = new ConstructingParser(inp, preserveWS).initialize } @@ -45,7 +49,7 @@ object ConstructingParser { * } * }}} */ -class ConstructingParser(val input: Source, val preserveWS: Boolean) +class ConstructingParser(override val input: Source, override val preserveWS: Boolean) extends ConstructingHandler with ExternalSources with MarkupParser diff --git a/shared/src/main/scala/scala/xml/parsing/DefaultMarkupHandler.scala b/shared/src/main/scala/scala/xml/parsing/DefaultMarkupHandler.scala new file mode 100755 index 000000000..03ee009d0 --- /dev/null +++ b/shared/src/main/scala/scala/xml/parsing/DefaultMarkupHandler.scala @@ -0,0 +1,30 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml +package parsing + +/** Default implementation of markup handler always returns `NodeSeq.Empty` */ +abstract class DefaultMarkupHandler extends MarkupHandler { + + override def elem(pos: Int, pre: String, label: String, attrs: MetaData, + scope: NamespaceBinding, empty: Boolean, args: NodeSeq): NodeSeq = NodeSeq.Empty + + override def procInstr(pos: Int, target: String, txt: String): NodeSeq = NodeSeq.Empty + + override def comment(pos: Int, comment: String): NodeSeq = NodeSeq.Empty + + override def entityRef(pos: Int, n: String): NodeSeq = NodeSeq.Empty + + override def text(pos: Int, txt: String): NodeSeq = NodeSeq.Empty +} diff --git a/shared/src/main/scala/scala/xml/parsing/DtdBuilder.scala b/shared/src/main/scala/scala/xml/parsing/DtdBuilder.scala new file mode 100644 index 000000000..df8e3a2f7 --- /dev/null +++ b/shared/src/main/scala/scala/xml/parsing/DtdBuilder.scala @@ -0,0 +1,190 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml +package parsing + +import scala.xml.dtd._ + +// Note: this is private to avoid it becoming a part of binary compatibility checks +final private[parsing] class DtdBuilder( + name: String, + externalID: ExternalID +) { + private var elements: List[ElemDecl] = List.empty + private var attributeLists: List[AttListDecl] = List.empty + private var entities: List[EntityDecl] = List.empty + private var notations: List[NotationDecl] = List.empty + private var unparsedEntities: List[UnparsedEntityDecl] = List.empty + private var parameterReferences: List[PEReference] = List.empty + + // AttListDecl under construction + private var elementName: Option[String] = None + private var attributes: List[AttrDecl] = List.empty + + private def flushAttributes(): Unit = if (elementName.isDefined) { + attributeLists ::= AttListDecl(elementName.get, attributes.reverse) + attributes = List.empty + elementName = None + } + + private var done: Boolean = false + def isDone: Boolean = done + + def endDTD(): Unit = { + flushAttributes() + done = true + } + + def dtd: DTD = new DTD { + // Note: weirdly, unlike DocType, DTD does not have a 'name'... + this.externalID = DtdBuilder.this.externalID + this.elem ++= elements.map(d => d.name -> d).toMap + this.attr ++= attributeLists.map(d => d.name -> d).toMap + this.ent ++= entities.map { d => + val name: String = d match { + case ParsedEntityDecl(name, _) => name + case ParameterEntityDecl(name, _) => name + case UnparsedEntityDecl(name, _, _) => name + } + name -> d + }.toMap + this.decls = + elements.reverse ++ + attributeLists.reverse ++ + entities.reverse ++ + DtdBuilder.this.notations.reverse ++ + parameterReferences.reverse + + override val notations: Seq[NotationDecl] = DtdBuilder.this.notations.reverse + override val unparsedEntities: Seq[EntityDecl] = DtdBuilder.this.unparsedEntities.reverse + } + + + def elementDecl(name: String, model: String): Unit = { + flushAttributes() + elements ::= ElemDecl(name, ElementContentModel.parseContentModel(model)) + } + + // The type will be one of the strings "CDATA", "ID", "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY", "ENTITIES", + // a parenthesized token group with the separator "|" and all whitespace removed, + // or the word "NOTATION" followed by a space followed by a parenthesized token group with all whitespace removed. + def attributeDecl( + eName: String, + aName: String, + `type`: String, + mode: String, + value: String + ): Unit = { + if (!elementName.contains(eName)) { + flushAttributes() + elementName = Some(eName) + } + + val attribute: AttrDecl = AttrDecl( + aName, + `type`, + mode match { + case "#REQUIRED" => REQUIRED + case "#IMPLIED" => IMPLIED + case "#FIXED" => DEFAULT(fixed = true, value) + case _ => DEFAULT(fixed = false, value) + } + ) + + attributes ::= attribute + } + + // General entities are reported with their regular names, + // parameter entities have '%' prepended to their names, + // and the external DTD subset has the pseudo-entity name "[dtd]". + def startEntity(name: String): Unit = { + flushAttributes() + if (name.startsWith("%")) parameterReferences ::= PEReference(name.tail.trim) + } + + def endEntity(name: String): Unit = { + } + + def notationDecl( + name: String, + publicId: String, + systemId: String + ): Unit = { + flushAttributes() + notations ::= NotationDecl(name, DtdBuilder.mkExternalID(publicId, systemId)) + } + + def unparsedEntityDecl( + name: String, + publicId: String, + systemId: String, + notationName: String + ): Unit = { + flushAttributes() + val unparsedEntity: UnparsedEntityDecl = + UnparsedEntityDecl(name, DtdBuilder.mkExternalID(publicId, systemId), notationName) + entities ::= unparsedEntity + unparsedEntities ::= unparsedEntity + } + + def internalEntityDecl( + name: String, + value: String + ): Unit = { + flushAttributes() + entityDecl(name, IntDef(value)) + } + + def externalEntityDecl( + name: String, + publicId: String, + systemId: String + ): Unit = { + flushAttributes() + entityDecl(name, ExtDef(DtdBuilder.mkExternalID(publicId, systemId))) + } + + private def entityDecl( + name: String, + entityDef: EntityDef + ): Unit = { + val entity: EntityDecl = + if (name.startsWith("%")) ParameterEntityDecl(name.tail.trim, entityDef) + else ParsedEntityDecl(name, entityDef) + entities ::= entity + } + + // DTD class currently does not provide for capturing processing instructions + def processingInstruction(target: String, data: String): Unit = () + + // DTD class currently does not provide for capturing comments + def comment(commentText: String): Unit = () +} + +// Note: this is private to avoid it becoming a part of binary compatibility checks +private[parsing] object DtdBuilder { + def apply( + name: String, + publicId: String, + systemId: String + ): DtdBuilder = new DtdBuilder( + name, + mkExternalID(publicId, systemId) + ) + + private def mkExternalID(publicId: String, systemId: String): ExternalID = + if (publicId != null) PublicID(publicId, systemId) + else if (systemId != null) SystemID(systemId) + else NoExternalID +} diff --git a/shared/src/main/scala/scala/xml/parsing/ElementContentModel.scala b/shared/src/main/scala/scala/xml/parsing/ElementContentModel.scala new file mode 100644 index 000000000..cb7316d6e --- /dev/null +++ b/shared/src/main/scala/scala/xml/parsing/ElementContentModel.scala @@ -0,0 +1,229 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.xml.parsing + +import scala.annotation.tailrec +import scala.xml.dtd + +// Note: this is private to avoid it becoming a part of binary compatibility checks. + +// The content model will consist of the string "EMPTY", the string "ANY", or a parenthesised group, +// optionally followed by an occurrence indicator. +// The model will be normalized so that all parameter entities are fully resolved and all whitespace is removed, +// and will include the enclosing parentheses. +// Other normalization (such as removing redundant parentheses or simplifying occurrence indicators) +// is at the discretion of the parser. + +// elementdecl ::= '' +// contentspec ::= 'EMPTY' | 'ANY' | Mixed | children +// children ::= (choice | seq) ('?' | '*' | '+')? +// cp ::= (Name | choice | seq) ('?' | '*' | '+')? +// choice ::= '(' S? cp ( S? '|' S? cp )+ S? ')' +// seq ::= '(' S? cp ( S? ',' S? cp )* S? ')' +// Mixed ::= '(' S? '#PCDATA' (S? '|' S? Name)* S? ')*' +// | '(' S? '#PCDATA' S? ')' +private[parsing] object ElementContentModel { + def parseContentModel(model: String): dtd.ContentModel = ContentSpec.parse(model) match { + case ContentSpec.Empty => dtd.EMPTY + case ContentSpec.Any => dtd.ANY + case ContentSpec.PCData => dtd.PCDATA + case ContentSpec.Children(elements, occurrence) => dtd.ELEMENTS(convertOccurrence(elements, occurrence)) + case ContentSpec.Mixed(elements) => + val result: List[dtd.ContentModel.RegExp] = + dtd.ContentModel.Letter(dtd.ContentModel.ElemName(ContentSpec.PCData.value)) +: + elements.map(convertElements) + // TODO scala.xml.dtd.impl.Alt.apply() insists on there being al least two alternatives, + // which causes an exception in MIXED.toString() when there is only one alternative besides #PCDATA. + // I think this is a bug. + dtd.MIXED(dtd.ContentModel.Alt(result: _*)) + } + + private def convertElements(elements: Elements): dtd.ContentModel.RegExp = { + def convertCp(cp: Cp): dtd.ContentModel.RegExp = convertOccurrence(cp.elements, cp.occurrence) + elements match { + case Elements.Element(name) => dtd.ContentModel.Letter(dtd.ContentModel.ElemName(name)) + case Elements.Choice(children) => dtd.ContentModel.Alt(children.map(convertCp): _*) + case Elements.Sequence(children) => dtd.ContentModel.Sequ(children.map(convertCp): _*) + } + } + + private def convertOccurrence(elements: Elements, occurrence: Occurrence): dtd.ContentModel.RegExp = { + val result: dtd.ContentModel.RegExp = convertElements(elements) + occurrence match { + case Occurrence.Once => result + case Occurrence.RepeatOptional => dtd.ContentModel.Star(result) + case Occurrence.OnceOptional => dtd.ContentModel.Star(result) // TODO fidelity lost! + case Occurrence.Repeat => dtd.ContentModel.Star(result) // TODO fidelity lost! + } + } + + sealed trait ContentSpec + object ContentSpec { + sealed trait Simple extends ContentSpec { + final override def toString: String = value + val value: String + } + case object Empty extends Simple { + override val value: String = "EMPTY" + } + case object Any extends Simple { + override val value: String = "ANY" + } + case object PCData extends ContentSpec { + override def toString: String = s"($value)" + val value: String = "#PCDATA" + } + final case class Mixed(elements: List[Elements.Element]) extends ContentSpec { + override def toString: String = { + val names: String = elements.mkString("|") + s"(${PCData.value}|$names)*" + } + } + final case class Children(elements: Elements.Many, occurrence: Occurrence) extends ContentSpec { + override def toString: String = s"$elements$occurrence" + } + object Children { + def parse(string: String, occurrence: Occurrence): Children = + Children(Elements.Many.parse(string), occurrence) + } + def parse(model: String): ContentSpec = model match { + case Empty.value => Empty + case Any.value => Any + case model => + val (parenthesized: String, occurrence: Occurrence) = Occurrence.parse(model) + require(isParenthesized(parenthesized)) + val string: String = removeParentheses(parenthesized) + if (occurrence == Occurrence.Once && string == PCData.value) PCData else if (occurrence == Occurrence.RepeatOptional) { + val choice: List[String] = Elements.Choice.split(string) + if (choice.length > 1 && choice.head == PCData.value) Mixed(choice.tail.map(Elements.Element.apply)) + else Children.parse(string, occurrence) + } else Children.parse(string, occurrence) + } + } + + sealed trait Elements + object Elements { + final case class Element(name: String) extends Elements { + override def toString: String = name + } + sealed abstract class ManyCompanion(val separator: Char) { + final def split(string: String): List[String] = ElementContentModel.split(string, separator) + } + sealed abstract class Many(children: List[Cp]) extends Elements { + final override def toString: String = children.map(_.toString).mkString("(", companion.separator.toString, ")") + def companion: ManyCompanion + } + object Choice extends ManyCompanion(separator = '|') + final case class Choice(children: List[Cp]) extends Many(children) { + override def companion: ManyCompanion = Choice + } + object Sequence extends ManyCompanion(separator = ',') + final case class Sequence(children: List[Cp]) extends Many(children) { + override def companion: ManyCompanion = Sequence + } + object Many { + def parse(string: String): Many = { + val choice: List[String] = Choice.split(string) + if (choice.length > 1) Choice(choice.map(Cp.parse)) + else Sequence(Sequence.split(string).map(Cp.parse)) + } + } + def parse(string: String): Elements = + if (!isParenthesized(string)) Element(string) + else Many.parse(removeParentheses(string)) + } + + final case class Cp(elements: Elements, occurrence: Occurrence) { + override def toString: String = s"$elements$occurrence" + } + object Cp { + def parse(string: String): Cp = { + val (maybeParenthesized: String, occurrence: Occurrence) = Occurrence.parse(string) + Cp(Elements.parse(maybeParenthesized), occurrence) + } + } + + sealed class Occurrence + object Occurrence { + case object Once extends Occurrence { + override def toString: String = "" + } + sealed trait Signed extends Occurrence { + final override def toString: String = sign + def sign: String + } + case object OnceOptional extends Signed { + override def sign: String = "?" + } + case object Repeat extends Signed { + override def sign: String = "+" + } + case object RepeatOptional extends Signed { + override def sign: String = "*" + } + def parse(string: String): (String, Occurrence) = + if (string.endsWith(OnceOptional.sign)) (string.init, OnceOptional) else + if (string.endsWith(RepeatOptional.sign)) (string.init, RepeatOptional) else + if (string.endsWith(Repeat.sign)) (string.init, Repeat) else + (string, Once) + } + + private def isParenthesized(string: String): Boolean = { + @tailrec + def isParenthesized(level: Int, tail: String): Boolean = { + val current: Char = tail.head + val nextTail: String = tail.tail + val nextLevel: Int = if (current == '(') level + 1 else if (current == ')') level - 1 else level + if (nextTail.isEmpty) nextLevel == 0 else if (nextLevel == 0) false else isParenthesized(nextLevel, nextTail) + } + + string.startsWith("(") && isParenthesized(0, string) + } + + @tailrec + private def removeParentheses(string: String): String = + if (!isParenthesized(string)) string + else removeParentheses(string.tail.init) + + // split at the top level of parentheses + private def split(string: String, separator: Char): List[String] = { + @tailrec + def split( + result: List[String], + level: Int, + init: String, + tail: String + ): List[String] = if (tail.isEmpty) if (init.isEmpty) result else result :+ init else { + val current: Char = tail.head + val nextTail: String = tail.tail + if (level == 0 && current == separator) split( + result :+ init, + level, + "", + nextTail + ) else split( + result, + if (current == '(') level + 1 else if (current == ')') level - 1 else level, + init :+ current, + nextTail + ) + } + + split( + List.empty, + 0, + "", + string + ) + } +} diff --git a/shared/src/main/scala/scala/xml/parsing/ExternalSources.scala b/shared/src/main/scala/scala/xml/parsing/ExternalSources.scala new file mode 100644 index 000000000..778ef6278 --- /dev/null +++ b/shared/src/main/scala/scala/xml/parsing/ExternalSources.scala @@ -0,0 +1,39 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml +package parsing + +import java.net.URL +import java.io.File.separator + +import scala.io.Source + +/** + * @author Burak Emir + */ +trait ExternalSources { + self: ExternalSources with MarkupParser with MarkupHandler => + + def externalSource(systemId: String): Source = { + if (systemId.startsWith("http:")) + return Source.fromURL(new URL(systemId)) + + val fileStr: String = input.descr match { + case x if x.startsWith("file:") => x.drop(5) + case x => x.take(x.lastIndexOf(separator) + 1) + } + + Source.fromFile(s"$fileStr$systemId") + } +} diff --git a/shared/src/main/scala/scala/xml/parsing/FactoryAdapter.scala b/shared/src/main/scala/scala/xml/parsing/FactoryAdapter.scala new file mode 100644 index 000000000..d3f75dd5c --- /dev/null +++ b/shared/src/main/scala/scala/xml/parsing/FactoryAdapter.scala @@ -0,0 +1,450 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml +package parsing + +import scala.collection.Seq +import org.xml.sax.{Attributes, Locator, SAXNotRecognizedException, SAXNotSupportedException} +import org.xml.sax.ext.{DefaultHandler2, Locator2} + +// can be mixed into FactoryAdapter if desired +trait ConsoleErrorHandler extends DefaultHandler2 { + // ignore warning, crimson warns even for entity resolution! + override def warning(ex: SAXParseException): Unit = () + override def error(ex: SAXParseException): Unit = printError("Error", ex) + override def fatalError(ex: SAXParseException): Unit = printError("Fatal Error", ex) + + protected def printError(errtype: String, ex: SAXParseException): Unit = + Console.withOut(Console.err) { + Console.println(s"[$errtype]:${ex.getLineNumber}:${ex.getColumnNumber}: ${ex.getMessage}") + Console.flush() + } +} + +/** + * SAX adapter class, for use with Java SAX parser. Keeps track of + * namespace bindings, without relying on namespace handling of the + * underlying SAX parser (but processing the parser's namespace-related events if it is namespace-aware). + */ +abstract class FactoryAdapter extends DefaultHandler2 with factory.XMLLoader[Node] { + val normalizeWhitespace: Boolean = false + + // reference to the XMLReader that parses the document; this is used to query + // features (e.g., 'is-standalone') and properties (e.g., document-xml-version) - + // see http://www.saxproject.org/apidoc/org/xml/sax/package-summary.html + private var xmlReader: Option[XMLReader] = None + + private var dtdBuilder: Option[DtdBuilder] = None + private def inDtd: Boolean = dtdBuilder.isDefined && !dtdBuilder.get.isDone + + private var document: Option[Document] = None + private var baseURI: Option[String] = None + private var xmlEncoding: Option[String] = None + + private var prefixMappings: List[(String, String)] = List.empty + + // TODO all the variables should be private, but - binary compatibility... + var prolog: List[Node] = List.empty + var rootElem: Node = _ + var epilogue: List[Node] = List.empty + + val buffer: StringBuilder = new StringBuilder() + private var inCDATA: Boolean = false + + /** List of attributes + * + * Previously was a mutable [[scala.collection.mutable.Stack Stack]], but is now a mutable reference to an immutable [[scala.collection.immutable.List List]]. + * + * @since 2.0.0 + */ + var attribStack: List[MetaData] = List.empty + /** List of elements + * + * Previously was a mutable [[scala.collection.mutable.Stack Stack]], but is now a mutable reference to an immutable [[scala.collection.immutable.List List]]. + * + * @since 2.0.0 + */ + var hStack: List[Node] = List.empty // [ element ] contains siblings + /** List of element names + * + * Previously was a mutable [[scala.collection.mutable.Stack Stack]], but is now a mutable reference to an immutable [[scala.collection.immutable.List List]]. + * + * @since 2.0.0 + */ + var tagStack: List[String] = List.empty + /** List of namespaces + * + * Previously was a mutable [[scala.collection.mutable.Stack Stack]], but is now a mutable reference to an immutable [[scala.collection.immutable.List List]]. + * + * @since 2.0.0 + */ + var scopeStack: List[NamespaceBinding] = List.empty + + var curTag: String = _ + var capture: Boolean = false + + /** + * Captures text or cdata. + */ + def captureText(): Unit = { + if (capture && buffer.nonEmpty) { + val text: String = buffer.toString + val newNode: Node = if (inCDATA) createPCData(text) else createText(text) + hStack ::= newNode + } + + buffer.clear() + inCDATA = false + } + + /** + * Load XML document from the inputSource using the xmlReader. + */ + def loadDocument(inputSource: InputSource, xmlReader: XMLReader): Document = { + if (inputSource == null) throw new IllegalArgumentException("InputSource cannot be null") + + xmlReader.setContentHandler(this) + xmlReader.setDTDHandler(this) + /* Do not overwrite pre-configured EntityResolver. */ + if (xmlReader.getEntityResolver == null) xmlReader.setEntityResolver(this) + /* Do not overwrite pre-configured ErrorHandler. */ + if (xmlReader.getErrorHandler == null) xmlReader.setErrorHandler(this) + + /* Use LexicalHandler if it is supported by the xmlReader. */ + try { + xmlReader.setProperty("http://xml.org/sax/properties/lexical-handler", this) + } catch { + case _: SAXNotRecognizedException => + case _: SAXNotSupportedException => + } + + /* Use DeclHandler if it is supported by the xmlReader. */ + try { + xmlReader.setProperty("http://xml.org/sax/properties/declaration-handler", this) + } catch { + case _: SAXNotRecognizedException => + case _: SAXNotSupportedException => + } + + this.xmlReader = Some(xmlReader) + xmlReader.parse(inputSource) + + document.get + } + + // abstract methods + + /** + * Tests if an XML element contains text. + * @return true if element named `localName` contains text. + */ + def nodeContainsText(localName: String): Boolean // abstract + + /** + * creates an new non-text(tree) node. + * @param elemName + * @param attribs + * @param chIter + * @return a new XML element. + */ + def createNode(pre: String, elemName: String, attribs: MetaData, + scope: NamespaceBinding, chIter: List[Node]): Node // abstract + + /** + * creates a Text node. + * @param text + * @return a new Text node. + */ + def createText(text: String): Text // abstract + + /** + * creates a PCData node. + * @param text + * @return a new PCData node. + */ + def createPCData(text: String): PCData // abstract + + /** + * creates a new processing instruction node. + */ + def createProcInstr(target: String, data: String): Seq[ProcInstr] + + /** + * creates a new comment node. + */ + def createComment(characters: String): Seq[Comment] + + /* ContentHandler methods */ + + // Since Java 14, ContentHandler has a method that delivers the values from the XML declaration: + // def declaration(version: String, encoding: String, standalone: String): Unit = () + // but it'll be years until we are all on Java 14 *and* Xerces starts calling this method... + + override def setDocumentLocator(locator: Locator): Unit = { + baseURI = Option(locator.getSystemId) + locator match { + case locator2: Locator2 => + // Note: Xerces calls setDocumentLocator() (and startDocument()) *before* it even reads the XML declaration; + // the version delivered here - locator2.getXMLVersion - is always "1.0"; + // the real version is retrieved as a property of the XML reader in endDocument(). + + xmlEncoding = Option(locator2.getEncoding) + case _ => + } + } + + override def startDocument(): Unit = { + scopeStack ::= TopScope // TODO turn into a parameter + } + + override def endDocument(): Unit = { + // capture the epilogue at the end of the document + epilogue = hStack.init.reverse + + val document = new Document + this.document = Some(document) + document.children = prolog ++ rootElem ++ epilogue + document.docElem = rootElem + document.dtd = dtdBuilder.map(_.dtd).orNull + document.baseURI = baseURI.orNull + document.encoding = xmlEncoding + + document.version = + try { + Option(xmlReader.get.getProperty("http://xml.org/sax/properties/document-xml-version").asInstanceOf[String]) + } catch { + case _: SAXNotRecognizedException => None + case _: SAXNotSupportedException => None + } + + document.standAlone = + try { + Some(xmlReader.get.getFeature("http://xml.org/sax/features/is-standalone")) + } catch { + case _: SAXNotRecognizedException => None + case _: SAXNotSupportedException => None + } + + // Note: resetting to the freshly-created state; needed only if this instance is reused, which we do not do... + dtdBuilder = None + xmlReader = None + + baseURI = None + xmlEncoding = None + + hStack = hStack.last :: Nil // TODO List.empty + scopeStack = scopeStack.tail // TODO List.empty + + rootElem = null + prolog = List.empty + epilogue = List.empty + + buffer.clear() + inCDATA = false + capture = false + curTag = null + + attribStack = List.empty + tagStack = List.empty + } + + override def startPrefixMapping(prefix: String, uri: String): Unit = + prefixMappings ::= ((prefix, uri)) + + override def endPrefixMapping(prefix: String): Unit = () + + /* Start element. */ + override def startElement( + uri: String, + _localName: String, + qname: String, + attributes: Attributes + ): Unit = { + captureText() + + // capture the prolog at the start of the root element + if (tagStack.isEmpty) { + prolog = hStack.reverse + hStack = List.empty + } + + tagStack ::= curTag + curTag = qname + + val localName: String = Utility.splitName(qname)._2 + capture = nodeContainsText(localName) + + hStack ::= null + var m: MetaData = Null + var scpe: NamespaceBinding = + if (scopeStack.isEmpty) TopScope + else scopeStack.head + + for (i <- 0.until(attributes.getLength).reverse) { + val qname: String = attributes.getQName(i) + val value: String = attributes.getValue(i) + val (pre: Option[String], key: String) = Utility.splitName(qname) + def nullIfEmpty(s: String): String = if (s == "") null else s + + if (pre.contains("xmlns") || (pre.isEmpty && qname == "xmlns")) { + val arg: String = if (pre.isEmpty) null else key + scpe = NamespaceBinding(arg, nullIfEmpty(value), scpe) + } else + m = Attribute(pre, key, Text(value), m) + } + + // Add namespace bindings for the prefix mappings declared by this element + // (if there are any, the parser is namespace-aware, and no namespace bindings were delivered as attributes). + // All `startPrefixMapping()` events will occur immediately before the corresponding `startElement()` event. + for ((prefix: String, uri: String) <- prefixMappings) + scpe = NamespaceBinding(if (prefix.isEmpty) null else prefix, uri, scpe) + + // Once the `prefixMappings` are processed into `scpe`, the list is emptied out + // so that already-declared namespaces are not re-declared on the nested elements. + prefixMappings = List.empty + + scopeStack ::= scpe + attribStack ::= m + } + + /** + * End element. + * @param uri + * @param _localName + * @param qname + * @throws org.xml.sax.SAXException if .. + */ + override def endElement(uri: String, _localName: String, qname: String): Unit = { + captureText() + val metaData: MetaData = attribStack.head + attribStack = attribStack.tail + + // reverse order to get it right + val v: List[Node] = hStack.takeWhile(_ != null).reverse + hStack = hStack.dropWhile(_ != null) match { + case null :: hs => hs + case hs => hs + } + val (pre: Option[String], localName: String) = Utility.splitName(qname) + val scp: NamespaceBinding = scopeStack.head + scopeStack = scopeStack.tail + + // create element + rootElem = createNode(pre.orNull, localName, metaData, scp, v) + hStack ::= rootElem + curTag = tagStack.head + tagStack = tagStack.tail + capture = curTag != null && nodeContainsText(curTag) // root level + } + + /** + * Capture characters, possibly normalizing whitespace. + * + * @param ch + * @param offset + * @param length + */ + override def characters(ch: Array[Char], offset: Int, length: Int): Unit = { + if (!capture) () + // compliant: report every character + else if (!normalizeWhitespace) buffer.appendAll(ch, offset, length) + // normalizing whitespace is not compliant, but useful + else { + var it: Iterator[Char] = ch.slice(offset, offset + length).iterator + while (it.hasNext) { + val c: Char = it.next() + val isSpace: Boolean = c.isWhitespace + buffer.append(if (isSpace) ' ' else c) + if (isSpace) + it = it.dropWhile(_.isWhitespace) + } + } + } + + override def ignorableWhitespace(ch: Array[Char], offset: Int, length: Int): Unit = () + + /** + * Processing instruction. + */ + override def processingInstruction(target: String, data: String): Unit = + if (inDtd) dtdBuilder.foreach(_.processingInstruction(target, data)) else { + captureText() + hStack = hStack.reverse_:::(createProcInstr(target, data).toList) + } + + override def skippedEntity(name: String): Unit = () + + /* LexicalHandler methods (see https://docs.oracle.com/javase/8/docs/api/org/xml/sax/ext/LexicalHandler.html) */ + + override def startDTD( + name: String, + publicId: String, + systemId: String + ): Unit = dtdBuilder = Some(DtdBuilder( + name, + publicId, + systemId + )) + + override def endDTD(): Unit = dtdBuilder.foreach(_.endDTD()) + + override def startEntity(name: String): Unit = dtdBuilder.foreach(_.startEntity(name)) + override def endEntity(name: String): Unit = dtdBuilder.foreach(_.endEntity(name)) + + /** + * Start of a CDATA section. + */ + override def startCDATA(): Unit = { + captureText() + inCDATA = true + } + + /** + * End of a CDATA section. + */ + override def endCDATA(): Unit = captureText() + + /** + * Comment. + */ + override def comment(ch: Array[Char], start: Int, length: Int): Unit = { + val commentText: String = String.valueOf(ch.slice(start, start + length)) + if (inDtd) dtdBuilder.foreach(_.comment(commentText)) else { + captureText() + hStack = hStack.reverse_:::(createComment(commentText).toList) + } + } + + /* DTDHandler methods (see https://docs.oracle.com/javase/8/docs/api/org/xml/sax/DTDHandler.html) */ + + override def notationDecl(name: String, publicId: String, systemId: String): Unit = + dtdBuilder.foreach(_.notationDecl(name, publicId, systemId)) + + override def unparsedEntityDecl(name: String, publicId: String, systemId: String, notationName: String): Unit = + dtdBuilder.foreach(_.unparsedEntityDecl(name, publicId, systemId, notationName)) + + /* DeclHandler methods (see https://docs.oracle.com/javase/8/docs/api/org/xml/sax/ext/DeclHandler.html) */ + + override def elementDecl(name: String, model: String): Unit = + dtdBuilder.foreach(_.elementDecl(name, model)) + + override def attributeDecl(eName: String, aName: String, `type`: String, mode: String, value: String): Unit = + dtdBuilder.foreach(_.attributeDecl(eName, aName, `type`, mode, value)) + + override def internalEntityDecl(name: String, value: String): Unit = + dtdBuilder.foreach(_.internalEntityDecl(name, value)) + + override def externalEntityDecl(name: String, publicId: String, systemId: String): Unit = + dtdBuilder.foreach(_.externalEntityDecl(name, publicId, systemId)) +} diff --git a/shared/src/main/scala/scala/xml/parsing/FatalError.scala b/shared/src/main/scala/scala/xml/parsing/FatalError.scala new file mode 100644 index 000000000..e77b7577d --- /dev/null +++ b/shared/src/main/scala/scala/xml/parsing/FatalError.scala @@ -0,0 +1,20 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml +package parsing + +/** + * !!! This is poorly named, but I guess it's in the API. + */ +case class FatalError(msg: String) extends java.lang.RuntimeException(msg) diff --git a/src/main/scala/scala/xml/parsing/MarkupHandler.scala b/shared/src/main/scala/scala/xml/parsing/MarkupHandler.scala similarity index 70% rename from src/main/scala/scala/xml/parsing/MarkupHandler.scala rename to shared/src/main/scala/scala/xml/parsing/MarkupHandler.scala index 369aa6c87..ab647cf76 100755 --- a/src/main/scala/scala/xml/parsing/MarkupHandler.scala +++ b/shared/src/main/scala/scala/xml/parsing/MarkupHandler.scala @@ -1,10 +1,14 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ package scala package xml @@ -16,11 +20,9 @@ import scala.xml.dtd._ /** * class that handles markup - provides callback methods to MarkupParser. - * the default is nonvalidating behaviour + * the default is nonvalidating behaviour * * @author Burak Emir - * @version 1.0 - * * @todo can we ignore more entity declarations (i.e. those with extIDs)? * @todo expanding entity references */ @@ -32,19 +34,15 @@ abstract class MarkupHandler { var decls: List[Decl] = Nil var ent: mutable.Map[String, EntityDecl] = new mutable.HashMap[String, EntityDecl]() - def lookupElemDecl(Label: String): ElemDecl = { - for (z@ElemDecl(Label, _) <- decls) - return z - - null - } + def lookupElemDecl(Label: String): ElemDecl = + (for (case z@ElemDecl(Label, _) <- decls) yield z).headOption.orNull def replacementText(entityName: String): Source = - Source fromString ((ent get entityName) match { + Source.fromString(ent.get(entityName) match { case Some(ParsedEntityDecl(_, IntDef(value))) => value - case Some(ParameterEntityDecl(_, IntDef(value))) => " %s " format value - case Some(_) => "" format entityName - case None => "" format entityName + case Some(ParameterEntityDecl(_, IntDef(value))) => s" value " + case Some(_) => s"" + case None => s"" }) def endDTD(n: String): Unit = () @@ -112,22 +110,19 @@ abstract class MarkupHandler { edef match { case _: ExtDef if !isValidating => // ignore (cf REC-xml 4.4.1) case _ => - val y = f(name, edef) + val y: EntityDecl = f(name, edef) decls ::= y ent.update(name, y) } def parameterEntityDecl(name: String, edef: EntityDef): Unit = - someEntityDecl(name, edef, ParameterEntityDecl.apply _) + someEntityDecl(name, edef, ParameterEntityDecl.apply) def parsedEntityDecl(name: String, edef: EntityDef): Unit = - someEntityDecl(name, edef, ParsedEntityDecl.apply _) + someEntityDecl(name, edef, ParsedEntityDecl.apply) - def peReference(name: String) { decls ::= PEReference(name) } + def peReference(name: String): Unit = { decls ::= PEReference(name) } def unparsedEntityDecl(name: String, extID: ExternalID, notat: String): Unit = () def notationDecl(notat: String, extID: ExternalID): Unit = () def reportSyntaxError(pos: Int, str: String): Unit - - @deprecated("This method and its usages will be removed. Use a debugger to debug code.", "2.11") - def log(msg: String): Unit = {} } diff --git a/src/main/scala/scala/xml/parsing/MarkupParser.scala b/shared/src/main/scala/scala/xml/parsing/MarkupParser.scala similarity index 71% rename from src/main/scala/scala/xml/parsing/MarkupParser.scala rename to shared/src/main/scala/scala/xml/parsing/MarkupParser.scala index 612cc88e3..688ca3425 100755 --- a/src/main/scala/scala/xml/parsing/MarkupParser.scala +++ b/shared/src/main/scala/scala/xml/parsing/MarkupParser.scala @@ -1,10 +1,14 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ package scala package xml @@ -25,21 +29,20 @@ import Utility.Escapes.{ pairs => unescape } * collected using side-effects. * * @author Burak Emir - * @version 1.0 */ trait MarkupParser extends MarkupParserCommon with TokenTests { self: MarkupParser with MarkupHandler => - type PositionType = Int - type InputType = Source - type ElementType = NodeSeq - type AttributesType = (MetaData, NamespaceBinding) - type NamespaceType = NamespaceBinding + override type PositionType = Int + override type InputType = Source + override type ElementType = NodeSeq + override type AttributesType = (MetaData, NamespaceBinding) + override type NamespaceType = NamespaceBinding - def truncatedError(msg: String): Nothing = throw FatalError(msg) - def errorNoEnd(tag: String) = throw FatalError("expected closing tag of " + tag) + override def truncatedError(msg: String): Nothing = throw FatalError(msg) + override def errorNoEnd(tag: String): Nothing = throw FatalError(s"expected closing tag of $tag") - def xHandleError(that: Char, msg: String) = reportSyntaxError(msg) + override def xHandleError(that: Char, msg: String): Unit = reportSyntaxError(msg) val input: Source @@ -55,26 +58,29 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { protected var curInput: Source = input // See ticket #3720 for motivations. - private class WithLookAhead(underlying: Source) extends Source { - private val queue = scala.collection.mutable.Queue[Char]() + // As for why it's `private[parsing]` rather than merely `private`, see + // https://github.com/scala/scala-xml/issues/541 ; the broader access is necessary, + // for now anyway, to work around https://github.com/scala/scala3/issues/13096 + private[parsing] class WithLookAhead(underlying: Source) extends Source { + private val queue: scala.collection.mutable.Queue[Char] = scala.collection.mutable.Queue[Char]() def lookahead(): BufferedIterator[Char] = { - val iter = queue.iterator ++ new Iterator[Char] { - def hasNext = underlying.hasNext - def next() = { val x = underlying.next(); queue += x; x } + val iter: Iterator[Char] = queue.iterator ++ new Iterator[Char] { + override def hasNext: Boolean = underlying.hasNext + override def next(): Char = { val x: Char = underlying.next(); queue += x; x } } iter.buffered } - val iter = new Iterator[Char] { - def hasNext = underlying.hasNext || !queue.isEmpty - def next() = if (!queue.isEmpty) queue.dequeue() else underlying.next() + override val iter: Iterator[Char] = new Iterator[Char] { + override def hasNext: Boolean = underlying.hasNext || queue.nonEmpty + override def next(): Char = if (queue.nonEmpty) queue.dequeue() else underlying.next() } } - def lookahead(): BufferedIterator[Char] = curInput match { + override def lookahead(): BufferedIterator[Char] = curInput match { case curInputWLA: WithLookAhead => curInputWLA.lookahead() case _ => - val newInput = new WithLookAhead(curInput) + val newInput: WithLookAhead = new WithLookAhead(curInput) curInput = newInput newInput.lookahead() } @@ -89,22 +95,25 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { var pos: Int = _ /* used when reading external subset */ - var extIndex = -1 + var extIndex: Int = -1 /** holds temporary values of pos */ + // Note: if marked as an override, this causes a "...cannot override a mutable variable" error with Scala 3; + // SethTisue noted on Oct 14, 2021 that scala/scala3#13744 should fix it - and it probably did, + // but Scala XML still builds against Scala 3 version that has this bug, so this still can not be marked as an override :( var tmppos: Int = _ /** holds the next character */ var nextChNeeded: Boolean = false var reachedEof: Boolean = false var lastChRead: Char = _ - def ch: Char = { + override def ch: Char = { if (nextChNeeded) { if (curInput.hasNext) { lastChRead = curInput.next() pos = curInput.pos } else { - val ilen = inpStack.length + val ilen: Int = inpStack.length //Console.println(" ilen = "+ilen+ " extIndex = "+extIndex); if ((ilen != extIndex) && (ilen > 0)) { /* for external source, inpStack == Nil ! need notify of eof! */ @@ -120,13 +129,13 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { } /** character buffer, for names */ - protected val cbuf = new StringBuilder() + protected val cbuf: StringBuilder = new StringBuilder() - var dtd: DTD = null + var dtd: DTD = _ - protected var doc: Document = null + protected var doc: Document = _ - def eof: Boolean = { ch; reachedEof } + override def eof: Boolean = { ch; reachedEof } // // methods @@ -140,7 +149,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { def xmlProcInstr(): MetaData = { xToken("xml") xSpace() - val (md, scp) = xAttributes(TopScope) + val (md: MetaData, scp: NamespaceBinding) = xAttributes(TopScope) if (scp != TopScope) reportSyntaxError("no xmlns definitions here, please.") xToken('?') @@ -156,8 +165,8 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { var info_enc: Option[String] = None var info_stdl: Option[Boolean] = None - val m = xmlProcInstr() - var n = 0 + val m: MetaData = xmlProcInstr() + var n: Int = 0 if (isProlog) xSpaceOpt() @@ -173,7 +182,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { case null => case Text(enc) => if (!isValidIANAEncoding(enc)) - reportSyntaxError("\"" + enc + "\" is not a valid encoding") + reportSyntaxError(s""""$enc" is not a valid encoding""") else { info_enc = Some(enc) n += 1 @@ -192,8 +201,8 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { } if (m.length - n != 0) { - val s = if (isProlog) "SDDecl? " else "" - reportSyntaxError("VersionInfo EncodingDecl? %sor '?>' expected!" format s) + val s: String = if (isProlog) "SDDecl? " else "" + reportSyntaxError(s"VersionInfo EncodingDecl? $s or '?>' expected!") } (info_ver, info_enc, info_stdl) @@ -233,7 +242,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { } nextch() // is prolog ? - var children: NodeSeq = null + var children: Seq[Node] = null if ('?' == ch) { nextch() info_prolog = prolog() @@ -243,13 +252,13 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { children = content(TopScope) // DTD handled as side effect } else { - val ts = new NodeBuffer() + val ts: NodeBuffer = new NodeBuffer() content1(TopScope, ts) // DTD handled as side effect ts &+ content(TopScope) - children = NodeSeq.fromSeq(ts) + children = ts.toVector } //println("[MarkupParser::document] children now: "+children.toList) - var elemCount = 0 + var elemCount: Int = 0 var theNode: Node = null for (c <- children) c match { case _: ProcInstr => @@ -257,7 +266,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { case _: EntityRef => // todo: fix entities, shouldn't be "special" reportSyntaxError("no entity references allowed here") case s: SpecialNode => - if (s.toString.trim().length > 0) //non-empty text nodes not allowed + if (s.toString.trim.nonEmpty) //non-empty text nodes not allowed elemCount += 2 case m: Node => elemCount += 1 @@ -265,7 +274,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { } if (1 != elemCount) { reportSyntaxError("document must contain exactly one element") - Console.println(children.toList) + //Console.println(children.toList) } doc.children = children @@ -274,7 +283,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { } /** append Unicode character to name buffer*/ - protected def putChar(c: Char) = cbuf append c + protected def putChar(c: Char): StringBuilder = cbuf.append(c) /** * As the current code requires you to call nextch once manually @@ -285,17 +294,17 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { this } - protected def ch_returning_nextch: Char = { val res = ch; nextch(); res } + override protected def ch_returning_nextch: Char = { val res: Char = ch; nextch(); res } - def mkAttributes(name: String, pscope: NamespaceBinding): AttributesType = + override def mkAttributes(name: String, pscope: NamespaceBinding): AttributesType = if (isNameStart (ch)) xAttributes(pscope) else (Null, pscope) - def mkProcInstr(position: Int, name: String, text: String): ElementType = + override def mkProcInstr(position: Int, name: String, text: String): ElementType = handle.procInstr(position, name, text) /** this method tells ch to get the next character when next called */ - def nextch() { + override def nextch(): Unit = { // Read current ch if needed ch @@ -313,22 +322,22 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { var scope: NamespaceBinding = pscope var aMap: MetaData = Null while (isNameStart(ch)) { - val qname = xName + val qname: String = xName xEQ() // side effect - val value = xAttributeValue() + val value: String = xAttributeValue() Utility.prefix(qname) match { case Some("xmlns") => - val prefix = qname.substring(6 /*xmlns:*/ , qname.length) - scope = new NamespaceBinding(prefix, value, scope) + val prefix: String = qname.substring(6 /*xmlns:*/ , qname.length) + scope = NamespaceBinding(prefix, value, scope) case Some(prefix) => - val key = qname.substring(prefix.length + 1, qname.length) + val key: String = qname.substring(prefix.length + 1, qname.length) aMap = new PrefixedAttribute(prefix, key, Text(value), aMap) case _ => if (qname == "xmlns") - scope = new NamespaceBinding(null, value, scope) + scope = NamespaceBinding(null, value, scope) else aMap = new UnprefixedAttribute(qname, Text(value), aMap) } @@ -340,7 +349,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { if (!aMap.wellformed(scope)) reportSyntaxError("double attribute") - (aMap, scope) + (aMap.reverse, scope) } /** @@ -351,15 +360,15 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { * }}} */ def xEntityValue(): String = { - val endch = ch + val endch: Char = ch nextch() while (ch != endch && !eof) { putChar(ch) nextch() } nextch() - val str = cbuf.toString() - cbuf.length = 0 + val str: String = cbuf.toString + cbuf.setLength(0) str } @@ -389,16 +398,16 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { def xComment: NodeSeq = { val sb: StringBuilder = new StringBuilder() xToken("--") - while (true) { + while (!eof) { if (ch == '-' && { sb.append(ch); nextch(); ch == '-' }) { - sb.length = sb.length - 1 + sb.setLength(sb.length - 1) nextch() xToken('>') - return handle.comment(pos, sb.toString()) + return handle.comment(pos, sb.toString) } else sb.append(ch) nextch() } - throw FatalError("this cannot happen") + truncatedError("broken comment") } /* todo: move this into the NodeBuilder class */ @@ -416,7 +425,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { * '<' content1 ::= ... * }}} */ - def content1(pscope: NamespaceBinding, ts: NodeBuffer) { + def content1(pscope: NamespaceBinding, ts: NodeBuffer): Unit = { ch match { case '!' => nextch() @@ -440,10 +449,9 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { * }}} */ def content(pscope: NamespaceBinding): NodeSeq = { - val ts = new NodeBuffer - var exit = eof - // todo: optimize seq repr. - def done = new NodeSeq { val theSeq = ts.toList } + val ts: NodeBuffer = new NodeBuffer + var exit: Boolean = eof + def done: NodeSeq = NodeSeq.fromSeq(ts.toVector) while (!exit) { tmppos = pos @@ -464,14 +472,14 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { nextch(); ch match { case '#' => // CharacterRef nextch() - val theChar = handle.text(tmppos, xCharRef(() => ch, () => nextch())) + val theChar: NodeSeq = handle.text(tmppos, xCharRef(() => ch, () => nextch())) xToken(';') ts &+ theChar case _ => // EntityRef - val n = xName + val n: String = xName xToken(';') - if (unescape contains n) { + if (unescape.contains(n)) { handle.entityRef(tmppos, n) ts &+ unescape(n) } else push(n) @@ -494,15 +502,15 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { nextch() xToken("YSTEM") xSpace() - val sysID = systemLiteral() - new SystemID(sysID) + val sysID: String = systemLiteral() + SystemID(sysID) case 'P' => nextch(); xToken("UBLIC") xSpace() - val pubID = pubidLiteral() + val pubID: String = pubidLiteral() xSpace() - val sysID = systemLiteral() - new PublicID(pubID, sysID) + val sysID: String = systemLiteral() + PublicID(pubID, sysID) } /** @@ -512,14 +520,14 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { * * }}} */ - def parseDTD() { // dirty but fast + def parseDTD(): Unit = { // dirty but fast var extID: ExternalID = null - if (this.dtd ne null) + if (dtd != null) reportSyntaxError("unexpected character (DOCTYPE already defined") xToken("DOCTYPE") xSpace() - val n = xName - xSpace() + val n: String = xName + xSpaceOpt() //external ID if ('S' == ch || 'P' == ch) { extID = externalID() @@ -549,11 +557,11 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { } xToken('>') this.dtd = new DTD { - /*override var*/ externalID = extID + this.externalID = extID /*override val */ decls = handle.decls.reverse } //this.dtd.initializeEntities(); - if (doc ne null) + if (doc != null) doc.dtd = this.dtd handle.endDTD(n) @@ -571,27 +579,24 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { * }}} */ def element1(pscope: NamespaceBinding): NodeSeq = { - val pos = this.pos - val (qname, (aMap, scope)) = xTag(pscope) - val (pre, local) = Utility.prefix(qname) match { - case Some(p) => (p, qname drop p.length + 1) - case _ => (null, qname) - } - val ts = { + val pos: Int = this.pos + val (qname: String, (aMap: MetaData, scope: NamespaceBinding)) = xTag(pscope) + val (pre: Option[String], local: String) = Utility.splitName(qname) + val ts: NodeSeq = { if (ch == '/') { // empty element xToken("/>") - handle.elemStart(pos, pre, local, aMap, scope) + handle.elemStart(pos, pre.orNull, local, aMap, scope) NodeSeq.Empty } else { // element with content xToken('>') - handle.elemStart(pos, pre, local, aMap, scope) - val tmp = content(scope) + handle.elemStart(pos, pre.orNull, local, aMap, scope) + val tmp: NodeSeq = content(scope) xEndTag(qname) tmp } } - val res = handle.elem(pos, pre, local, aMap, scope, ts == NodeSeq.Empty, ts) - handle.elemEnd(pos, pre, local) + val res: NodeSeq = handle.elem(pos, pre.orNull, local, aMap, scope, ts == NodeSeq.Empty, ts) + handle.elemEnd(pos, pre.orNull, local) res } @@ -601,15 +606,15 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { * precondition: `xEmbeddedBlock == false` (we are not in a scala block) */ private def xText: String = { - var exit = false + var exit: Boolean = false while (!exit) { putChar(ch) nextch() exit = eof || (ch == '<') || (ch == '&') } - val str = cbuf.toString - cbuf.length = 0 + val str: String = cbuf.toString + cbuf.setLength(0) str } @@ -621,7 +626,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { * }}} */ def systemLiteral(): String = { - val endch = ch + val endch: Char = ch if (ch != '\'' && ch != '"') reportSyntaxError("quote ' or \" expected") nextch() @@ -630,8 +635,8 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { nextch() } nextch() - val str = cbuf.toString() - cbuf.length = 0 + val str: String = cbuf.toString + cbuf.setLength(0) str } @@ -641,7 +646,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { * }}} */ def pubidLiteral(): String = { - val endch = ch + val endch: Char = ch if (ch != '\'' && ch != '"') reportSyntaxError("quote ' or \" expected") nextch() @@ -649,12 +654,12 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { putChar(ch) //println("hello '"+ch+"'"+isPubIDChar(ch)) if (!isPubIDChar(ch)) - reportSyntaxError("char '" + ch + "' is not allowed in public id") + reportSyntaxError(s"char '$ch' is not allowed in public id") nextch() } nextch() - val str = cbuf.toString - cbuf.length = 0 + val str: String = cbuf.toString + cbuf.setLength(0) str } @@ -676,12 +681,12 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { markupDecl() } - def markupDecl1() = { - def doInclude() = { - xToken('['); while (']' != ch) markupDecl(); nextch() // ']' + def markupDecl1(): Any = { + def doInclude(): Unit = { + xToken('['); while (']' != ch && !eof) markupDecl(); nextch() // ']' } - def doIgnore() = { - xToken('['); while (']' != ch) nextch(); nextch() // ']' + def doIgnore(): Unit = { + xToken('['); while (']' != ch && !eof) nextch(); nextch() // ']' } if ('?' == ch) { nextch() @@ -714,13 +719,13 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { ch match { case '%' => nextch() - val ent = xName + val ent: String = xName xToken(';') xSpaceOpt() push(ent) xSpaceOpt() - val stmt = xName + val stmt: String = xName xSpaceOpt() stmt match { @@ -746,8 +751,8 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { xToken('>') case _ => - curInput.reportError(pos, "unexpected character '" + ch + "', expected some markupdecl") - while (ch != '>') + curInput.reportError(pos, s"unexpected character '$ch', expected some markupdecl") + while (ch != '>' && !eof) nextch() } } @@ -756,7 +761,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { def markupDecl(): Unit = ch match { case '%' => // parameter entity reference nextch() - val ent = xName + val ent: String = xName xToken(';') if (!isValidating) handle.peReference(ent) // n-v: just create PE-reference @@ -770,37 +775,37 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { case _ if isSpace(ch) => xSpace() case _ => - reportSyntaxError("markupdecl: unexpected character '" + ch + "' #" + ch.toInt) + reportSyntaxError(s"markupdecl: unexpected character '$ch' #${ch.toInt}") nextch() } /** * "rec-xml/#ExtSubset" pe references may not occur within markup declarations */ - def intSubset() { + def intSubset(): Unit = { //Console.println("(DEBUG) intSubset()") xSpace() - while (']' != ch) + while (']' != ch && !eof) markupDecl() } /** * <! element := ELEMENT */ - def elementDecl() { + def elementDecl(): Unit = { xToken("EMENT") xSpace() - val n = xName + val n: String = xName xSpace() - while ('>' != ch) { + while ('>' != ch && !eof) { //Console.println("["+ch+"]") putChar(ch) nextch() } //Console.println("END["+ch+"]") nextch() - val cmstr = cbuf.toString() - cbuf.length = 0 + val cmstr: String = cbuf.toString + cbuf.setLength(0) handle.elemDecl(n, cmstr) } @@ -809,16 +814,16 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { * ' != ch) { - val aname = xName + while ('>' != ch && !eof) { + val aname: String = xName xSpace() // could be enumeration (foo,bar) parse this later :-/ while ('"' != ch && '\'' != ch && '#' != ch && '<' != ch) { @@ -826,8 +831,8 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { cbuf.append(ch) nextch() } - val atpe = cbuf.toString - cbuf.length = 0 + val atpe: String = cbuf.toString + cbuf.setLength(0) val defdecl: DefaultDecl = ch match { case '\'' | '"' => @@ -847,7 +852,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { xSpaceOpt() attList ::= AttrDecl(aname, atpe, defdecl) - cbuf.length = 0 + cbuf.setLength(0) } nextch() handle.attListDecl(n, attList.reverse) @@ -858,8 +863,8 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { * //sy - val extID = externalID() + val extID: ExternalID = externalID() if (isParameterEntity) { xSpaceOpt() xToken('>') @@ -881,7 +886,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { if ('>' != ch) { xToken("NDATA") xSpace() - val notat = xName + val notat: String = xName xSpaceOpt() xToken('>') handle.unparsedEntityDecl(n, extID, notat) @@ -892,7 +897,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { } case '"' | '\'' => - val av = xEntityValue() + val av: String = xEntityValue() xSpaceOpt() xToken('>') if (isParameterEntity) @@ -908,39 +913,39 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { * 'N' notationDecl ::= "OTATION" * }}} */ - def notationDecl() { + def notationDecl(): Unit = { xToken("OTATION") xSpace() - val notat = xName + val notat: String = xName xSpace() - val extID = if (ch == 'S') { + val extID: ExternalID = if (ch == 'S') { externalID() } else if (ch == 'P') { /* PublicID (without system, only used in NOTATION) */ nextch() xToken("UBLIC") xSpace() - val pubID = pubidLiteral() + val pubID: String = pubidLiteral() xSpaceOpt() - val sysID = if (ch != '>') + val sysID: String = if (ch != '>') systemLiteral() else null - new PublicID(pubID, sysID) + PublicID(pubID, sysID) } else { reportSyntaxError("PUBLIC or SYSTEM expected") - scala.sys.error("died parsing notationdecl") + truncatedError("died parsing notationdecl") } xSpaceOpt() xToken('>') handle.notationDecl(notat, extID) } - def reportSyntaxError(pos: Int, str: String) { curInput.reportError(pos, str) } - def reportSyntaxError(str: String) { reportSyntaxError(pos, str) } - def reportValidationError(pos: Int, str: String) { reportSyntaxError(pos, str) } + override def reportSyntaxError(pos: Int, str: String): Unit = { curInput.reportError(pos, str) } + override def reportSyntaxError(str: String): Unit = { reportSyntaxError(pos, str) } + def reportValidationError(pos: Int, str: String): Unit = { reportSyntaxError(pos, str) } - def push(entityName: String) { + def push(entityName: String): Unit = { if (!eof) inpStack = curInput :: inpStack @@ -951,7 +956,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { nextch() } - def pushExternal(systemId: String) { + def pushExternal(systemId: String): Unit = { if (!eof) inpStack = curInput :: inpStack @@ -962,7 +967,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests { nextch() } - def pop() { + def pop(): Unit = { curInput = inpStack.head inpStack = inpStack.tail lastChRead = curInput.ch diff --git a/src/main/scala/scala/xml/parsing/MarkupParserCommon.scala b/shared/src/main/scala/scala/xml/parsing/MarkupParserCommon.scala similarity index 71% rename from src/main/scala/scala/xml/parsing/MarkupParserCommon.scala rename to shared/src/main/scala/scala/xml/parsing/MarkupParserCommon.scala index 31d81d343..be88269d2 100644 --- a/src/main/scala/scala/xml/parsing/MarkupParserCommon.scala +++ b/shared/src/main/scala/scala/xml/parsing/MarkupParserCommon.scala @@ -1,19 +1,20 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ package scala package xml package parsing -import scala.io.Source -import scala.annotation.switch -import Utility.Escapes.{ pairs => unescape } - +import scala.collection.Seq import Utility.SU /** @@ -21,8 +22,9 @@ import Utility.SU * between the library level XML parser and the compiler's. * All members should be accessed through those. */ +// Note: this is no longer true; Scala compiler uses its own copy since at least 2013. private[scala] trait MarkupParserCommon extends TokenTests { - protected def unreachable = scala.sys.error("Cannot be reached.") + protected def unreachable: Nothing = truncatedError("Cannot be reached.") // type HandleType // MarkupHandler, SymbolicXMLBuilder type InputType // Source, CharArrayReader @@ -40,7 +42,7 @@ private[scala] trait MarkupParserCommon extends TokenTests { * [44] EmptyElemTag ::= '<' Name { S Attribute } [S] */ protected def xTag(pscope: NamespaceType): (String, AttributesType) = { - val name = xName + val name: String = xName xSpaceOpt() (name, mkAttributes(name, pscope)) @@ -52,7 +54,7 @@ private[scala] trait MarkupParserCommon extends TokenTests { * see [15] */ def xProcInstr: ElementType = { - val n = xName + val n: String = xName xSpaceOpt() xTakeUntil(mkProcInstr(_, n, _), () => tmppos, "?>") } @@ -62,12 +64,12 @@ private[scala] trait MarkupParserCommon extends TokenTests { * @param endCh either `'` or `"` */ def xAttributeValue(endCh: Char): String = { - val buf = new StringBuilder - while (ch != endCh) { + val buf: StringBuilder = new StringBuilder + while (ch != endCh && !eof) { // well-formedness constraint - if (ch == '<') return errorAndResult("'<' not allowed in attrib value", "") + if (ch == '<') truncatedError("'<' not allowed in attrib value") else if (ch == SU) truncatedError("") - else buf append ch_returning_nextch + else buf.append(ch_returning_nextch) } ch_returning_nextch // @todo: normalize attribute value @@ -75,24 +77,24 @@ private[scala] trait MarkupParserCommon extends TokenTests { } def xAttributeValue(): String = { - val str = xAttributeValue(ch_returning_nextch) + val str: String = xAttributeValue(ch_returning_nextch) // well-formedness constraint normalizeAttributeValue(str) } private def takeUntilChar(it: Iterator[Char], end: Char): String = { - val buf = new StringBuilder + val buf: StringBuilder = new StringBuilder while (it.hasNext) it.next() match { case `end` => return buf.toString - case ch => buf append ch + case ch => buf.append(ch) } - scala.sys.error("Expected '%s'".format(end)) + scala.sys.error(s"Expected '$end'") } /** * [42] '<' xmlEndTag ::= '<' '/' Name S? '>' */ - def xEndTag(startName: String) { + def xEndTag(startName: String): Unit = { xToken('/') if (xName != startName) errorNoEnd(startName) @@ -114,27 +116,26 @@ private[scala] trait MarkupParserCommon extends TokenTests { if (ch == SU) truncatedError("") else if (!isNameStart(ch)) - return errorAndResult("name expected, but char '%s' cannot start a name" format ch, "") + return errorAndResult(s"name expected, but char '$ch' cannot start a name", "") - val buf = new StringBuilder + val buf: StringBuilder = new StringBuilder - do buf append ch_returning_nextch - while (isNameChar(ch)) + while ({ buf.append(ch_returning_nextch); isNameChar(ch)}) () if (buf.last == ':') { reportSyntaxError("name cannot end in ':'") - buf.toString dropRight 1 + buf.toString.dropRight(1) } else buf.toString } - private def attr_unescape(s: String) = s match { + private def attr_unescape(s: String): String = s match { case "lt" => "<" case "gt" => ">" case "amp" => "&" case "apos" => "'" case "quot" => "\"" case "quote" => "\"" - case _ => "&" + s + ";" + case _ => s"&$s;" } /** @@ -142,13 +143,12 @@ private[scala] trait MarkupParserCommon extends TokenTests { * see spec 3.3.3 */ private def normalizeAttributeValue(attval: String): String = { - val buf = new StringBuilder - val it = attval.iterator.buffered + val buf: StringBuilder = new StringBuilder + val it: BufferedIterator[Char] = attval.iterator.buffered - while (it.hasNext) buf append (it.next() match { + while (it.hasNext) buf.append(it.next() match { case ' ' | '\t' | '\n' | '\r' => " " - case '&' if it.head == '#' => - it.next(); xCharRef(it) + case '&' if it.head == '#' => it.next(); xCharRef(it) case '&' => attr_unescape(takeUntilChar(it, ';')) case c => c }) @@ -163,11 +163,11 @@ private[scala] trait MarkupParserCommon extends TokenTests { * see [66] */ def xCharRef(ch: () => Char, nextch: () => Unit): String = - Utility.parseCharRef(ch, nextch, reportSyntaxError _, truncatedError _) + Utility.parseCharRef(ch, nextch, reportSyntaxError, truncatedError) def xCharRef(it: Iterator[Char]): String = { - var c = it.next() - Utility.parseCharRef(() => c, () => { c = it.next() }, reportSyntaxError _, truncatedError _) + var c: Char = it.next() + Utility.parseCharRef(() => c, () => { c = it.next() }, reportSyntaxError, truncatedError) } def xCharRef: String = xCharRef(() => ch, () => nextch()) @@ -203,20 +203,20 @@ private[scala] trait MarkupParserCommon extends TokenTests { x } - def xToken(that: Char) { + def xToken(that: Char): Unit = { if (ch == that) nextch() - else xHandleError(that, "'%s' expected instead of '%s'".format(that, ch)) + else xHandleError(that, s"'$that' expected instead of '$ch'") } - def xToken(that: Seq[Char]) { that foreach xToken } + def xToken(that: Seq[Char]): Unit = that.foreach(xToken) /** scan [S] '=' [S]*/ - def xEQ() = { xSpaceOpt(); xToken('='); xSpaceOpt() } + def xEQ(): Unit = { xSpaceOpt(); xToken('='); xSpaceOpt() } /** skip optional space S? */ - def xSpaceOpt() = while (isSpace(ch) && !eof) nextch() + def xSpaceOpt(): Unit = while (isSpace(ch) && !eof) nextch() /** scan [3] S ::= (#x20 | #x9 | #xD | #xA)+ */ - def xSpace() = + def xSpace(): Unit = if (isSpace(ch)) { nextch(); xSpaceOpt() } else xHandleError(ch, "whitespace expected") @@ -225,7 +225,7 @@ private[scala] trait MarkupParserCommon extends TokenTests { /** Execute body with a variable saved and restored after execution */ def saving[A, B](getter: A, setter: A => Unit)(body: => B): B = { - val saved = getter + val saved: A = getter try body finally setter(saved) } @@ -240,17 +240,17 @@ private[scala] trait MarkupParserCommon extends TokenTests { positioner: () => PositionType, until: String): T = { - val sb = new StringBuilder - val head = until.head - val rest = until.tail + val sb: StringBuilder = new StringBuilder + val head: Char = until.head + val rest: String = until.tail - while (true) { + while (!eof) { if (ch == head && peek(rest)) return handler(positioner(), sb.toString) - else if (ch == SU) - truncatedError("") // throws TruncatedXMLControl in compiler + else if (ch == SU || eof) + truncatedError(s"died parsing until $until") // throws TruncatedXMLControl in compiler - sb append ch + sb.append(ch) nextch() } unreachable @@ -263,9 +263,9 @@ private[scala] trait MarkupParserCommon extends TokenTests { * and leave input unchanged. */ private def peek(lookingFor: String): Boolean = - (lookahead() take lookingFor.length sameElements lookingFor.iterator) && { + lookahead().take(lookingFor.length).sameElements(lookingFor.iterator) && { // drop the chars from the real reader (all lookahead + orig) - (0 to lookingFor.length) foreach (_ => nextch()) + 0.to(lookingFor.length).foreach(_ => nextch()) true } } diff --git a/shared/src/main/scala/scala/xml/parsing/NoBindingFactoryAdapter.scala b/shared/src/main/scala/scala/xml/parsing/NoBindingFactoryAdapter.scala new file mode 100644 index 000000000..fcc522a6b --- /dev/null +++ b/shared/src/main/scala/scala/xml/parsing/NoBindingFactoryAdapter.scala @@ -0,0 +1,48 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml +package parsing + +import scala.collection.Seq +import factory.NodeFactory + +/** + * nobinding adaptor providing callbacks to parser to create elements. + * implements hash-consing + */ +class NoBindingFactoryAdapter extends FactoryAdapter with NodeFactory[Elem] { + /** True. Every XML node may contain text that the application needs */ + override def nodeContainsText(label: String): Boolean = true + + /** From NodeFactory. Constructs an instance of scala.xml.Elem -- TODO: deprecate as in Elem */ + override protected def create(pre: String, label: String, attrs: MetaData, scope: NamespaceBinding, children: Seq[Node]): Elem = + Elem(pre, label, attrs, scope, children.isEmpty, children.toSeq: _*) + + /** From FactoryAdapter. Creates a node. never creates the same node twice, using hash-consing. + TODO: deprecate as in Elem, or forward to create?? */ + override def createNode(pre: String, label: String, attrs: MetaData, scope: NamespaceBinding, children: List[Node]): Elem = + Elem(pre, label, attrs, scope, children.isEmpty, children: _*) + + /** Creates a text node. */ + override def createText(text: String): Text = makeText(text) + + /** Creates a processing instruction. */ + override def createProcInstr(target: String, data: String): Seq[ProcInstr] = makeProcInstr(target, data) + + /** Creates a comment. */ + override def createComment(characters: String): Seq[Comment] = makeComment(characters) + + /** Creates a cdata. */ + override def createPCData(characters: String): PCData = makePCData(characters) +} diff --git a/src/main/scala/scala/xml/parsing/TokenTests.scala b/shared/src/main/scala/scala/xml/parsing/TokenTests.scala similarity index 54% rename from src/main/scala/scala/xml/parsing/TokenTests.scala rename to shared/src/main/scala/scala/xml/parsing/TokenTests.scala index f528f7db0..3875d4683 100644 --- a/src/main/scala/scala/xml/parsing/TokenTests.scala +++ b/shared/src/main/scala/scala/xml/parsing/TokenTests.scala @@ -1,15 +1,21 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ package scala package xml package parsing +import scala.collection.Seq + /** * Helper functions for parsing XML fragments */ @@ -29,20 +35,20 @@ trait TokenTests { * (#x20 | #x9 | #xD | #xA)+ * }}} */ - final def isSpace(cs: Seq[Char]): Boolean = cs.nonEmpty && (cs forall isSpace) + final def isSpace(cs: Seq[Char]): Boolean = cs.nonEmpty && cs.forall(isSpace) /** These are 99% sure to be redundant but refactoring on the safe side. */ - def isAlpha(c: Char) = (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') - def isAlphaDigit(c: Char) = isAlpha(c) || (c >= '0' && c <= '9') + def isAlpha(c: Char): Boolean = (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') + def isAlphaDigit(c: Char): Boolean = isAlpha(c) || (c >= '0' && c <= '9') /** * {{{ - * NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' + * NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' | #xB7 * | CombiningChar | Extender * }}} - * See [4] and Appendix B of XML 1.0 specification. + * See [4] and [4a] of Appendix B of XML 1.0 specification. */ - def isNameChar(ch: Char) = { + def isNameChar(ch: Char): Boolean = { import java.lang.Character._ // The constants represent groups Mc, Me, Mn, Lm, and Nd. @@ -50,28 +56,28 @@ trait TokenTests { case COMBINING_SPACING_MARK | ENCLOSING_MARK | NON_SPACING_MARK | MODIFIER_LETTER | DECIMAL_DIGIT_NUMBER => true - case _ => ".-:" contains ch + case _ => ".-:·".contains(ch) }) } /** * {{{ - * NameStart ::= ( Letter | '_' ) + * NameStart ::= ( Letter | '_' | ':' ) * }}} * where Letter means in one of the Unicode general * categories `{ Ll, Lu, Lo, Lt, Nl }`. * * We do not allow a name to start with `:`. - * See [3] and Appendix B of XML 1.0 specification + * See [4] and Appendix B of XML 1.0 specification */ - def isNameStart(ch: Char) = { + def isNameStart(ch: Char): Boolean = { import java.lang.Character._ getType(ch).toByte match { case LOWERCASE_LETTER | UPPERCASE_LETTER | OTHER_LETTER | TITLECASE_LETTER | LETTER_NUMBER => true - case _ => ch == '_' + case _ => ":_".contains(ch) } } @@ -81,12 +87,12 @@ trait TokenTests { * }}} * See [5] of XML 1.0 specification. */ - def isName(s: String) = - s.nonEmpty && isNameStart(s.head) && (s.tail forall isNameChar) + def isName(s: String): Boolean = + s.nonEmpty && isNameStart(s.head) && s.tail.forall(isNameChar) def isPubIDChar(ch: Char): Boolean = isAlphaDigit(ch) || (isSpace(ch) && ch != '\u0009') || - ("""-\()+,./:=?;!*#@$_%""" contains ch) + """-\()+,./:=?;!*#@$_%""".contains(ch) /** * Returns `true` if the encoding name is a valid IANA encoding. @@ -96,13 +102,13 @@ trait TokenTests { * * @param ianaEncoding The IANA encoding name. */ - def isValidIANAEncoding(ianaEncoding: Seq[Char]) = { - def charOK(c: Char) = isAlphaDigit(c) || ("._-" contains c) + def isValidIANAEncoding(ianaEncoding: Seq[Char]): Boolean = { + def charOK(c: Char): Boolean = isAlphaDigit(c) || "._-".contains(c) ianaEncoding.nonEmpty && isAlpha(ianaEncoding.head) && - (ianaEncoding.tail forall charOK) + ianaEncoding.tail.forall(charOK) } - def checkSysID(s: String) = List('"', '\'') exists (c => !(s contains c)) - def checkPubID(s: String) = s forall isPubIDChar + def checkSysID(s: String): Boolean = List('"', '\'').exists(c => !s.contains(c)) + def checkPubID(s: String): Boolean = s.forall(isPubIDChar) } diff --git a/src/main/scala/scala/xml/parsing/XhtmlEntities.scala b/shared/src/main/scala/scala/xml/parsing/XhtmlEntities.scala similarity index 82% rename from src/main/scala/scala/xml/parsing/XhtmlEntities.scala rename to shared/src/main/scala/scala/xml/parsing/XhtmlEntities.scala index 7d5ba4340..e87eea1b1 100644 --- a/src/main/scala/scala/xml/parsing/XhtmlEntities.scala +++ b/shared/src/main/scala/scala/xml/parsing/XhtmlEntities.scala @@ -1,10 +1,14 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ package scala package xml @@ -17,7 +21,7 @@ import scala.xml.dtd.{ IntDef, ParsedEntityDecl } * */ object XhtmlEntities { - val entList = List(("quot", 34), ("amp", 38), ("lt", 60), ("gt", 62), ("nbsp", 160), ("iexcl", 161), ("cent", 162), ("pound", 163), ("curren", 164), ("yen", 165), + 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), ("euro", 8364), ("brvbar", 166), ("sect", 167), ("uml", 168), ("copy", 169), ("ordf", 170), ("laquo", 171), ("shy", 173), ("reg", 174), ("trade", 8482), ("macr", 175), ("deg", 176), ("plusmn", 177), ("sup2", 178), ("sup3", 179), ("acute", 180), ("micro", 181), ("para", 182), ("middot", 183), ("cedil", 184), ("sup1", 185), ("ordm", 186), ("raquo", 187), ("frac14", 188), ("frac12", 189), ("frac34", 190), ("iquest", 191), ("times", 215), ("divide", 247), @@ -32,7 +36,7 @@ object XhtmlEntities { ("rlm", 8207), ("ndash", 8211), ("mdash", 8212), ("lsquo", 8216), ("rsquo", 8217), ("sbquo", 8218), ("ldquo", 8220), ("rdquo", 8221), ("bdquo", 8222), ("dagger", 8224), ("Dagger", 8225), ("permil", 8240), ("lsaquo", 8249), ("rsaquo", 8250), ("fnof", 402), ("bull", 8226), ("hellip", 8230), ("prime", 8242), ("Prime", 8243), ("oline", 8254), ("frasl", 8260), ("weierp", 8472), ("image", 8465), ("real", 8476), ("alefsym", 8501), ("larr", 8592), ("uarr", 8593), - ("rarr", 8594), ("darr", 8495), ("harr", 8596), ("crarr", 8629), ("lArr", 8656), ("uArr", 8657), ("rArr", 8658), ("dArr", 8659), ("hArr", 8660), + ("rarr", 8594), ("darr", 8595), ("harr", 8596), ("crarr", 8629), ("lArr", 8656), ("uArr", 8657), ("rArr", 8658), ("dArr", 8659), ("hArr", 8660), ("forall", 8704), ("part", 8706), ("exist", 8707), ("empty", 8709), ("nabla", 8711), ("isin", 8712), ("notin", 8713), ("ni", 8715), ("prod", 8719), ("sum", 8721), ("minus", 8722), ("lowast", 8727), ("radic", 8730), ("prop", 8733), ("infin", 8734), ("ang", 8736), ("and", 8743), ("or", 8744), ("cap", 8745), ("cup", 8746), ("int", 8747), ("there4", 8756), ("sim", 8764), ("cong", 8773), ("asymp", 8776), ("ne", 8800), ("equiv", 8801), ("le", 8804), @@ -47,8 +51,8 @@ object XhtmlEntities { val entMap: Map[String, Char] = Map.empty[String, Char] ++ entList.map { case (name, value) => (name, value.toChar) } - val entities = entList. - map { case (name, value) => (name, new ParsedEntityDecl(name, new IntDef(value.toChar.toString))) } + val entities: List[(String, ParsedEntityDecl)] = entList. + map { case (name, value) => (name, ParsedEntityDecl(name, IntDef(value.toChar.toString))) } - def apply() = entities + def apply(): List[(String, ParsedEntityDecl)] = entities } diff --git a/shared/src/main/scala/scala/xml/parsing/XhtmlParser.scala b/shared/src/main/scala/scala/xml/parsing/XhtmlParser.scala new file mode 100644 index 000000000..accc65d6b --- /dev/null +++ b/shared/src/main/scala/scala/xml/parsing/XhtmlParser.scala @@ -0,0 +1,37 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml +package parsing + +import scala.io.Source + +/** + * An XML Parser that preserves `CDATA` blocks and knows about + * [[scala.xml.parsing.XhtmlEntities]]. + * + * @author (c) David Pollak, 2007 WorldWide Conferencing, LLC. + */ +class XhtmlParser(override val input: Source) extends ConstructingHandler with MarkupParser with ExternalSources { + override val preserveWS: Boolean = true + ent ++= XhtmlEntities() +} + +/** + * Convenience method that instantiates, initializes and runs an `XhtmlParser`. + * + * @author Burak Emir + */ +object XhtmlParser { + def apply(source: Source): NodeSeq = new XhtmlParser(source).initialize.document() +} diff --git a/shared/src/main/scala/scala/xml/transform/BasicTransformer.scala b/shared/src/main/scala/scala/xml/transform/BasicTransformer.scala new file mode 100644 index 000000000..144849162 --- /dev/null +++ b/shared/src/main/scala/scala/xml/transform/BasicTransformer.scala @@ -0,0 +1,63 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml +package transform + +import scala.collection.Seq + +/** + * A class for XML transformations. + * + * @author Burak Emir + */ +abstract class BasicTransformer extends (Node => Node) { + protected def unchanged(n: Node, ns: Seq[Node]): Boolean = + ns.length == 1 && (ns.head == n) + + /** + * Call transform(Node) for each node in ns, append results + * to NodeBuffer. + */ + def transform(it: Iterator[Node], nb: NodeBuffer): Seq[Node] = + it.foldLeft(nb)(_ ++= transform(_)) + + /** + * Call transform(Node) to each node in ns, yield ns if nothing changes, + * otherwise a new sequence of concatenated results. + */ + def transform(ns: Seq[Node]): Seq[Node] = { + val changed: Seq[Node] = ns.flatMap(transform) + if (changed.length != ns.length || changed.zip(ns).exists(p => p._1 != p._2)) changed + else ns +} + + def transform(n: Node): Seq[Node] = + if (n.doTransform) n match { + case Group(xs) => Group(transform(xs)) // un-group the hack Group tag + case _ => + val ch = n.child + val nch = transform(ch) + + if (ch.eq(nch)) n + else Elem(n.prefix, n.label, n.attributes, n.scope, nch.isEmpty, nch.toSeq: _*) + } + else n + + override def apply(n: Node): Node = { + val seq: Seq[Node] = transform(n) + if (seq.length > 1) + throw new UnsupportedOperationException("transform must return single node for root") + else seq.head + } +} diff --git a/shared/src/main/scala/scala/xml/transform/NestingTransformer.scala b/shared/src/main/scala/scala/xml/transform/NestingTransformer.scala new file mode 100644 index 000000000..52ee1997c --- /dev/null +++ b/shared/src/main/scala/scala/xml/transform/NestingTransformer.scala @@ -0,0 +1,22 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml +package transform + +import scala.collection.Seq + +class NestingTransformer(rule: RewriteRule) extends BasicTransformer { + override def transform(n: Node): Seq[Node] = + rule.transform(super.transform(n)) +} diff --git a/shared/src/main/scala/scala/xml/transform/RewriteRule.scala b/shared/src/main/scala/scala/xml/transform/RewriteRule.scala new file mode 100644 index 000000000..5ef1ad6c3 --- /dev/null +++ b/shared/src/main/scala/scala/xml/transform/RewriteRule.scala @@ -0,0 +1,30 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml +package transform + +import scala.collection.Seq + +/** + * A RewriteRule, when applied to a term, yields either + * the result of rewriting the term or the term itself if the rule + * is not applied. + * + * @author Burak Emir + */ +abstract class RewriteRule extends BasicTransformer { + override def transform(ns: Seq[Node]): Seq[Node] = super.transform(ns) + override def transform(n: Node): Seq[Node] = n +} + diff --git a/shared/src/main/scala/scala/xml/transform/RuleTransformer.scala b/shared/src/main/scala/scala/xml/transform/RuleTransformer.scala new file mode 100644 index 000000000..60288fb06 --- /dev/null +++ b/shared/src/main/scala/scala/xml/transform/RuleTransformer.scala @@ -0,0 +1,25 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml +package transform + +import scala.collection.Seq + +class RuleTransformer(rules: RewriteRule*) extends BasicTransformer { + private val transformers: Seq[NestingTransformer] = rules.map(new NestingTransformer(_)) + + override def transform(n: Node): Seq[Node] = + if (transformers.isEmpty) n + else transformers.tail.foldLeft(transformers.head.transform(n)) { (res, transformer) => transformer.transform(res) } +} diff --git a/shared/src/test/scala-2.x/scala/xml/ShouldCompile.scala b/shared/src/test/scala-2.x/scala/xml/ShouldCompile.scala new file mode 100644 index 000000000..21f06210f --- /dev/null +++ b/shared/src/test/scala-2.x/scala/xml/ShouldCompile.scala @@ -0,0 +1,10 @@ +package scala.xml + +// these tests depend on xml, so they ended up here, +// though really they are compiler tests + +// t1626 +object o { + val n: Elem =
+ n.namespace == null +} diff --git a/shared/src/test/scala-2.x/scala/xml/TransformersTest.scala b/shared/src/test/scala-2.x/scala/xml/TransformersTest.scala new file mode 100644 index 000000000..9f7e744fc --- /dev/null +++ b/shared/src/test/scala-2.x/scala/xml/TransformersTest.scala @@ -0,0 +1,93 @@ +package scala.xml + +import scala.collection.Seq +import scala.xml.transform._ + +import org.junit.Test +import org.junit.Assert.assertEquals + +class TransformersTest { + + def transformer: RuleTransformer = new RuleTransformer(new RewriteRule { + override def transform(n: Node): NodeSeq = n match { + case { _* } => + case n => n + } + }) + + @Test + def transform(): Unit = // SI-2124 + assertEquals(transformer.transform(

), +

) + + @Test + def transformNamespaced(): Unit = // SI-2125 + assertEquals(transformer.transform(

), + Group(

)) + + @Test + def rewriteRule(): Unit = { // SI-2276 + val inputXml: Node = + + + 1 + + + 1 + + + + object t1 extends RewriteRule { + override def transform(n: Node): Seq[Node] = n match { + case { x } if x.toString.toInt < 4 => { x.toString.toInt + 1 } + case other => other + } + } + + val ruleTransformer: RuleTransformer = new RuleTransformer(t1) + JUnitAssertsForXML.assertEquals(ruleTransformer(inputXml).toString, // TODO: why do we need toString? + + + 2 + + + 2 + + ) + } + + @Test + def preserveReferentialComplexityInLinearComplexity(): Unit = { // SI-4528 + var i: Int = 0 + + val xmlNode: Elem =

Hello Example

+ + new RuleTransformer(new RewriteRule { + override def transform(n: Node): Seq[Node] = { + n match { + case t: Text if t.text.trim.nonEmpty => + i += 1 + Text(s"${t.text}!") + case _ => n + } + } + }).transform(xmlNode) + + assertEquals(1, i) + } + + @Test + def appliesRulesRecursivelyOnPreviousChanges(): Unit = { // #257 + def add(outer: Elem, inner: Node): RewriteRule = new RewriteRule { + override def transform(n: Node): Seq[Node] = n match { + case e: Elem if e.label == outer.label => e.copy(child = e.child ++ inner) + case other => other + } + } + + def transformer: RuleTransformer = new RuleTransformer(add(, ), add(, )) + + assertEquals(, transformer()) + } +} + diff --git a/shared/src/test/scala-2.x/scala/xml/XMLTest2x.scala b/shared/src/test/scala-2.x/scala/xml/XMLTest2x.scala new file mode 100644 index 000000000..7ac8d5f4f --- /dev/null +++ b/shared/src/test/scala-2.x/scala/xml/XMLTest2x.scala @@ -0,0 +1,31 @@ +package scala.xml + +import org.junit.{Test => UnitTest} +import org.junit.Assert.assertTrue +import org.junit.Assert.assertEquals + +class XMLTest2x { + // t-486 + def wsdlTemplate3(serviceName: String): Node = + + + + @UnitTest + def wsdl(): Unit = { + assertEquals(""" + """, wsdlTemplate3("service3").toString) + } + + @UnitTest + def t5154(): Unit = { + + // extra space made the pattern OK + def f: Boolean = {{3}} match { case {{3}} => true } + + // lack of space used to error: illegal start of simple pattern + def g: Boolean = {{3}} match { case {{3}} => true } + + assertTrue(f) + assertTrue(g) + } +} diff --git a/shared/src/test/scala/scala/xml/AttributeTest.scala b/shared/src/test/scala/scala/xml/AttributeTest.scala new file mode 100644 index 000000000..90ea3b0a6 --- /dev/null +++ b/shared/src/test/scala/scala/xml/AttributeTest.scala @@ -0,0 +1,182 @@ +package scala.xml + +import scala.collection.Seq +import org.junit.Test +import org.junit.Assert.assertEquals + +class AttributeTest { + @Test + def unprefixedAttribute(): Unit = { + val x: UnprefixedAttribute = new UnprefixedAttribute("foo","bar", Null) + assertEquals(Some(Text("bar")), x.get("foo")) + assertEquals(Text("bar"), x("foo")) + assertEquals(None, x.get("no_foo")) + assertEquals(null, x("no_foo")) + + val y: MetaData = x.remove("foo") + assertEquals(Null, y) + + val z: UnprefixedAttribute = new UnprefixedAttribute("foo", null:NodeSeq, x) + assertEquals(None, z.get("foo")) + + var appended: MetaData = x.append(x).append(x).append(x) + var len: Int = 0 + while (!appended.isNull) { + appended = appended.next + len = len + 1 + } + assertEquals("removal of duplicates for unprefixed attributes in append", 1L, len.toLong) + } + + @Test + def attributeWithOption(): Unit = { + val x: UnprefixedAttribute = new UnprefixedAttribute("foo", Some(Text("bar")), Null) + + assertEquals(Some(Text("bar")), x.get("foo")) + assertEquals(Text("bar"), x("foo")) + assertEquals(None, x.get("no_foo")) + assertEquals(null, x("no_foo")) + + val attr1: Option[Text] = Some(Text("foo value")) + val attr2: Option[Text] = None + val y: Elem = + assertEquals(Some(Text("foo value")), y.attributes.get("foo")) + assertEquals(Text("foo value"), y.attributes("foo")) + assertEquals(None, y.attributes.get("bar")) + assertEquals(null, y.attributes("bar")) + + val z: UnprefixedAttribute = new UnprefixedAttribute("foo", None, x) + assertEquals(None, z.get("foo")) + } + + @Test + def attributeOrder(): Unit = { + val x: Elem = + assertEquals("""""", x.toString) + } + + def attributeToString(): Unit = { + val expected: String = """""" + assertEquals(expected, .toString) + assertEquals(expected, .toString) + } + + @Test + def attributeOperator(): Unit = { + val xml: Elem = + assertEquals("apple", xml \@ "bar") + } + + @Test + def attributePathRootNoAttribute(): Unit = { + val xml: Elem = + assertEquals(NodeSeq.Empty, xml \ "@bar") + } + + @Test(expected=classOf[IllegalArgumentException]) + def attributePathIllegalEmptyAttribute(): Unit = { + val xml: Elem = + xml \ "@" + } + + @Test + def attributePathRootWithOneAttribute(): Unit = { + val xml: Elem = + assertEquals(Group(Text("apple")), xml \ "@bar") + // assertEquals(NodeSeq.fromSeq(Seq(Text("apple"))), xml \ "@bar") + } + + @Test + def attributePathRootWithMissingAttributes(): Unit = { + val xml: Elem = + assertEquals(NodeSeq.Empty, xml \ "@oops") + } + + @Test + def attributePathDuplicateAttribute(): Unit = { + val xml: Elem = Elem(null, "foo", + Attribute("bar", Text("apple"), + Attribute("bar", Text("orange"), Null)), TopScope, minimizeEmpty = true) + assertEquals(Group(Text("apple")), xml \ "@bar") + } + + @Test + def attributePathDescendantAttributes(): Unit = { + val xml: Elem = + assertEquals(NodeSeq.fromSeq(Seq(Text("1"), Text("2"))), xml \\ "@bar") + } + + @Test + def attributeDescendantPathChildAttributes(): Unit = { + val xml: Elem = + assertEquals(NodeSeq.fromSeq(Seq(Text("1"), Text("2"))), xml \ "b" \\ "@bar") + } + + @Test + def attributeDescendantPathDescendantAttributes(): Unit = { + val xml: Elem = + assertEquals(NodeSeq.fromSeq(Seq(Text("1"), Text("2"))), xml \\ "b" \\ "@bar") + } + + @Test + def attributeChildDescendantPathDescendantAttributes(): Unit = { + val xml: Elem = + assertEquals(NodeSeq.fromSeq(Seq(Text("1"), Text("2"))), xml \ "a" \\ "@bar") + } + + @Test + def attributeDescendantDescendantPathDescendantAttributes(): Unit = { + val xml: Elem = + assertEquals(NodeSeq.fromSeq(Seq(Text("1"), Text("2"))), xml \\ "b" \\ "@bar") + } + + @Test(expected=classOf[IllegalArgumentException]) + def attributePathDescendantIllegalEmptyAttribute(): Unit = { + val xml: Elem = + xml \\ "@" + } + + @Test + def attributePathNoDescendantAttributes(): Unit = { + val xml: Elem = + assertEquals(NodeSeq.Empty, xml \\ "@oops") + } + + @Test + def attributePathOneChildWithAttributes(): Unit = { + val xml: Elem = > + assertEquals(Group(Seq(Text("1"))), xml \ "b" \ "@bar") + } + + @Test + def attributePathTwoChildrenWithAttributes(): Unit = { + val xml: Elem = + val b: NodeSeq = xml \ "b" + assertEquals(2, b.length.toLong) + assertEquals(NodeSeq.fromSeq(Seq(, )), b) + val barFail: NodeSeq = b \ "@bar" + val barList: Seq[NodeSeq] = b.map(_ \ "@bar") + assertEquals(NodeSeq.Empty, barFail) + assertEquals(List(Group(Seq(Text("1"))), Group(Seq(Text("2")))), barList) + } + + @Test(expected=classOf[IllegalArgumentException]) + def invalidAttributeFailForOne(): Unit = { + \ "@" + } + + @Test(expected=classOf[IllegalArgumentException]) + def invalidAttributeFailForMany(): Unit = { + .child \ "@" // implicit seqToNodeSeq + } + + @Test(expected=classOf[IllegalArgumentException]) + def invalidEmptyAttributeFailForOne(): Unit = { + \@ "" + } + + @Test(expected=classOf[IllegalArgumentException]) + def invalidEmptyAttributeFailForMany(): Unit = { + .child \@ "" // implicit seqToNodeSeq + } +} diff --git a/shared/src/test/scala/scala/xml/CommentTest.scala b/shared/src/test/scala/scala/xml/CommentTest.scala new file mode 100644 index 000000000..7ad4fd695 --- /dev/null +++ b/shared/src/test/scala/scala/xml/CommentTest.scala @@ -0,0 +1,29 @@ +package scala.xml + +import org.junit.Assert.assertEquals +import org.junit.Test + +final class CommentTest { + + @Test(expected=classOf[IllegalArgumentException]) + def invalidCommentWithTwoDashes(): Unit = { + Comment("invalid--comment") + } + + @Test(expected=classOf[IllegalArgumentException]) + def invalidCommentWithFinalDash(): Unit = { + Comment("invalid comment-") + } + + @Test + def validCommentWithDash(): Unit = { + val valid: String = "valid-comment" + assertEquals(s"", Comment(valid).toString) + } + + @Test + def validEmptyComment(): Unit = { + val valid: String = "" + assertEquals(s"", Comment(valid).toString) + } +} diff --git a/shared/src/test/scala/scala/xml/JUnitAssertsForXML.scala b/shared/src/test/scala/scala/xml/JUnitAssertsForXML.scala new file mode 100644 index 000000000..4eedd88fe --- /dev/null +++ b/shared/src/test/scala/scala/xml/JUnitAssertsForXML.scala @@ -0,0 +1,7 @@ +package scala.xml + +object JUnitAssertsForXML { + + private[xml] def assertEquals(expected: String, actual: NodeSeq): Unit = + org.junit.Assert.assertEquals(expected, actual.toString) +} diff --git a/shared/src/test/scala/scala/xml/MetaDataTest.scala b/shared/src/test/scala/scala/xml/MetaDataTest.scala new file mode 100644 index 000000000..039382fad --- /dev/null +++ b/shared/src/test/scala/scala/xml/MetaDataTest.scala @@ -0,0 +1,63 @@ +package scala.xml + +import org.junit.Test +import org.junit.Assert.assertEquals + +class MetaDataTest { + + @Test + def absentElementPrefixed1(): Unit = { + // type ascription to help overload resolution pick the right variant + assertEquals(null: Object, Null("za://foo.com", TopScope, "bar")) + assertEquals(null, Null("bar")) + } + + @Test + def absentElementPrefixed2(): Unit = { + assertEquals(Option.empty, Null.get("za://foo.com", TopScope, "bar" )) + assertEquals(Option.empty, Null.get("bar")) + } + + @Test + def presentElement1(): Unit = { + val x: PrefixedAttribute = new PrefixedAttribute("zo","bar", new Atom(42), Null) + val s: NamespaceBinding = NamespaceBinding("zo","za://foo.com", TopScope) + assertEquals(new Atom(42), x("za://foo.com", s, "bar" )) + assertEquals(null, x("bar")) + assertEquals(Some(new Atom(42)), x.get("za://foo.com", s, "bar")) + assertEquals(Option.empty, x.get("bar")) + } + + @Test + def presentElement2(): Unit = { + val s: NamespaceBinding = NamespaceBinding("zo","za://foo.com", TopScope) + val x1: PrefixedAttribute = new PrefixedAttribute("zo","bar", new Atom(42), Null) + val x: UnprefixedAttribute = new UnprefixedAttribute("bar","meaning", x1) + assertEquals(null, x(null, s, "bar")) + assertEquals(Text("meaning"), x("bar")) + assertEquals(None, x.get(null, s, "bar" )) + assertEquals(Some(Text("meaning")), x.get("bar")) + } + + @Test + def attributeExtractor(): Unit = { + def domatch(x: Node): Node = { + x match { + case Node("foo", md @ UnprefixedAttribute(_, value, _), _*) if value.nonEmpty => + md("bar")(0) + case _ => new Atom(3) + } + } + val z: Elem = + val z2: Elem = + assertEquals(Text("gar"), domatch(z)) + assertEquals(new Atom(3), domatch(z2)) + } + + @Test + def reverseTest(): Unit = { + assertEquals("", Null.reverse.toString) + assertEquals(""" b="c"""", .attributes.reverse.toString) + assertEquals(""" d="e" b="c"""", .attributes.reverse.toString) + } +} diff --git a/shared/src/test/scala/scala/xml/NodeBufferTest.scala b/shared/src/test/scala/scala/xml/NodeBufferTest.scala new file mode 100644 index 000000000..5b3e69eb7 --- /dev/null +++ b/shared/src/test/scala/scala/xml/NodeBufferTest.scala @@ -0,0 +1,13 @@ +package scala.xml + +import org.junit.Test +import org.junit.Assert.assertEquals + +class NodeBufferTest { + + @Test + def testToString(): Unit = { + val nodeBuffer: NodeBuffer = new NodeBuffer + assertEquals("NodeBuffer()", nodeBuffer.toString) + } +} diff --git a/shared/src/test/scala/scala/xml/NodeSeqTest.scala b/shared/src/test/scala/scala/xml/NodeSeqTest.scala new file mode 100644 index 000000000..a5808e797 --- /dev/null +++ b/shared/src/test/scala/scala/xml/NodeSeqTest.scala @@ -0,0 +1,105 @@ +package scala.xml + +import org.junit.Test +import org.junit.Assert.assertEquals +import org.junit.Assert.fail + +import scala.collection.immutable + +class NodeSeqTest { + + @Test + def testAppend(): Unit = { // Bug #392. + val a: NodeSeq = Hello + val b: Elem = Hi + a ++ Hi match { + case res: NodeSeq => assertEquals(2, res.size.toLong) + case _: Seq[Node] => fail("Should be NodeSeq was Seq[Node]") // Unreachable code? + } + val res: NodeSeq = a ++ b + val exp: NodeSeq = NodeSeq.fromSeq(Seq(Hello, Hi)) + assertEquals(exp, res) + } + + @Test + def testAppendedAll(): Unit = { // Bug #392. + val a: NodeSeq = Hello + val b: Elem = Hi + a :+ Hi match { + case res: Seq[Node] => assertEquals(2, res.size.toLong) + case _: NodeSeq => fail("Should be Seq[Node] was NodeSeq") // Unreachable code? + } + val res: NodeSeq = a :+ b // implicit seqToNodeSeq + val exp: NodeSeq = NodeSeq.fromSeq(Seq(Hello, Hi)) + assertEquals(exp, res) + } + + @Test + def testPrepended(): Unit = { + val a: NodeSeq = Hello + val b: Elem = Hi + a +: Hi match { + case res: Seq[Node] => assertEquals(2, res.size.toLong) + case _: NodeSeq => fail("Should be Seq[Node] was NodeSeq") // Unreachable code? + } + val res: Seq[NodeSeq] = a +: b + val exp: NodeBuffer = { + HelloHi + } + assertEquals(exp.toSeq, res) + } + + @Test + def testPrependedAll(): Unit = { + val a: NodeSeq = Hello + val b: Elem = Hi + val c: Elem = Hey + a ++: Hi ++: Hey match { + case res: Seq[Node] => assertEquals(3, res.size.toLong) + case _: NodeSeq => fail("Should be Seq[Node] was NodeSeq") // Unreachable code? + } + val res: NodeSeq = a ++: b ++: c // implicit seqToNodeSeq + val exp: NodeSeq = NodeSeq.fromSeq(Seq(Hello, Hi, Hey)) + assertEquals(exp, res) + } + + @Test + def testMap(): Unit = { + val a: NodeSeq = Hello + val exp: NodeSeq = Seq(Hi) // implicit seqToNodeSeq + assertEquals(exp, a.map(_ => Hi)) + assertEquals(exp, for { _ <- a } yield { Hi }) + } + + @Test + def testFlatMap(): Unit = { + val a: NodeSeq = Hello + val exp: NodeSeq = Seq(Hi) // implicit seqToNodeSeq + assertEquals(exp, a.flatMap(_ => Seq(Hi))) + assertEquals(exp, for { b <- a; _ <- b } yield { Hi }) + assertEquals(exp, for { b <- a; c <- b; _ <- c } yield { Hi }) + } + + @Test + def testStringProjection(): Unit = { + val a: Elem = + + b + + + e + e + + c + + + val res: Seq[String] = for { + b <- a \ "b" + c <- b.child + e <- (c \ "e").headOption + } yield { + e.text.trim + } + assertEquals(Seq("e"), res) + } +} diff --git a/shared/src/test/scala/scala/xml/PCDataTest.scala b/shared/src/test/scala/scala/xml/PCDataTest.scala new file mode 100644 index 000000000..3dce2f60c --- /dev/null +++ b/shared/src/test/scala/scala/xml/PCDataTest.scala @@ -0,0 +1,27 @@ +package scala.xml + +import org.junit.Test +import org.junit.Assert.assertEquals + +class PCDataTest { + + def check(pcdata: String, expected: String): Unit = { + val actual: PCData = new PCData(pcdata) + assertEquals(expected, actual.toString) + } + + @Test + def emptyTest(): Unit = check("", "") + + @Test + def bracketTest(): Unit = check("[]", "") + + @Test + def hellaBracketingTest(): Unit = check("[[[[[[[[]]]]]]]]", "") + + @Test + def simpleNestingTest(): Unit = check("]]>", "]]>") + + @Test + def recursiveNestingTest(): Unit = check("", "]]>") +} diff --git a/shared/src/test/scala/scala/xml/PatternMatchingTest.scala b/shared/src/test/scala/scala/xml/PatternMatchingTest.scala new file mode 100644 index 000000000..96890e81d --- /dev/null +++ b/shared/src/test/scala/scala/xml/PatternMatchingTest.scala @@ -0,0 +1,117 @@ +package scala.xml + +import scala.collection.Seq +import org.junit.Test +import org.junit.Assert.assertTrue +import org.junit.Assert.assertEquals + +class PatternMatchingTest { + @Test + def unprefixedAttribute(): Unit = { + val li: List[String] = List("1", "2", "3", "4") + assertTrue(matchSeq(li)) + assertTrue(matchList(li)) + } + + def matchSeq(args: Seq[String]): Boolean = args match { + case Seq(a, b, c, d @ _*) => true + } + + def matchList(args: List[String]): Boolean = + Elem(null, "bla", Null, TopScope, minimizeEmpty = true, args.map { x => Text(x) }: _*) match { + case Elem(_, _, _, _, Text("1"), _*) => true + } + + @Test + def simpleNode(): Unit = + assertTrue( match { + case => true + }) + + @Test + def nameSpaced(): Unit = + assertTrue( match { + case => true + }) + + val cx: Elem = + crazy text world + + + @Test + def nodeContents(): Unit = { + assertTrue(Utility.trim(cx) match { + case n @ crazy text world if (n \ "@foo") xml_== "bar" => true + }) + assertTrue(Utility.trim(cx) match { + case n @ crazy text world if (n \ "@foo") xml_== "bar" => true + }) + assertTrue( match { + case QNode("gaga", "foo", md, child @ _*) => true + }) + + assertTrue( match { + case Node("foo", md, child @ _*) => true + }) + } + + object SafeNodeSeq { + def unapplySeq(any: Any): Option[Seq[Node]] = any match { + case s: Seq[?] => Some(s.flatMap { + case n: Node => n + case _ => NodeSeq.Empty + }) + case _ => None + } + } + + @Test + def nodeSeq(): Unit = { // t0646 + val books: Elem = + + Blabla + Blubabla + Baaaaaaalabla + + + assertTrue(NodeSeq.fromSeq(books.child) match { + case t @ Seq(Blabla) => false + case _ => true + }) + + // SI-1059 + val m: PartialFunction[Any, Any] = { case SafeNodeSeq(s @ _*) => s } + + assertEquals(m( ++ ), List(, )) + assertTrue(m.isDefinedAt( ++ )) + } + + @Test + def SI_4124(): Unit = { + val body: Node = hi + + assertTrue((body: AnyRef, "foo") match { + case (node: Node, "bar") => false + case (ser: Serializable, "foo") => true + case (_, _) => false + }) + + assertTrue((body, "foo") match { + case (node: Node, "bar") => false + case (ser: Serializable, "foo") => true + case (_, _) => false + }) + + assertTrue((body: AnyRef, "foo") match { + case (node: Node, "foo") => true + case (ser: Serializable, "foo") => false + case (_, _) => false + }) + + assertTrue((body: AnyRef, "foo") match { + case (node: Node, "foo") => true + case (ser: Serializable, "foo") => false + case (_, _) => false + }) + } +} diff --git a/shared/src/test/scala/scala/xml/PrintEmptyElementsTest.scala b/shared/src/test/scala/scala/xml/PrintEmptyElementsTest.scala new file mode 100644 index 000000000..e61e195d7 --- /dev/null +++ b/shared/src/test/scala/scala/xml/PrintEmptyElementsTest.scala @@ -0,0 +1,54 @@ +package scala.xml + +import org.junit.Test +import JUnitAssertsForXML.assertEquals + +class PrintEmptyElementsTest { + + @Test + def representEmptyXMLElementsInShortForm(): Unit = { + val expected: String = + """| + | + | + | + | + |is pretty cool + |""".stripMargin + // the xml snippet is not indented because indentation affects pretty printing + // results + val actual: NodeSeq = + + + + + +is pretty cool + + assertEquals(expected, actual) + } + + @Test + def programmaticLong(): Unit = { + assertEquals(" ", + Elem(null, "emptiness", Null, TopScope, minimizeEmpty = false) ++ Text(" ") ++ Comment("programmatic long")) + } + + @Test + def programmaticShort(): Unit = { + assertEquals(" ", + Elem(null, "vide", Null, TopScope, minimizeEmpty = true) ++ Text(" ") ++ Comment("programmatic short")) + } + + @Test + def programmaticShortWithAttribute(): Unit = { + assertEquals(""" """, + Elem(null, "elem", Attribute("attr", Text("value"), Null), TopScope, minimizeEmpty = true) ++ Text(" ") ++ Comment ("programmatic short with attribute")) + } + + @Test + def programmaticLongWithAttribute(): Unit = { + assertEquals(""" """, + Elem(null, "elem2", Attribute("attr2", Text("value2"), Null), TopScope, minimizeEmpty = false) ++ Text(" ") ++ Comment ("programmatic long with attribute")) + } +} diff --git a/shared/src/test/scala/scala/xml/Properties.scala b/shared/src/test/scala/scala/xml/Properties.scala new file mode 100644 index 000000000..b160e3023 --- /dev/null +++ b/shared/src/test/scala/scala/xml/Properties.scala @@ -0,0 +1,19 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package xml + +object Properties extends util.PropertiesTrait { + override protected def propCategory: String = "scala-xml" + override protected def pickJarBasedOn: Class[Node] = classOf[Node] +} diff --git a/shared/src/test/scala/scala/xml/ShouldCompile.scala b/shared/src/test/scala/scala/xml/ShouldCompile.scala new file mode 100644 index 000000000..63dd91fce --- /dev/null +++ b/shared/src/test/scala/scala/xml/ShouldCompile.scala @@ -0,0 +1,89 @@ +package scala.xml + +// these tests depend on xml, so they ended up here, +// though really they are compiler tests + +import scala.collection._ +import scala.collection.mutable.ArrayBuffer + +// t1761 +class Foo { + val elements: Seq[Node] = Nil + val innerTransform: PartialFunction[Elem, String] = { + case Elem(_, l: String, _, _, _@ _*) if elements.exists(_.label == l) => + l + } +} + +// t2281 +class t2281A { + def f(x: Boolean): Seq[Node] = if (x)

else
+} + +class t2281B { + def splitSentences(text: String): ArrayBuffer[String] = { + val outarr: ArrayBuffer[String] = new ArrayBuffer[String] + var outstr: StringBuffer = new StringBuffer + var prevspace: Boolean = false + val ctext: String = text.replaceAll("\n+", "\n") + ctext.foreach { c => + outstr.append(c) + if (c == '.' || c == '!' || c == '?' || c == '\n' || c == ':' || c == ';' || (prevspace && c == '-')) { + outarr += outstr.toString + outstr = new StringBuffer + } + if (c == '\n') { + outarr += "\n\n" + } + prevspace = c == ' ' + } + if (outstr.length > 0) { + outarr += outstr.toString + } + outarr + } + + def spanForSentence(x: String, picktext: String): Seq[Node] = + if (x == "\n\n") { +

+ } else { + { x } + } + + def selectableSentences(text: String, picktext: String): ArrayBuffer[Seq[Node]] = { + val sentences: ArrayBuffer[String] = splitSentences(text) + sentences.map(x => spanForSentence(x, picktext)) + } +} + +// SI-5858 +object SI_5858 { + new Elem(null, null, Null, TopScope, true, Nil: _*) // was ambiguous +} + +class Floozy { + def fooz(x: Node => String): Unit = () + def foo(m: Node): Unit = fooz { + case Elem(_, _, _, _, n, _*) if n == m => "gaga" + } +} + +object guardedMatch { // SI-3705 + // guard caused verifyerror in oldpatmat -- TODO: move this to compiler test suite + def updateNodes(ns: ScalaVersionSpecific.SeqOfNode): ScalaVersionSpecific.SeqOfNode = + for (subnode <- ns) yield subnode match { + case { _ } if true => abc + case Elem(prefix, label, attribs, scope, children @ _*) => + Elem(prefix, label, attribs, scope, minimizeEmpty = true, updateNodes(children): _*) + case other => other + } + updateNodes() +} + +// SI-6897 +object shouldCompile { + val html: Seq[Node] = (null: Any) match { + case 1 => + case 2 =>

+ } +} diff --git a/shared/src/test/scala/scala/xml/UtilityTest.scala b/shared/src/test/scala/scala/xml/UtilityTest.scala new file mode 100644 index 000000000..4553a6f44 --- /dev/null +++ b/shared/src/test/scala/scala/xml/UtilityTest.scala @@ -0,0 +1,239 @@ +package scala.xml + +import scala.collection.Seq +import org.junit.Test +import org.junit.Assert.assertTrue +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotEquals + +class UtilityTest { + + @Test + def isNameStart(): Unit = { + assertTrue(Utility.isNameStart('b')) + assertTrue(Utility.isNameStart(':')) + } + + @Test + def trim(): Unit = { + val x: Elem = + + + val y: Node = Utility.trim(x) + assertTrue(y match { case => true }) + + val x2: Elem = + a b b a + + val y2: Node = Utility.trim(x2) + assertTrue(y2 match { case a b b a => true }) + } + + @Test + def aposEscaping(): Unit = { + val z: Elem = '' + val z1: String = z.toString + assertEquals("''", z1) + } + + @Test + def sort(): Unit = { + assertEquals("", Utility.sort(
.attributes).toString) + assertEquals(""" b="c"""", Utility.sort(.attributes).toString) + val q: Node = Utility.sort() + assertEquals(" a=\"2\" g=\"3\" j=\"2\" oo=\"2\"", Utility.sort(q.attributes).toString) + val pp: PrettyPrinter = new PrettyPrinter(80,5) + assertEquals("", pp.format(q)) + } + + @Test + def issue777(): Unit = { + + + + .hashCode // Bug #777 + } + + @Test + def issue90(): Unit = { + val x: Elem = + assertEquals("", Utility.serialize(x, minimizeTags = MinimizeMode.Always).toString) + } + + @Test + def issue183(): Unit = { + val x: Elem = + assertEquals("", Utility.serialize(x, stripComments = true).toString) + assertEquals("", Utility.serialize(x, stripComments = false).toString) + } + + val printableAscii: Seq[Char] = { + (' ' to '/') ++ // Punctuation + ('0' to '9') ++ // Digits + (':' to '@') ++ // Punctuation (cont.) + ('A' to 'Z') ++ // Uppercase + ('[' to '`') ++ // Punctuation (cont.) + ('a' to 'z') ++ // Lowercase + ('{' to '~') // Punctuation (cont.) + } + + val escapedChars: Seq[Char] = + Utility.Escapes.escMap.keys.toSeq + + @Test + def escapePrintablesTest(): Unit = { + for { + char <- printableAscii.diff(escapedChars) + } yield { + assertEquals(char.toString, Utility.escape(char.toString)) + } + } + + @Test + def escapeEscapablesTest(): Unit = { + for { + char <- escapedChars + } yield { + assertNotEquals(char.toString, Utility.escape(char.toString)) + } + } + + @Test + def escapeAsciiControlCharsTest(): Unit = { + + /* Escapes that Scala (Java) doesn't support. + * \u0007 -> \a (bell) + * \u001B -> \e (escape) + * \u000B -> \v (vertical tab) + * \u007F -> DEL (delete) + */ + val input: String = " \u0007\b\u001B\f\n\r\t\u000B\u007F" + + val expect: String = " \n\r\t\u007F" + + val result: String = Utility.escape(input) + + assertEquals(printfc(expect), printfc(result)) // Pretty, + assertEquals(expect, result) // but verify. + } + + @Test + def escapeUnicodeExtendedControlCodesTest(): Unit = { + for { + char <- '\u0080' to '\u009f' // Extended control codes (C1) + } yield { + assertEquals(char.toString, Utility.escape(char.toString)) + } + } + + @Test + def escapeUnicodeTwoBytesTest(): Unit = { + for { + char <- '\u00A0' to '\u07FF' // Two bytes (cont.) + } yield { + assertEquals(char.toString, Utility.escape(char.toString)) + } + } + + @Test + def escapeUnicodeThreeBytesTest(): Unit = { + for { + char <- '\u0800' to '\uFFFF' // Three bytes + } yield { + assertEquals(char.toString, Utility.escape(char.toString)) + } + } + + /** + * Human-readable character printing + * + * Think of `od -c` of unix od(1) command. + * + * Or think of `printf("%c", i)` in C, but a little better. + */ + def printfc(str: String): String = { + str.map(prettyChar).mkString + } + + /** + * Visual representation of characters that enhances output of + * failed test assertions. + */ + val prettyChar: Map[Char,String] = Map( + '\u0000' -> "\\0", // Null + '\u0001' -> "^A", // Start of header + '\u0002' -> "^B", // Start of text + '\u0003' -> "^C", // End of text + '\u0004' -> "^D", // End of transmission + '\u0005' -> "^E", // Enquiry + '\u0006' -> "^F", // Acknowledgment + '\u0007' -> "\\a", // Bell (^G) + '\b' -> "\\b", // Backspace (^H) + '\t' -> "\\t", // Tab (^I) + '\n' -> "\\n", // Newline (^J) + '\u000B' -> "\\v", // Vertical tab (^K) + '\f' -> "\\f", // Form feed (^L) + '\r' -> "\\r", // Carriage return (^M) + '\u000E' -> "^N", // Shift out + '\u000F' -> "^O", // Shift in + '\u0010' -> "^P", // Data link escape + '\u0011' -> "^Q", // DC1 (XON) + '\u0012' -> "^R", // DC2 + '\u0013' -> "^S", // DC3 (XOFF) + '\u0014' -> "^T", // DC4 + '\u0015' -> "^U", // Negative acknowledgment + '\u0016' -> "^V", // Synchronous idle + '\u0017' -> "^W", // End of transmission block + '\u0018' -> "^X", // Cancel + '\u0019' -> "^Y", // End of medium + '\u001A' -> "^Z", // Substitute + '\u001B' -> "\\e", // Escape + '\u001C' -> "^\\", // File separator + '\u001D' -> "^]", // Group separator + '\u001E' -> "^^", // Record separator + '\u001F' -> "^_", // Unit separator + '\u007F' -> "^?" // Delete + ).withDefault { + (key: Char) => key.toString + } + + def issue73StartsWithAndEndsWithWSInFirst(): Unit = { + val x: Elem =
{Text(" My name is ")}{Text("Harry")}
+ assertEquals(
My name is Harry
, Utility.trim(x)) + } + + @Test + def issue73EndsWithWSInLast(): Unit = { + val x: Elem =
{Text("My name is ")}{Text("Harry ")}
+ assertEquals(
My name is Harry
, Utility.trim(x)) + } + + @Test + def issue73HasWSInMiddle(): Unit = { + val x: Elem =
{Text("My name is")}{Text(" ")}{Text("Harry")}
+ assertEquals(
My name is Harry
, Utility.trim(x)) + } + + @Test + def issue73HasWSEverywhere(): Unit = { + val x: Elem =
{Text(" My name ")}{Text(" is ")}{Text(" Harry ")}
+ assertEquals(
My name is Harry
, Utility.trim(x)) + } + + @Test + def issue306InvalidUnclosedCharRef(): Unit = { + val entity = "&# test " + val it: Iterator[Char] = entity.iterator + var c = it.next() + val next = () => if (it.hasNext) c = it.next() else c = 0.asInstanceOf[Char] + val result = Utility.parseCharRef({ () => c }, next, _ => {}, _ => {}) + assertEquals("", result) + } + + @Test + def toStringStackSafe(): Unit = { + val xml = (1 to 5000).foldRight() { case (_, n) => {n}} + xml.toString + } + +} diff --git a/shared/src/test/scala/scala/xml/XMLEmbeddingTest.scala b/shared/src/test/scala/scala/xml/XMLEmbeddingTest.scala new file mode 100644 index 000000000..746513ee1 --- /dev/null +++ b/shared/src/test/scala/scala/xml/XMLEmbeddingTest.scala @@ -0,0 +1,17 @@ +package scala.xml + +import org.junit.Test +import org.junit.Assert.assertEquals + +class XMLEmbeddingTest { + + @Test + def basic(): Unit = { + val ya: Elem = {{ + assertEquals("{", ya.text) + val ua: Elem = }} + assertEquals("}", ua.text) + val za: Elem = {{}}{{}}{{}} + assertEquals("{}{}{}", za.text) + } +} diff --git a/shared/src/test/scala/scala/xml/XMLSyntaxTest.scala b/shared/src/test/scala/scala/xml/XMLSyntaxTest.scala new file mode 100644 index 000000000..83e6829bf --- /dev/null +++ b/shared/src/test/scala/scala/xml/XMLSyntaxTest.scala @@ -0,0 +1,65 @@ +package scala.xml + +import scala.collection.Seq +import org.junit.Test +import org.junit.Assert.assertTrue +import org.junit.Assert.assertFalse +import org.junit.Assert.assertEquals + +class XMLSyntaxTest { + + private def handle[A](x: Node): A = { + x.child(0).asInstanceOf[Atom[A]].data + } + + @Test + def test1(): Unit = { + val xNull: Elem = {null} // these used to be Atom(unit), changed to empty children + assertTrue(xNull.child.sameElements(Nil)) + + val x0: Elem = {} // these used to be Atom(unit), changed to empty children + val x00: Elem = { } // dto. + val xa: Elem = { "world" } + + assertTrue(x0.child.sameElements(Nil)) + assertTrue(x00.child.sameElements(Nil)) + assertEquals("world", handle[String](xa)) + + val xb: Elem = { 1.5 } + assertEquals(1.5, handle[Double](xb), 0.0) + + val xc: Elem = { 5 } + assertEquals(5, handle[Int](xc).toLong) + + val xd: Elem = { true } + assertEquals(true, handle[Boolean](xd)) + + val xe: Elem = { 5:Short } + assertEquals((5:Short).toLong, handle[Short](xe).toLong) + + val xf: Elem = { val x = 27; x } + assertEquals(27, handle[Int](xf).toLong) + + val xg: Elem = { List(1,2,3,4) } + assertEquals("1 2 3 4", xg.toString) + assertFalse(xg.child.map(_.isInstanceOf[Text]).exists(identity)) + + val xh: Elem = { for(x <- List(1,2,3,4) if x % 2 == 0) yield x } + assertEquals("2 4", xh.toString) + assertFalse(xh.child.map(_.isInstanceOf[Text]).exists(identity)) + } + + /** see SVN r13821 (emir): support for , + * so that Options can be used for optional attributes. + */ + @Test + def test2(): Unit = { + val x1: Option[Seq[Node]] = Some(hello) + val n1: Elem = + assertEquals(x1, n1.attribute("key")) + + val x2: Option[Seq[Node]] = None + val n2: Elem = + assertEquals(x2, n2.attribute("key")) + } +} diff --git a/shared/src/test/scala/scala/xml/XMLTest.scala b/shared/src/test/scala/scala/xml/XMLTest.scala new file mode 100644 index 000000000..746d58d68 --- /dev/null +++ b/shared/src/test/scala/scala/xml/XMLTest.scala @@ -0,0 +1,531 @@ +package scala.xml + +import org.junit.{Test => UnitTest} +import org.junit.Assert.assertTrue +import org.junit.Assert.assertFalse +import org.junit.Assert.assertEquals +import java.io.StringWriter +import scala.collection.Iterable +import scala.collection.Seq +import scala.xml.dtd.{DocType, PublicID} +import scala.xml.Utility.sort + +object XMLTest { + val e: MetaData = Null //Node.NoAttributes + val sc: NamespaceBinding = TopScope +} + +class XMLTest { + @UnitTest + def nodeSeq(): Unit = { + val p: Elem = + + + + + + val pelems_1: NodeSeq = for (x <- p \ "bar"; y <- p \ "baz") yield { + val value = x.attributes("value") + val bazValue = y.attributes("bazValue") + Text(s"$value$bazValue!") + } + + val pelems_2: NodeSeq = NodeSeq.fromSeq(List(Text("38!"), Text("58!"))) + assertTrue(pelems_1.sameElements(pelems_2)) + assertTrue(Text("8").sameElements(p \\ "@bazValue")) + } + + @UnitTest + def queryBooks(): Unit = { + val books: Elem = + + Blabla + Blubabla + Baaaaaaalabla + + + val reviews: Elem = + + + Blabla + + Hallo Welt. + + + + Blubabla + + Hello Blu + + + + Blubabla + + rem 2 + + + + + val results1: String = new PrettyPrinter(80, 5).formatNodes( + for { + t <- books \\ "title" + r <- reviews \\ "entry" if (r \ "title") xml_== t + } yield + { t } + { r \ "remarks" } + ) + val results1Expected: String = """ + | Blabla + | Hallo Welt. + | + | Blubabla + | Hello Blu + | + | Blubabla + | rem 2 + |""".stripMargin + assertEquals(results1Expected, results1) + + { + val actual: List[Node] = for (case t @ Blabla <- NodeSeq.fromSeq(books.child).toList) + yield t + val expected: List[Elem] = List(Blabla) + assertEquals(expected, actual) + } + } + + @UnitTest + def queryPhoneBook(): Unit = { + val phoneBook: Elem = + + + This is thephonebook + of the +
ACME + corporation. + + + John + +41 21 693 68 67 + +41 79 602 23 23 + + + + val addrBook: Elem = + + + This is theaddressbook + of the + ACME + corporation. + + + John + Elm Street + Dolphin City + + + + val actual: String = new PrettyPrinter(80, 5).formatNodes( + for { + t <- addrBook \\ "entry" + r <- phoneBook \\ "entry" if (t \ "name") xml_== (r \ "name") + } yield + { t.child } + { r \ "phone" } + ) + val expected: String = + """| + | John + | Elm Street + | Dolphin City + | +41 21 693 68 67 + | +41 79 602 23 23 + |""".stripMargin + assertEquals(expected, actual) + } + + @UnitTest(expected=classOf[IllegalArgumentException]) + def failEmptyStringChildren(): Unit = { + \ "" + } + + @UnitTest(expected=classOf[IllegalArgumentException]) + def failEmptyStringDescendants(): Unit = { + \\ "" + } + + @UnitTest + def namespaces(): Unit = { + val cuckoo: Elem = + + + + assertEquals("http://cuckoo.com", cuckoo.namespace) + for (n <- cuckoo \ "_") { + assertEquals("http://cuckoo.com", n.namespace) + } + } + + @UnitTest + def namespacesWithNestedXmls(): Unit = { + val foo: Elem = + val bar: Elem = {foo} + val expected: String = """""" + val actual: String = bar.toString + assertEquals(expected, actual) + } + + def Elem(prefix: String, label: String, attributes: MetaData, scope: NamespaceBinding, child: Node*): Elem = + scala.xml.Elem.apply(prefix, label, attributes, scope, minimizeEmpty = true, child: _*) + + @UnitTest + def groupNode(): Unit = { + val zx1: Node = Group { } + val zy1: Node = { zx1 } + assertEquals("", zy1.toString) + + assertEquals("", + Group { List(, zy1, zx1) }.toString) + + val zz1: Group = + + assertTrue(zx1 xml_== zz1) + assertTrue(zz1.length == 3) + } + + @UnitTest + def dodgyNamespace(): Unit = { + val x: Elem = + assertTrue(x.toString.matches(".*xmlns:dog=\"http://dog.com\".*")) + } + + val ax: Elem = + + + + val cx: Elem = + crazy text world + + + val bx: Elem = + + @UnitTest + def XmlEx(): Unit = { + assertTrue((ax \ "@foo") xml_== "bar") // uses NodeSeq.view! + assertTrue((ax \ "@foo") xml_== Text("bar")) // dto. + assertTrue((bx \ "@foo") xml_== "bar&x") // dto. + assertTrue((bx \ "@foo") xml_sameElements List(Text("bar&x"))) + assertTrue("" == bx.toString) + } + + @UnitTest + def XmlEy(): Unit = { + assertTrue((ax \ "@{the namespace from outer space}foo") xml_== "baz") + assertTrue((cx \ "@{the namespace from outer space}foo") xml_== "baz") + + try { + ax \ "@" + assertTrue(false) + } catch { + case _: IllegalArgumentException => + } + try { + ax \ "@{" + assertTrue(false) + } catch { + case _: IllegalArgumentException => + } + try { + ax \ "@{}" + assertTrue(false) + } catch { + case _: IllegalArgumentException => + } + + } + + @UnitTest + def comment(): Unit = + assertEquals("", .toString) + + @UnitTest + def weirdElem(): Unit = + assertEquals("", .toString) + + @UnitTest + def escape(): Unit = + assertEquals(""" + "Come, come again, whoever you are, come! +Heathen, fire worshipper or idolatrous, come! +Come even if you broke your penitence a hundred times, +Ours is the portal of hope, come as you are." + Mevlana Celaleddin Rumi""", .toString) // this guy will escaped, and rightly so + + @UnitTest + def unparsed2(): Unit = { + object myBreak extends Unparsed("
") + assertEquals("
", { myBreak }.toString) // shows use of unparsed + } + + @UnitTest + def justDontFail(): Unit = { + match { + case QNode("gaga", "foo", md, child @ _*) => + } + + match { + case Node("foo", md, child @ _*) => + } + } + + def f(s: String): Elem = { + + { + for (item <- s split ',') yield { item } + } + + } + + @UnitTest + def nodeBuffer(): Unit = + assertEquals( + """ + abc + """, f("a,b,c").toString) + + // t-486 + def wsdlTemplate1(serviceName: String): Node = + + + + def wsdlTemplate2(serviceName: String, targetNamespace: String): Node = + + + + def wsdlTemplate4(serviceName: String, targetNamespace: () => String): Node = + + + + @UnitTest + def wsdl(): Unit = { + assertEquals(""" + """, wsdlTemplate1("service1").toString) + assertEquals(""" + """, wsdlTemplate2("service2", "target2").toString) + assertEquals(""" + """, wsdlTemplate4("service4", () => "target4").toString) + } + + @UnitTest + def t547(): Unit = { + // ambiguous toString problem from #547 + val atom: Atom[Unit] = new Atom(()) + assertEquals(().toString, atom.toString) + } + + @UnitTest + def t1079(): Unit = assertFalse( == ) + + @UnitTest + def t1620(): Unit = { + val dt: DocType = DocType("foo", PublicID("-//Foo Corp//DTD 1.0//EN", "foo.dtd"), Seq()) + var pw: StringWriter = new StringWriter() + XML.write(pw, , "utf-8", xmlDecl = true, dt) + pw.flush() + assertEquals(""" + +""", pw.toString) + + pw = new StringWriter() + val dt2: DocType = DocType("foo", PublicID("-//Foo Corp//DTD 1.0//EN", null), Seq()) + XML.write(pw, , "utf-8", xmlDecl = true, dt2) + pw.flush() + assertEquals(""" + +""", pw.toString) + } + + @UnitTest + def t1773(): Unit = { + val xs: List[Elem] = List( +
, + , + { NodeSeq.Empty }, + { "" }, + { if (true) "" else "I like turtles" }) + + for (x1 <- xs; x2 <- xs) assertTrue(x1 xml_== x2) + } + + @UnitTest + def t3886(): Unit = { + assertTrue( == ) + assertTrue( != ) + assertTrue( != ) + + assertTrue( != ) + assertTrue( != ) + assertTrue( != ) + } + + @UnitTest + def t4124(): Unit = { + val body: Node = hi + assertEquals("hi", ((body: AnyRef, "foo"): @unchecked) match { + case (node: Node, "bar") => "bye" + case (ser: Serializable, "foo") => "hi" + }) + + assertEquals("hi", ((body, "foo"): @unchecked) match { + case (node: Node, "bar") => "bye" + case (ser: Serializable, "foo") => "hi" + }) + + assertEquals("bye", ((body: AnyRef, "foo"): @unchecked) match { + case (node: Node, "foo") => "bye" + case (ser: Serializable, "foo") => "hi" + }) + + assertEquals("bye", ((body: AnyRef, "foo"): @unchecked) match { + case (node: Node, "foo") => "bye" + case (ser: Serializable, "foo") => "hi" + }) + } + + @UnitTest + def t5052(): Unit = { + assertTrue( xml_== ) + assertTrue( xml_== ) + assertTrue( xml_== ) + assertTrue( xml_== ) + } + + @UnitTest + def t5115(): Unit = { + def assertHonorsIterableContract(i: Iterable[?]): Unit = assertEquals(i.size.toLong, i.iterator.size.toLong) + + assertHonorsIterableContract(.attributes) + assertHonorsIterableContract(.attributes) + assertHonorsIterableContract(.attributes) + assertHonorsIterableContract(.attributes) + assertHonorsIterableContract(.attributes) + assertHonorsIterableContract(.attributes) + assertHonorsIterableContract(.attributes) + assertHonorsIterableContract(.attributes) + } + + @UnitTest + def t5843(): Unit = { + val foo: Attribute = Attribute(null, "foo", "1", Null) + val bar: Attribute = Attribute(null, "bar", "2", foo) + val ns: NamespaceBinding = NamespaceBinding(null, "uri", TopScope) + + assertEquals(""" foo="1"""", foo.toString) + assertEquals(null, TopScope.getURI(foo.pre)) + assertEquals(""" bar="2"""", bar.remove("foo").toString) + assertEquals(""" foo="1"""", bar.remove("bar").toString) + assertEquals(""" bar="2"""", bar.remove(null, TopScope, "foo").toString) + assertEquals(""" foo="1"""", bar.remove(null, TopScope, "bar").toString) + assertEquals(""" bar="2" foo="1"""", bar.toString) + assertEquals(""" bar="2" foo="1"""", bar.remove(null, ns, "foo").toString) + assertEquals(""" bar="2" foo="1"""", bar.remove(null, ns, "bar").toString) + } + + @UnitTest + def t7074(): Unit = { + assertEquals("""""", sort().toString) + assertEquals("""""", sort().toString) + assertEquals("""""", sort().toString) + assertEquals("""""", sort().toString) + assertEquals("""""", sort().toString) + assertEquals("""""", sort().toString) + assertEquals("""""", sort().toString) + assertEquals("""""", sort().toString) + assertEquals("""""", sort().toString) + } + + @UnitTest + def attributes(): Unit = { + val noAttr: Elem = + val attrNull: Elem = + val attrNone: Elem = + val preAttrNull: Elem = + val preAttrNone: Elem = + assertEquals(noAttr, attrNull) + assertEquals(noAttr, attrNone) + assertEquals(noAttr, preAttrNull) + assertEquals(noAttr, preAttrNone) + + val xml1: Elem = + val xml2: Elem = + val xml3: Elem = + assertEquals(xml1, xml2) + assertEquals(xml1, xml3) + + assertEquals("""""", noAttr.toString) + assertEquals("""""", attrNull.toString) + assertEquals("""""", attrNone.toString) + assertEquals("""""", preAttrNull.toString) + assertEquals("""""", preAttrNone.toString) + assertEquals("""""", xml1.toString) + assertEquals("""""", xml2.toString) + assertEquals("""""", xml3.toString) + + // Check if attribute order is retained + assertEquals("""""", .toString) + assertEquals("""""", .toString) + assertEquals("""""", .toString) + assertEquals("""""", .toString) + } + + @UnitTest + def issue28(): Unit = { + val x: Elem = + // val ns = new NamespaceBinding("x", "gaga", sc) + // val x = Elem("x", "foo", e, ns) + val pp: PrettyPrinter = new PrettyPrinter(80, 2) + // This assertion passed + assertEquals("""""", x.toString) + // This was the bug, producing an errant xmlns attribute + assertEquals("""""", pp.format(x)) + } + + @UnitTest + def nodeSeqNs(): Unit = { + val x: NodeBuffer = { + + } + val pp: PrettyPrinter = new PrettyPrinter(80, 2) + val expected: String = """""" + assertEquals(expected, pp.formatNodes(x)) + } + + @UnitTest + def nodeStringBuilder(): Unit = { + val x: Elem = { + + } + val pp: PrettyPrinter = new PrettyPrinter(80, 2) + val expected: String = """""" + val sb: StringBuilder = new StringBuilder + pp.format(x, sb) + assertEquals(expected, sb.toString) + } + + @UnitTest + def i1976(): Unit = { + val node: Elem = { "whatever " } + assertEquals("whatever ", node.child.text) // implicit seqToNodeSeq + } + + @UnitTest + def i6547(): Unit = { + + } +} diff --git a/shared/src/test/scala/scala/xml/dtd/DeclTest.scala b/shared/src/test/scala/scala/xml/dtd/DeclTest.scala new file mode 100644 index 000000000..5b01fb2fc --- /dev/null +++ b/shared/src/test/scala/scala/xml/dtd/DeclTest.scala @@ -0,0 +1,42 @@ +package scala.xml +package dtd + +import org.junit.Test +import org.junit.Assert.assertEquals + +class DeclTest { + + @Test + def elemDeclToString(): Unit = { + assertEquals( + "", + ElemDecl("x", PCDATA).toString + ) + } + + @Test + def attListDeclToString(): Unit = { + + val expected: String = + """|""".stripMargin + + val actual: String = AttListDecl("x", + List( + AttrDecl("y", "CDATA", REQUIRED), + AttrDecl("z", "CDATA", REQUIRED) + ) + ).toString + + assertEquals(expected, actual) + } + + @Test + def parsedEntityDeclToString(): Unit = { + assertEquals( + """""", + ParsedEntityDecl("foo", ExtDef(SystemID("bar"))).toString + ) + } +} diff --git a/shared/src/test/scala/scala/xml/parsing/PiParsingTest.scala b/shared/src/test/scala/scala/xml/parsing/PiParsingTest.scala new file mode 100644 index 000000000..6bbff668a --- /dev/null +++ b/shared/src/test/scala/scala/xml/parsing/PiParsingTest.scala @@ -0,0 +1,19 @@ +package scala.xml.parsing + +import org.junit.Test +import scala.xml.JUnitAssertsForXML.assertEquals + +class PiParsingTest { + + @Test + def piNoWSLiteral(): Unit = { + val expected: String = "ab" + assertEquals(expected, ab) + } + + @Test + def piLiteral(): Unit = { + val expected: String = " a b " + assertEquals(expected, a b ) + } +} diff --git a/shared/src/test/scala/scala/xml/parsing/Ticket0632Test.scala b/shared/src/test/scala/scala/xml/parsing/Ticket0632Test.scala new file mode 100644 index 000000000..b35bc4d36 --- /dev/null +++ b/shared/src/test/scala/scala/xml/parsing/Ticket0632Test.scala @@ -0,0 +1,28 @@ +package scala.xml.parsing + +import org.junit.Test +import scala.xml.JUnitAssertsForXML.assertEquals + +class Ticket0632Test { + + @Test + def singleAmp(): Unit = { + val expected: String = "" + assertEquals(expected, ) + assertEquals(expected, ) + } + + @Test + def oneAndHalfAmp(): Unit = { + val expected: String = "" + assertEquals(expected, ) + assertEquals(expected, ) + } + + @Test + def doubleAmp(): Unit = { + val expected: String = "" + assertEquals(expected, ) + assertEquals(expected, ) + } +} diff --git a/src/main/scala/scala/xml/Atom.scala b/src/main/scala/scala/xml/Atom.scala deleted file mode 100644 index 84f59bc50..000000000 --- a/src/main/scala/scala/xml/Atom.scala +++ /dev/null @@ -1,49 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml - -/** - * The class `Atom` provides an XML node for text (`PCDATA`). - * It is used in both non-bound and bound XML representations. - * - * @author Burak Emir - * @param data the text contained in this node, may not be `'''null'''`. - */ -class Atom[+A](val data: A) extends SpecialNode with Serializable { - if (data == null) - throw new IllegalArgumentException("cannot construct " + getClass.getSimpleName + " with null") - - override protected def basisForHashCode: Seq[Any] = Seq(data) - - override def strict_==(other: Equality) = other match { - case x: Atom[_] => data == x.data - case _ => false - } - - override def canEqual(other: Any) = other match { - case _: Atom[_] => true - case _ => false - } - - final override def doCollectNamespaces = false - final override def doTransform = false - - def label = "#PCDATA" - - /** - * Returns text, with some characters escaped according to the XML - * specification. - */ - def buildString(sb: StringBuilder): StringBuilder = - Utility.escape(data.toString, sb) - - override def text: String = data.toString - -} diff --git a/src/main/scala/scala/xml/Comment.scala b/src/main/scala/scala/xml/Comment.scala deleted file mode 100644 index 23d4a9d4a..000000000 --- a/src/main/scala/scala/xml/Comment.scala +++ /dev/null @@ -1,33 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml - -/** - * The class `Comment` implements an XML node for comments. - * - * @author Burak Emir - * @param commentText the text contained in this node, may not contain "--" - */ -case class Comment(commentText: String) extends SpecialNode { - - def label = "#REM" - override def text = "" - final override def doCollectNamespaces = false - final override def doTransform = false - - if (commentText contains "--") - throw new IllegalArgumentException("text contains \"--\"") - - /** - * Appends "" to this string buffer. - */ - override def buildString(sb: StringBuilder) = - sb append "" -} diff --git a/src/main/scala/scala/xml/Elem.scala b/src/main/scala/scala/xml/Elem.scala deleted file mode 100755 index 933f0d664..000000000 --- a/src/main/scala/scala/xml/Elem.scala +++ /dev/null @@ -1,142 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml - -/** - * This singleton object contains the `apply` and `unapplySeq` methods for - * convenient construction and deconstruction. It is possible to deconstruct - * any `Node` instance (that is not a `SpecialNode` or a `Group`) using the - * syntax `case Elem(prefix, label, attribs, scope, child @ _*) => ...` - * - * Copyright 2008 Google Inc. All Rights Reserved. - * @author Burak Emir - */ -object Elem { - /** - * Build an Elem, setting its minimizeEmpty property to `true` if it has no children. Note that this - * default may not be exactly what you want, as some XML dialects don't permit some elements to be minimized. - * - * @deprecated This factory method is retained for backward compatibility; please use the other one, with which you - * can specify your own preference for minimizeEmpty. - */ - @deprecated("Use the other apply method in this object", "2.10.0") - def apply(prefix: String, label: String, attributes: MetaData, scope: NamespaceBinding, child: Node*): Elem = - apply(prefix, label, attributes, scope, child.isEmpty, child: _*) - - def apply(prefix: String, label: String, attributes: MetaData, scope: NamespaceBinding, minimizeEmpty: Boolean, child: Node*): Elem = - new Elem(prefix, label, attributes, scope, minimizeEmpty, child: _*) - - def unapplySeq(n: Node) = n match { - case _: SpecialNode | _: Group => None - case _ => Some((n.prefix, n.label, n.attributes, n.scope, n.child)) - } - - import scala.sys.process._ - import scala.language.implicitConversions - - /** - * Implicitly convert a [[scala.xml.Elem]] into a - * [[scala.sys.process.ProcessBuilder]]. This is done by obtaining the text - * elements of the element, trimming spaces, and then converting the result - * from string to a process. Importantly, tags are completely ignored, so - * they cannot be used to separate parameters. - */ - @deprecated("To create a scala.sys.process.Process from an xml.Elem, please use Process(elem.text.trim).", "2.11.0") - implicit def xmlToProcess(command: scala.xml.Elem): ProcessBuilder = Process(command.text.trim) - - @deprecated("To create a scala.sys.process.Process from an xml.Elem, please use Process(elem.text.trim).", "2.11.0") - implicit def processXml(p: Process.type) = new { - /** - * Creates a [[scala.sys.process.ProcessBuilder]] from a Scala XML Element. - * This can be used as a way to template strings. - * - * @example {{{ - * apply( {dxPath.absolutePath} --dex --output={classesDexPath.absolutePath} {classesMinJarPath.absolutePath}) - * }}} - */ - def apply(command: Elem): ProcessBuilder = Process(command.text.trim) - } -} - -/** - * The case class `Elem` extends the `Node` class, - * providing an immutable data object representing an XML element. - * - * @param prefix namespace prefix (may be null, but not the empty string) - * @param label the element name - * @param attributes1 the attribute map - * @param scope the scope containing the namespace bindings - * @param minimizeEmpty `true` if this element should be serialized as minimized (i.e. "<el/>") when - * empty; `false` if it should be written out in long form. - * @param child the children of this node - * - * Copyright 2008 Google Inc. All Rights Reserved. - * @author Burak Emir - */ -class Elem( - override val prefix: String, - val label: String, - attributes1: MetaData, - override val scope: NamespaceBinding, - val minimizeEmpty: Boolean, - val child: Node*) - extends Node with Serializable { - @deprecated("This constructor is retained for backward compatibility. Please use the primary constructor, which lets you specify your own preference for `minimizeEmpty`.", "2.10.0") - def this(prefix: String, label: String, attributes: MetaData, scope: NamespaceBinding, child: Node*) = { - this(prefix, label, attributes, scope, child.isEmpty, child: _*) - } - - final override def doCollectNamespaces = true - final override def doTransform = true - - override val attributes = MetaData.normalize(attributes1, scope) - - if (prefix == "") - throw new IllegalArgumentException("prefix of zero length, use null instead") - - if (scope == null) - throw new IllegalArgumentException("scope is null, use scala.xml.TopScope for empty scope") - - //@todo: copy the children, - // setting namespace scope if necessary - // cleaning adjacent text nodes if necessary - - override protected def basisForHashCode: Seq[Any] = - prefix :: label :: attributes :: child.toList - - /** - * Returns a new element with updated attributes, resolving namespace uris - * from this element's scope. See MetaData.update for details. - * - * @param updates MetaData with new and updated attributes - * @return a new symbol with updated attributes - */ - final def %(updates: MetaData): Elem = - copy(attributes = MetaData.update(attributes, scope, updates)) - - /** - * Returns a copy of this element with any supplied arguments replacing - * this element's value for that field. - * - * @return a new symbol with updated attributes - */ - def copy( - prefix: String = this.prefix, - label: String = this.label, - attributes: MetaData = this.attributes, - scope: NamespaceBinding = this.scope, - minimizeEmpty: Boolean = this.minimizeEmpty, - child: Seq[Node] = this.child.toSeq): Elem = Elem(prefix, label, attributes, scope, minimizeEmpty, child: _*) - - /** - * Returns concatenation of `text(n)` for each child `n`. - */ - override def text = (child map (_.text)).mkString -} diff --git a/src/main/scala/scala/xml/EntityRef.scala b/src/main/scala/scala/xml/EntityRef.scala deleted file mode 100644 index f19a4a00f..000000000 --- a/src/main/scala/scala/xml/EntityRef.scala +++ /dev/null @@ -1,42 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml - -/** - * The class `EntityRef` implements an XML node for entity references. - * - * @author Burak Emir - * @version 1.0 - * @param entityName the name of the entity reference, for example `amp`. - */ -case class EntityRef(entityName: String) extends SpecialNode { - final override def doCollectNamespaces = false - final override def doTransform = false - def label = "#ENTITY" - - override def text = entityName match { - case "lt" => "<" - case "gt" => ">" - case "amp" => "&" - case "apos" => "'" - case "quot" => "\"" - case _ => Utility.sbToString(buildString) - } - - /** - * Appends `"& entityName;"` to this string buffer. - * - * @param sb the string buffer. - * @return the modified string buffer `sb`. - */ - override def buildString(sb: StringBuilder) = - sb.append("&").append(entityName).append(";") - -} diff --git a/src/main/scala/scala/xml/Group.scala b/src/main/scala/scala/xml/Group.scala deleted file mode 100644 index f2248ff7d..000000000 --- a/src/main/scala/scala/xml/Group.scala +++ /dev/null @@ -1,44 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml - -/** - * A hack to group XML nodes in one node for output. - * - * @author Burak Emir - * @version 1.0 - */ -final case class Group(nodes: Seq[Node]) extends Node { - override def theSeq = nodes - - override def canEqual(other: Any) = other match { - case x: Group => true - case _ => false - } - - override def strict_==(other: Equality) = other match { - case Group(xs) => nodes sameElements xs - case _ => false - } - - override protected def basisForHashCode = nodes - - /** - * Since Group is very much a hack it throws an exception if you - * try to do anything with it. - */ - private def fail(msg: String) = throw new UnsupportedOperationException("class Group does not support method '%s'" format msg) - - def label = fail("label") - override def attributes = fail("attributes") - override def namespace = fail("namespace") - override def child = fail("child") - def buildString(sb: StringBuilder) = fail("toString(StringBuilder)") -} diff --git a/src/main/scala/scala/xml/MalformedAttributeException.scala b/src/main/scala/scala/xml/MalformedAttributeException.scala deleted file mode 100644 index d264831a5..000000000 --- a/src/main/scala/scala/xml/MalformedAttributeException.scala +++ /dev/null @@ -1,12 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml - -case class MalformedAttributeException(msg: String) extends RuntimeException(msg) diff --git a/src/main/scala/scala/xml/NamespaceBinding.scala b/src/main/scala/scala/xml/NamespaceBinding.scala deleted file mode 100644 index 8409c41e1..000000000 --- a/src/main/scala/scala/xml/NamespaceBinding.scala +++ /dev/null @@ -1,84 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml - -import Utility.sbToString - -/** - * The class `NamespaceBinding` represents namespace bindings - * and scopes. The binding for the default namespace is treated as a null - * prefix. the absent namespace is represented with the null uri. Neither - * prefix nor uri may be empty, which is not checked. - * - * @author Burak Emir - * @version 1.0 - */ -@SerialVersionUID(0 - 2518644165573446725L) -case class NamespaceBinding(prefix: String, uri: String, parent: NamespaceBinding) extends AnyRef with Equality { - if (prefix == "") - throw new IllegalArgumentException("zero length prefix not allowed") - - def getURI(_prefix: String): String = - if (prefix == _prefix) uri else parent getURI _prefix - - /** - * Returns some prefix that is mapped to the URI. - * - * @param _uri the input URI - * @return the prefix that is mapped to the input URI, or null - * if no prefix is mapped to the URI. - */ - def getPrefix(_uri: String): String = - if (_uri == uri) prefix else parent getPrefix _uri - - override def toString(): String = sbToString(buildString(_, TopScope)) - - private def shadowRedefined(stop: NamespaceBinding): NamespaceBinding = { - def prefixList(x: NamespaceBinding): List[String] = - if ((x == null) || (x eq stop)) Nil - else x.prefix :: prefixList(x.parent) - def fromPrefixList(l: List[String]): NamespaceBinding = l match { - case Nil => stop - case x :: xs => new NamespaceBinding(x, this.getURI(x), fromPrefixList(xs)) - } - val ps0 = prefixList(this).reverse - val ps = ps0.distinct - if (ps.size == ps0.size) this - else fromPrefixList(ps) - } - - override def canEqual(other: Any) = other match { - case _: NamespaceBinding => true - case _ => false - } - - override def strict_==(other: Equality) = other match { - case x: NamespaceBinding => (prefix == x.prefix) && (uri == x.uri) && (parent == x.parent) - case _ => false - } - - def basisForHashCode: Seq[Any] = List(prefix, uri, parent) - - def buildString(stop: NamespaceBinding): String = sbToString(buildString(_, stop)) - - def buildString(sb: StringBuilder, stop: NamespaceBinding) { - shadowRedefined(stop).doBuildString(sb, stop) - } - - private def doBuildString(sb: StringBuilder, stop: NamespaceBinding) { - if ((this == null) || (this eq stop)) return // contains? - - val s = " xmlns%s=\"%s\"".format( - (if (prefix != null) ":" + prefix else ""), - (if (uri != null) uri else "") - ) - parent.doBuildString(sb append s, stop) // copy(ignore) - } -} diff --git a/src/main/scala/scala/xml/NodeSeq.scala b/src/main/scala/scala/xml/NodeSeq.scala deleted file mode 100644 index 5f2b6b6c9..000000000 --- a/src/main/scala/scala/xml/NodeSeq.scala +++ /dev/null @@ -1,161 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml - -import scala.collection.{ mutable, immutable, generic, SeqLike, AbstractSeq } -import mutable.{ Builder, ListBuffer } -import generic.{ CanBuildFrom } -import scala.language.implicitConversions - -/** - * This object ... - * - * @author Burak Emir - * @version 1.0 - */ -object NodeSeq { - final val Empty = fromSeq(Nil) - def fromSeq(s: Seq[Node]): NodeSeq = new NodeSeq { - def theSeq = s - } - type Coll = NodeSeq - implicit def canBuildFrom: CanBuildFrom[Coll, Node, NodeSeq] = - new CanBuildFrom[Coll, Node, NodeSeq] { - def apply(from: Coll) = newBuilder - def apply() = newBuilder - } - def newBuilder: Builder[Node, NodeSeq] = new ListBuffer[Node] mapResult fromSeq - implicit def seqToNodeSeq(s: Seq[Node]): NodeSeq = fromSeq(s) -} - -/** - * This class implements a wrapper around `Seq[Node]` that adds XPath - * and comprehension methods. - * - * @author Burak Emir - * @version 1.0 - */ -abstract class NodeSeq extends AbstractSeq[Node] with immutable.Seq[Node] with SeqLike[Node, NodeSeq] with Equality { - import NodeSeq.seqToNodeSeq // import view magic for NodeSeq wrappers - - /** Creates a list buffer as builder for this class */ - override protected[this] def newBuilder = NodeSeq.newBuilder - - def theSeq: Seq[Node] - def length = theSeq.length - override def iterator = theSeq.iterator - - def apply(i: Int): Node = theSeq(i) - def apply(f: Node => Boolean): NodeSeq = filter(f) - - def xml_sameElements[A](that: Iterable[A]): Boolean = { - val these = this.iterator - val those = that.iterator - while (these.hasNext && those.hasNext) - if (these.next xml_!= those.next) - return false - - !these.hasNext && !those.hasNext - } - - protected def basisForHashCode: Seq[Any] = theSeq - - override def canEqual(other: Any) = other match { - case _: NodeSeq => true - case _ => false - } - - override def strict_==(other: Equality) = other match { - case x: NodeSeq => (length == x.length) && (theSeq sameElements x.theSeq) - case _ => false - } - - /** - * Projection function, which returns elements of `this` sequence based - * on the string `that`. Use: - * - `this \ "foo"` to get a list of all elements that are labelled with `"foo"`; - * - `\ "_"` to get a list of all elements (wildcard); - * - `ns \ "@foo"` to get the unprefixed attribute `"foo"`; - * - `ns \ "@{uri}foo"` to get the prefixed attribute `"pre:foo"` whose - * prefix `"pre"` is resolved to the namespace `"uri"`. - * - * For attribute projections, the resulting [[scala.xml.NodeSeq]] attribute - * values are wrapped in a [[scala.xml.Group]]. - * - * There is no support for searching a prefixed attribute by its literal prefix. - * - * The document order is preserved. - */ - def \(that: String): NodeSeq = { - def fail = throw new IllegalArgumentException(that) - def atResult = { - lazy val y = this(0) - val attr = - if (that.length == 1) fail - else if (that(1) == '{') { - val i = that indexOf '}' - if (i == -1) fail - val (uri, key) = (that.substring(2, i), that.substring(i + 1, that.length())) - if (uri == "" || key == "") fail - else y.attribute(uri, key) - } else y.attribute(that drop 1) - - attr match { - case Some(x) => Group(x) - case _ => NodeSeq.Empty - } - } - - def makeSeq(cond: (Node) => Boolean) = - NodeSeq fromSeq (this flatMap (_.child) filter cond) - - that match { - case "" => fail - case "_" => makeSeq(!_.isAtom) - case _ if (that(0) == '@' && this.length == 1) => atResult - case _ => makeSeq(_.label == that) - } - } - - /** - * Projection function, which returns elements of `this` sequence and of - * all its subsequences, based on the string `that`. Use: - * - `this \\ 'foo` to get a list of all elements that are labelled with `"foo"`; - * - `\\ "_"` to get a list of all elements (wildcard); - * - `ns \\ "@foo"` to get the unprefixed attribute `"foo"`; - * - `ns \\ "@{uri}foo"` to get each prefixed attribute `"pre:foo"` whose - * prefix `"pre"` is resolved to the namespace `"uri"`. - * - * For attribute projections, the resulting [[scala.xml.NodeSeq]] attribute - * values are wrapped in a [[scala.xml.Group]]. - * - * There is no support for searching a prefixed attribute by its literal prefix. - * - * The document order is preserved. - */ - def \\(that: String): NodeSeq = { - def filt(cond: (Node) => Boolean) = this flatMap (_.descendant_or_self) filter cond - that match { - case "_" => filt(!_.isAtom) - case _ if that(0) == '@' => filt(!_.isAtom) flatMap (_ \ that) - case _ => filt(x => !x.isAtom && x.label == that) - } - } - - /** - * Convenience method which returns string text of the named attribute. Use: - * - `that \@ "foo"` to get the string text of attribute `"foo"`; - */ - def \@(attributeName: String): String = (this \ ("@" + attributeName)).text - - override def toString(): String = theSeq.mkString - - def text: String = (this map (_.text)).mkString -} diff --git a/src/main/scala/scala/xml/Null.scala b/src/main/scala/scala/xml/Null.scala deleted file mode 100644 index d02fd0dcb..000000000 --- a/src/main/scala/scala/xml/Null.scala +++ /dev/null @@ -1,63 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml - -import Utility.isNameStart -import scala.collection.Iterator - -/** - * Essentially, every method in here is a dummy, returning Zero[T]. - * It provides a backstop for the unusual collection defined by MetaData, - * sort of a linked list of tails. - * - * @author Burak Emir - * @version 1.0 - */ -case object Null extends MetaData { - override def iterator = Iterator.empty - override def size = 0 - override def append(m: MetaData, scope: NamespaceBinding = TopScope): MetaData = m - override def filter(f: MetaData => Boolean): MetaData = this - - def copy(next: MetaData) = next - def getNamespace(owner: Node) = null - - override def hasNext = false - def next = null - def key = null - def value = null - def isPrefixed = false - - override def length = 0 - override def length(i: Int) = i - - override def strict_==(other: Equality) = other match { - case x: MetaData => x.length == 0 - case _ => false - } - override protected def basisForHashCode: Seq[Any] = Nil - - def apply(namespace: String, scope: NamespaceBinding, key: String) = null - def apply(key: String) = - if (isNameStart(key.head)) null - else throw new IllegalArgumentException("not a valid attribute name '" + key + "', so can never match !") - - protected def toString1(sb: StringBuilder) = () - override protected def toString1(): String = "" - - override def toString(): String = "" - - override def buildString(sb: StringBuilder): StringBuilder = sb - - override def wellformed(scope: NamespaceBinding) = true - - def remove(key: String) = this - def remove(namespace: String, scope: NamespaceBinding, key: String) = this -} diff --git a/src/main/scala/scala/xml/PrefixedAttribute.scala b/src/main/scala/scala/xml/PrefixedAttribute.scala deleted file mode 100644 index a6dd27d79..000000000 --- a/src/main/scala/scala/xml/PrefixedAttribute.scala +++ /dev/null @@ -1,62 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml - -/** - * prefixed attributes always have a non-null namespace. - * - * @param pre - * @param key - * @param value the attribute value - * @param next1 - */ -class PrefixedAttribute( - val pre: String, - val key: String, - val value: Seq[Node], - val next1: MetaData) - extends Attribute { - val next = if (value ne null) next1 else next1.remove(key) - - /** same as this(pre, key, Text(value), next), or no attribute if value is null */ - def this(pre: String, key: String, value: String, next: MetaData) = - this(pre, key, if (value ne null) Text(value) else null: NodeSeq, next) - - /** same as this(pre, key, value.get, next), or no attribute if value is None */ - def this(pre: String, key: String, value: Option[Seq[Node]], next: MetaData) = - this(pre, key, value.orNull, next) - - /** - * Returns a copy of this unprefixed attribute with the given - * next field. - */ - def copy(next: MetaData) = - new PrefixedAttribute(pre, key, value, next) - - def getNamespace(owner: Node) = - owner.getNamespace(pre) - - /** forwards the call to next (because caller looks for unprefixed attribute */ - def apply(key: String): Seq[Node] = next(key) - - /** - * gets attribute value of qualified (prefixed) attribute with given key - */ - def apply(namespace: String, scope: NamespaceBinding, key: String): Seq[Node] = { - if (key == this.key && scope.getURI(pre) == namespace) - value - else - next(namespace, scope, key) - } -} - -object PrefixedAttribute { - def unapply(x: PrefixedAttribute) = Some((x.pre, x.key, x.value, x.next)) -} diff --git a/src/main/scala/scala/xml/ProcInstr.scala b/src/main/scala/scala/xml/ProcInstr.scala deleted file mode 100644 index a42f6bc05..000000000 --- a/src/main/scala/scala/xml/ProcInstr.scala +++ /dev/null @@ -1,39 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml - -/** - * an XML node for processing instructions (PI) - * - * @author Burak Emir - * @param target target name of this PI - * @param proctext text contained in this node, may not contain "?>" - */ -case class ProcInstr(target: String, proctext: String) extends SpecialNode { - if (!Utility.isName(target)) - throw new IllegalArgumentException(target + " must be an XML Name") - if (proctext contains "?>") - throw new IllegalArgumentException(proctext + " may not contain \"?>\"") - if (target.toLowerCase == "xml") - throw new IllegalArgumentException(target + " is reserved") - - final override def doCollectNamespaces = false - final override def doTransform = false - - final def label = "#PI" - override def text = "" - - /** - * appends "<?" target (" "+text)?+"?>" - * to this stringbuffer. - */ - override def buildString(sb: StringBuilder) = - sb append "".format(target, (if (proctext == "") "" else " " + proctext)) -} diff --git a/src/main/scala/scala/xml/QNode.scala b/src/main/scala/scala/xml/QNode.scala deleted file mode 100644 index cb0dcc648..000000000 --- a/src/main/scala/scala/xml/QNode.scala +++ /dev/null @@ -1,21 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml - -/** - * This object provides an extractor method to match a qualified node with - * its namespace URI - * - * @author Burak Emir - * @version 1.0 - */ -object QNode { - def unapplySeq(n: Node) = Some((n.scope.getURI(n.prefix), n.label, n.attributes, n.child)) -} diff --git a/src/main/scala/scala/xml/SpecialNode.scala b/src/main/scala/scala/xml/SpecialNode.scala deleted file mode 100644 index a7db42b5e..000000000 --- a/src/main/scala/scala/xml/SpecialNode.scala +++ /dev/null @@ -1,34 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml - -/** - * `SpecialNode` is a special XML node which represents either text - * `(PCDATA)`, a comment, a `PI`, or an entity ref. - * - * `SpecialNode`s also play the role of [[scala.xml.pull.XMLEvent]]s for - * pull-parsing. - * - * @author Burak Emir - */ -abstract class SpecialNode extends Node with pull.XMLEvent { - - /** always empty */ - final override def attributes = Null - - /** always Node.EmptyNamespace */ - final override def namespace = null - - /** always empty */ - final def child = Nil - - /** Append string representation to the given string buffer argument. */ - def buildString(sb: StringBuilder): StringBuilder -} diff --git a/src/main/scala/scala/xml/TextBuffer.scala b/src/main/scala/scala/xml/TextBuffer.scala deleted file mode 100644 index 7e2cecd7e..000000000 --- a/src/main/scala/scala/xml/TextBuffer.scala +++ /dev/null @@ -1,47 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml - -import Utility.isSpace - -object TextBuffer { - def fromString(str: String): TextBuffer = new TextBuffer() append str -} - -/** - * The class `TextBuffer` is for creating text nodes without surplus - * whitespace. All occurrences of one or more whitespace in strings - * appended with the `append` method will be replaced by a single space - * character, and leading and trailing space will be removed completely. - */ -class TextBuffer { - val sb = new StringBuilder() - - /** - * Appends this string to the text buffer, trimming whitespaces as needed. - */ - def append(cs: Seq[Char]): this.type = { - cs foreach { c => - if (!isSpace(c)) sb append c - else if (sb.isEmpty || !isSpace(sb.last)) sb append ' ' - } - this - } - - /** - * Returns an empty sequence if text is only whitespace. - * - * @return the text without whitespaces. - */ - def toText: Seq[Text] = sb.toString.trim match { - case "" => Nil - case s => Seq(Text(s)) - } -} diff --git a/src/main/scala/scala/xml/TopScope.scala b/src/main/scala/scala/xml/TopScope.scala deleted file mode 100644 index 53d4b2c5d..000000000 --- a/src/main/scala/scala/xml/TopScope.scala +++ /dev/null @@ -1,31 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml - -/** - * top level namespace scope. only contains the predefined binding - * for the "xml" prefix which is bound to - * "http://www.w3.org/XML/1998/namespace" - */ -object TopScope extends NamespaceBinding(null, null, null) { - - import XML.{ xml, namespace } - - override def getURI(prefix1: String): String = - if (prefix1 == xml) namespace else null - - override def getPrefix(uri1: String): String = - if (uri1 == namespace) xml else null - - override def toString() = "" - - override def buildString(stop: NamespaceBinding) = "" - override def buildString(sb: StringBuilder, ignore: NamespaceBinding) = {} -} diff --git a/src/main/scala/scala/xml/TypeSymbol.scala b/src/main/scala/scala/xml/TypeSymbol.scala deleted file mode 100644 index d2eab6a9b..000000000 --- a/src/main/scala/scala/xml/TypeSymbol.scala +++ /dev/null @@ -1,12 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml - -abstract class TypeSymbol diff --git a/src/main/scala/scala/xml/Unparsed.scala b/src/main/scala/scala/xml/Unparsed.scala deleted file mode 100644 index 97ad396e3..000000000 --- a/src/main/scala/scala/xml/Unparsed.scala +++ /dev/null @@ -1,39 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml - -/** - * An XML node for unparsed content. It will be output verbatim, all bets - * are off regarding wellformedness etc. - * - * @author Burak Emir - * @param data content in this node, may not be null. - */ -class Unparsed(data: String) extends Atom[String](data) { - - /** - * Returns text, with some characters escaped according to XML - * specification. - */ - override def buildString(sb: StringBuilder): StringBuilder = - sb append data -} - -/** - * This singleton object contains the `apply`and `unapply` methods for - * convenient construction and deconstruction. - * - * @author Burak Emir - * @version 1.0 - */ -object Unparsed { - def apply(data: String) = new Unparsed(data) - def unapply(x: Unparsed) = Some(x.data) -} diff --git a/src/main/scala/scala/xml/UnprefixedAttribute.scala b/src/main/scala/scala/xml/UnprefixedAttribute.scala deleted file mode 100644 index 6cc156694..000000000 --- a/src/main/scala/scala/xml/UnprefixedAttribute.scala +++ /dev/null @@ -1,60 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml - -/** - * Unprefixed attributes have the null namespace, and no prefix field - * - * @author Burak Emir - */ -class UnprefixedAttribute( - val key: String, - val value: Seq[Node], - next1: MetaData) - extends Attribute { - final val pre = null - val next = if (value ne null) next1 else next1.remove(key) - - /** same as this(key, Text(value), next), or no attribute if value is null */ - def this(key: String, value: String, next: MetaData) = - this(key, if (value ne null) Text(value) else null: NodeSeq, next) - - /** same as this(key, value.get, next), or no attribute if value is None */ - def this(key: String, value: Option[Seq[Node]], next: MetaData) = - this(key, value.orNull, next) - - /** returns a copy of this unprefixed attribute with the given next field*/ - def copy(next: MetaData) = new UnprefixedAttribute(key, value, next) - - final def getNamespace(owner: Node): String = null - - /** - * Gets value of unqualified (unprefixed) attribute with given key, null if not found - * - * @param key - * @return value as Seq[Node] if key is found, null otherwise - */ - def apply(key: String): Seq[Node] = - if (key == this.key) value else next(key) - - /** - * Forwards the call to next (because caller looks for prefixed attribute). - * - * @param namespace - * @param scope - * @param key - * @return .. - */ - def apply(namespace: String, scope: NamespaceBinding, key: String): Seq[Node] = - next(namespace, scope, key) -} -object UnprefixedAttribute { - def unapply(x: UnprefixedAttribute) = Some((x.key, x.value, x.next)) -} diff --git a/src/main/scala/scala/xml/XML.scala b/src/main/scala/scala/xml/XML.scala deleted file mode 100755 index 034c7e5da..000000000 --- a/src/main/scala/scala/xml/XML.scala +++ /dev/null @@ -1,114 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml - -import parsing.NoBindingFactoryAdapter -import factory.XMLLoader -import java.io.{ File, FileDescriptor, FileInputStream, FileOutputStream } -import java.io.{ InputStream, Reader, StringReader, Writer } -import java.nio.channels.Channels -import scala.util.control.Exception.ultimately - -object Source { - def fromFile(file: File) = new InputSource(new FileInputStream(file)) - def fromFile(fd: FileDescriptor) = new InputSource(new FileInputStream(fd)) - def fromFile(name: String) = new InputSource(new FileInputStream(name)) - - def fromInputStream(is: InputStream) = new InputSource(is) - def fromReader(reader: Reader) = new InputSource(reader) - def fromSysId(sysID: String) = new InputSource(sysID) - def fromString(string: String) = fromReader(new StringReader(string)) -} - -/** - * Governs how empty elements (i.e. those without child elements) should be serialized. - */ -object MinimizeMode extends Enumeration { - /** - * Minimize empty tags if they were originally empty when parsed, or if they were constructed - * with [[scala.xml.Elem]]`#minimizeEmpty` == true - */ - val Default = Value - - /** - * Always minimize empty tags. Note that this may be problematic for XHTML, in which - * case [[scala.xml.Xhtml]]`#toXhtml` should be used instead. - */ - val Always = Value - - /** - * Never minimize empty tags. - */ - val Never = Value -} - -/** - * The object `XML` provides constants, and functions to load - * and save XML elements. Use this when data binding is not desired, i.e. - * when XML is handled using `Symbol` nodes. - * - * @author Burak Emir - * @version 1.0, 25/04/2005 - */ -object XML extends XMLLoader[Elem] { - val xml = "xml" - val xmlns = "xmlns" - val namespace = "http://www.w3.org/XML/1998/namespace" - val preserve = "preserve" - val space = "space" - val lang = "lang" - val encoding = "ISO-8859-1" - - /** Returns an XMLLoader whose load* methods will use the supplied SAXParser. */ - def withSAXParser(p: SAXParser): XMLLoader[Elem] = - new XMLLoader[Elem] { override val parser: SAXParser = p } - - /** - * Saves a node to a file with given filename using given encoding - * optionally with xmldecl and doctype declaration. - * - * @param filename the filename - * @param node the xml node we want to write - * @param enc encoding to use - * @param xmlDecl if true, write xml declaration - * @param doctype if not null, write doctype declaration - */ - final def save( - filename: String, - node: Node, - enc: String = encoding, - xmlDecl: Boolean = false, - doctype: dtd.DocType = null): Unit = - { - val fos = new FileOutputStream(filename) - val w = Channels.newWriter(fos.getChannel(), enc) - - ultimately(w.close())( - write(w, node, enc, xmlDecl, doctype) - ) - } - - /** - * Writes the given node using writer, optionally with xml decl and doctype. - * It's the caller's responsibility to close the writer. - * - * @param w the writer - * @param node the xml node we want to write - * @param enc the string to be used in `xmlDecl` - * @param xmlDecl if true, write xml declaration - * @param doctype if not null, write doctype declaration - */ - final def write(w: java.io.Writer, node: Node, enc: String, xmlDecl: Boolean, doctype: dtd.DocType, minimizeTags: MinimizeMode.Value = MinimizeMode.Default) { - /* TODO: optimize by giving writer parameter to toXML*/ - if (xmlDecl) w.write("\n") - if (doctype ne null) w.write(doctype.toString() + "\n") - w.write(Utility.serialize(node, minimizeTags = minimizeTags).toString) - } -} diff --git a/src/main/scala/scala/xml/Xhtml.scala b/src/main/scala/scala/xml/Xhtml.scala deleted file mode 100644 index ad5fbeda0..000000000 --- a/src/main/scala/scala/xml/Xhtml.scala +++ /dev/null @@ -1,97 +0,0 @@ - -package scala -package xml - -import parsing.XhtmlEntities -import Utility.{ sbToString, isAtomAndNotText } - -/* (c) David Pollak 2007 WorldWide Conferencing, LLC */ - -object Xhtml { - /** - * Convenience function: same as toXhtml(node, false, false) - * - * @param node the node - */ - def toXhtml(node: Node): String = sbToString(sb => toXhtml(x = node, sb = sb)) - - /** - * Convenience function: amounts to calling toXhtml(node) on each - * node in the sequence. - * - * @param nodeSeq the node sequence - */ - def toXhtml(nodeSeq: NodeSeq): String = sbToString(sb => sequenceToXML(nodeSeq: Seq[Node], sb = sb)) - - /** - * Elements which we believe are safe to minimize if minimizeTags is true. - * See http://www.w3.org/TR/xhtml1/guidelines.html#C_3 - */ - private val minimizableElements = - List("base", "meta", "link", "hr", "br", "param", "img", "area", "input", "col") - - def toXhtml( - x: Node, - pscope: NamespaceBinding = TopScope, - sb: StringBuilder = new StringBuilder, - stripComments: Boolean = false, - decodeEntities: Boolean = false, - preserveWhitespace: Boolean = false, - minimizeTags: Boolean = true): Unit = - { - def decode(er: EntityRef) = XhtmlEntities.entMap.get(er.entityName) match { - case Some(chr) if chr.toInt >= 128 => sb.append(chr) - case _ => er.buildString(sb) - } - def shortForm = - minimizeTags && - (x.child == null || x.child.length == 0) && - (minimizableElements contains x.label) - - x match { - case c: Comment => if (!stripComments) c buildString sb - case er: EntityRef if decodeEntities => decode(er) - case x: SpecialNode => x buildString sb - case g: Group => - g.nodes foreach { toXhtml(_, x.scope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags) } - - case _ => - sb.append('<') - x.nameToString(sb) - if (x.attributes ne null) x.attributes.buildString(sb) - x.scope.buildString(sb, pscope) - - if (shortForm) sb.append(" />") - else { - sb.append('>') - sequenceToXML(x.child, x.scope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags) - sb.append("') - } - } - } - - /** - * Amounts to calling toXhtml(node, ...) with the given parameters on each node. - */ - def sequenceToXML( - children: Seq[Node], - pscope: NamespaceBinding = TopScope, - sb: StringBuilder = new StringBuilder, - stripComments: Boolean = false, - decodeEntities: Boolean = false, - preserveWhitespace: Boolean = false, - minimizeTags: Boolean = true): Unit = - { - if (children.isEmpty) - return - - val doSpaces = children forall isAtomAndNotText // interleave spaces - for (c <- children.take(children.length - 1)) { - toXhtml(c, pscope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags) - if (doSpaces) sb append ' ' - } - toXhtml(children.last, pscope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags) - } -} diff --git a/src/main/scala/scala/xml/dtd/ContentModelParser.scala b/src/main/scala/scala/xml/dtd/ContentModelParser.scala deleted file mode 100644 index ec48436b3..000000000 --- a/src/main/scala/scala/xml/dtd/ContentModelParser.scala +++ /dev/null @@ -1,137 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://www.scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml -package dtd - -/** Parser for regexps (content models in DTD element declarations) */ - -object ContentModelParser extends Scanner { // a bit too permissive concerning #PCDATA - import ContentModel._ - - /** parses the argument to a regexp */ - def parse(s: String): ContentModel = { initScanner(s); contentspec } - - def accept(tok: Int) = { - if (token != tok) { - if ((tok == STAR) && (token == END)) // common mistake - scala.sys.error("in DTDs, \n" + - "mixed content models must be like (#PCDATA|Name|Name|...)*") - else - scala.sys.error("expected " + token2string(tok) + - ", got unexpected token:" + token2string(token)) - } - nextToken() - } - - // s [ '+' | '*' | '?' ] - def maybeSuffix(s: RegExp) = token match { - case STAR => - nextToken(); Star(s) - case PLUS => - nextToken(); Sequ(s, Star(s)) - case OPT => - nextToken(); Alt(Eps, s) - case _ => s - } - - // contentspec ::= EMPTY | ANY | (#PCDATA) | "(#PCDATA|"regexp) - - def contentspec: ContentModel = token match { - - case NAME => value match { - case "ANY" => ANY - case "EMPTY" => EMPTY - case _ => scala.sys.error("expected ANY, EMPTY or '(' instead of " + value) - } - case LPAREN => - - nextToken() - sOpt() - if (token != TOKEN_PCDATA) - ELEMENTS(regexp) - else { - nextToken() - token match { - case RPAREN => - PCDATA - case CHOICE => - val res = MIXED(choiceRest(Eps)) - sOpt() - accept(RPAREN) - accept(STAR) - res - case _ => - scala.sys.error("unexpected token:" + token2string(token)) - } - } - - case _ => - scala.sys.error("unexpected token:" + token2string(token)) - } - // sopt ::= S? - def sOpt() = if (token == S) nextToken() - - // (' S? mixed ::= '#PCDATA' S? ')' - // | '#PCDATA' (S? '|' S? atom)* S? ')*' - - // '(' S? regexp ::= cp S? [seqRest|choiceRest] ')' [ '+' | '*' | '?' ] - def regexp: RegExp = { - val p = particle - sOpt() - maybeSuffix(token match { - case RPAREN => - nextToken(); p - case CHOICE => - val q = choiceRest(p); accept(RPAREN); q - case COMMA => val q = seqRest(p); accept(RPAREN); q - }) - } - - // seqRest ::= (',' S? cp S?)+ - def seqRest(p: RegExp) = { - var k = List(p) - while (token == COMMA) { - nextToken() - sOpt() - k = particle :: k - sOpt() - } - Sequ(k.reverse: _*) - } - - // choiceRest ::= ('|' S? cp S?)+ - def choiceRest(p: RegExp) = { - var k = List(p) - while (token == CHOICE) { - nextToken() - sOpt() - k = particle :: k - sOpt() - } - Alt(k.reverse: _*) - } - - // particle ::= '(' S? regexp - // | name [ '+' | '*' | '?' ] - def particle = token match { - case LPAREN => - nextToken(); sOpt(); regexp - case NAME => - val a = Letter(ElemName(value)); nextToken(); maybeSuffix(a) - case _ => scala.sys.error("expected '(' or Name, got:" + token2string(token)) - } - - // atom ::= name - def atom = token match { - case NAME => - val a = Letter(ElemName(value)); nextToken(); a - case _ => scala.sys.error("expected Name, got:" + token2string(token)) - } -} diff --git a/src/main/scala/scala/xml/dtd/DTD.scala b/src/main/scala/scala/xml/dtd/DTD.scala deleted file mode 100644 index 95a3e888f..000000000 --- a/src/main/scala/scala/xml/dtd/DTD.scala +++ /dev/null @@ -1,35 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml -package dtd - -import scala.collection.mutable - -/** - * A document type declaration. - * - * @author Burak Emir - */ -abstract class DTD { - var externalID: ExternalID = null - var decls: List[Decl] = Nil - def notations: Seq[NotationDecl] = Nil - def unparsedEntities: Seq[EntityDecl] = Nil - - var elem: mutable.Map[String, ElemDecl] = new mutable.HashMap[String, ElemDecl]() - var attr: mutable.Map[String, AttListDecl] = new mutable.HashMap[String, AttListDecl]() - var ent: mutable.Map[String, EntityDecl] = new mutable.HashMap[String, EntityDecl]() - - override def toString() = - "DTD [\n%s%s]".format( - Option(externalID) getOrElse "", - decls.mkString("", "\n", "\n") - ) -} diff --git a/src/main/scala/scala/xml/dtd/ElementValidator.scala b/src/main/scala/scala/xml/dtd/ElementValidator.scala deleted file mode 100644 index a55098852..000000000 --- a/src/main/scala/scala/xml/dtd/ElementValidator.scala +++ /dev/null @@ -1,135 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://www.scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml -package dtd - -import PartialFunction._ -import scala.collection.mutable - -import ContentModel.ElemName -import MakeValidationException._ // @todo other exceptions - -import impl._ - -/** - * validate children and/or attributes of an element - * exceptions are created but not thrown. - */ -class ElementValidator() extends Function1[Node, Boolean] { - - private var exc: List[ValidationException] = Nil - - protected var contentModel: ContentModel = _ - protected var dfa: DetWordAutom[ElemName] = _ - protected var adecls: List[AttrDecl] = _ - - /** set content model, enabling element validation */ - def setContentModel(cm: ContentModel) = { - contentModel = cm - cm match { - case ELEMENTS(r) => - val nfa = ContentModel.Translator.automatonFrom(r, 1) - dfa = new SubsetConstruction(nfa).determinize - case _ => - dfa = null - } - } - - def getContentModel = contentModel - - /** set meta data, enabling attribute validation */ - def setMetaData(adecls: List[AttrDecl]) { this.adecls = adecls } - - def getIterable(nodes: Seq[Node], skipPCDATA: Boolean): Iterable[ElemName] = { - def isAllWhitespace(a: Atom[_]) = cond(a.data) { case s: String if s.trim == "" => true } - - nodes.filter { - case y: SpecialNode => y match { - case a: Atom[_] if isAllWhitespace(a) => false // always skip all-whitespace nodes - case _ => !skipPCDATA - } - case x => x.namespace eq null - }.map (x => ElemName(x.label)) - } - - /** - * check attributes, return true if md corresponds to attribute declarations in adecls. - */ - def check(md: MetaData): Boolean = { - val len: Int = exc.length - val ok = new mutable.BitSet(adecls.length) - - for (attr <- md) { - def attrStr = attr.value.toString - def find(Key: String): Option[AttrDecl] = { - adecls.zipWithIndex find { - case (a@AttrDecl(Key, _, _), j) => - ok += j; return Some(a) - case _ => false - } - None - } - - find(attr.key) match { - case None => - exc ::= fromUndefinedAttribute(attr.key) - - case Some(AttrDecl(_, tpe, DEFAULT(true, fixedValue))) if attrStr != fixedValue => - exc ::= fromFixedAttribute(attr.key, fixedValue, attrStr) - - case _ => - } - } - - adecls.zipWithIndex foreach { - case (AttrDecl(key, tpe, REQUIRED), j) if !ok(j) => exc ::= fromMissingAttribute(key, tpe) - case _ => - } - - exc.length == len //- true if no new exception - } - - /** - * check children, return true if conform to content model - * @note contentModel != null - */ - def check(nodes: Seq[Node]): Boolean = contentModel match { - case ANY => true - case EMPTY => getIterable(nodes, skipPCDATA = false).isEmpty - case PCDATA => getIterable(nodes, skipPCDATA = true).isEmpty - case MIXED(ContentModel.Alt(branches@_*)) => // @todo - val j = exc.length - def find(Key: String): Boolean = - branches exists { case ContentModel.Letter(ElemName(Key)) => true; case _ => false } - - getIterable(nodes, skipPCDATA = true) map (_.name) filterNot find foreach { - exc ::= MakeValidationException fromUndefinedElement _ - } - (exc.length == j) // - true if no new exception - - case _: ELEMENTS => - dfa isFinal { - getIterable(nodes, skipPCDATA = false).foldLeft(0) { (q, e) => - (dfa delta q).getOrElse(e, throw ValidationException("element %s not allowed here" format e)) - } - } - case _ => false - } - - /** - * applies various validations - accumulates error messages in exc - * @todo fail on first error, ignore other errors (rearranging conditions) - */ - def apply(n: Node): Boolean = - //- ? check children - ((contentModel == null) || check(n.child)) && - //- ? check attributes - ((adecls == null) || check(n.attributes)) -} diff --git a/src/main/scala/scala/xml/dtd/Scanner.scala b/src/main/scala/scala/xml/dtd/Scanner.scala deleted file mode 100644 index 84a05dde0..000000000 --- a/src/main/scala/scala/xml/dtd/Scanner.scala +++ /dev/null @@ -1,87 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml -package dtd - -/** - * Scanner for regexps (content models in DTD element declarations) - * todo: cleanup - */ -class Scanner extends Tokens with parsing.TokenTests { - - final val ENDCH = '\u0000' - - var token: Int = END - var value: String = _ - - private var it: Iterator[Char] = null - private var c: Char = 'z' - - /** initializes the scanner on input s */ - final def initScanner(s: String) { - value = "" - it = (s).iterator - token = 1 + END - next() - nextToken() - } - - /** scans the next token */ - final def nextToken() { - if (token != END) token = readToken - } - - // todo: see XML specification... probably isLetter,isDigit is fine - final def isIdentChar = (('a' <= c && c <= 'z') - || ('A' <= c && c <= 'Z')) - - final def next() = if (it.hasNext) c = it.next() else c = ENDCH - - final def acc(d: Char) { - if (c == d) next() else scala.sys.error("expected '" + d + "' found '" + c + "' !") - } - - final def accS(ds: Seq[Char]) { ds foreach acc } - - final def readToken: Int = - if (isSpace(c)) { - while (isSpace(c)) c = it.next() - S - } else c match { - case '(' => - next(); LPAREN - case ')' => - next(); RPAREN - case ',' => - next(); COMMA - case '*' => - next(); STAR - case '+' => - next(); PLUS - case '?' => - next(); OPT - case '|' => - next(); CHOICE - case '#' => - next(); accS("PCDATA"); TOKEN_PCDATA - case ENDCH => END - case _ => - if (isNameStart(c)) name; // NAME - else scala.sys.error("unexpected character:" + c) - } - - final def name = { - val sb = new StringBuilder() - do { sb.append(c); next() } while (isNameChar(c)) - value = sb.toString() - NAME - } - -} diff --git a/src/main/scala/scala/xml/dtd/Tokens.scala b/src/main/scala/scala/xml/dtd/Tokens.scala deleted file mode 100644 index 56438589c..000000000 --- a/src/main/scala/scala/xml/dtd/Tokens.scala +++ /dev/null @@ -1,42 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://www.scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml -package dtd - -class Tokens { - - // Tokens - - final val TOKEN_PCDATA = 0 - final val NAME = 1 - final val LPAREN = 3 - final val RPAREN = 4 - final val COMMA = 5 - final val STAR = 6 - final val PLUS = 7 - final val OPT = 8 - final val CHOICE = 9 - final val END = 10 - final val S = 13 - - final def token2string(i: Int): String = i match { - case 0 => "#PCDATA" - case 1 => "NAME" - case 3 => "(" - case 4 => ")" - case 5 => "," - case 6 => "*" - case 7 => "+" - case 8 => "?" - case 9 => "|" - case 10 => "END" - case 13 => " " - } -} diff --git a/src/main/scala/scala/xml/dtd/ValidationException.scala b/src/main/scala/scala/xml/dtd/ValidationException.scala deleted file mode 100644 index e4286b9f2..000000000 --- a/src/main/scala/scala/xml/dtd/ValidationException.scala +++ /dev/null @@ -1,41 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://www.scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml -package dtd - -case class ValidationException(e: String) extends Exception(e) - -/** - * @author Burak Emir - */ -object MakeValidationException { - def fromFixedAttribute(k: String, value: String, actual: String) = - ValidationException("value of attribute " + k + " FIXED to \"" + - value + "\", but document tries \"" + actual + "\"") - - def fromNonEmptyElement() = - new ValidationException("element should be *empty*") - - def fromUndefinedElement(label: String) = - new ValidationException("element \"" + label + "\" not allowed here") - - def fromUndefinedAttribute(key: String) = - new ValidationException("attribute " + key + " not allowed here") - - def fromMissingAttribute(allKeys: Set[String]) = { - val sb = new StringBuilder("missing value for REQUIRED attribute") - if (allKeys.size > 1) sb.append('s') - allKeys foreach (k => sb append "'%s'".format(k)) - new ValidationException(sb.toString()) - } - - def fromMissingAttribute(key: String, tpe: String) = - new ValidationException("missing value for REQUIRED attribute %s of type %s".format(key, tpe)) -} diff --git a/src/main/scala/scala/xml/dtd/impl/Base.scala b/src/main/scala/scala/xml/dtd/impl/Base.scala deleted file mode 100644 index 35e5be757..000000000 --- a/src/main/scala/scala/xml/dtd/impl/Base.scala +++ /dev/null @@ -1,66 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml.dtd.impl - -/** - * Basic regular expressions. - * - * @author Burak Emir - * @version 1.0 - */ - -@deprecated("This class will be removed", "2.10.0") -private[dtd] abstract class Base { - type _regexpT <: RegExp - - abstract class RegExp { - val isNullable: Boolean - } - - object Alt { - /** `Alt( R,R,R* )`. */ - def apply(rs: _regexpT*) = - if (rs.size < 2) throw new SyntaxError("need at least 2 branches in Alt") - else new Alt(rs: _*) - // Can't enforce that statically without changing the interface - // def apply(r1: _regexpT, r2: _regexpT, rs: _regexpT*) = new Alt(Seq(r1, r2) ++ rs: _*) - def unapplySeq(x: Alt) = Some(x.rs) - } - - class Alt private (val rs: _regexpT*) extends RegExp { - final val isNullable = rs exists (_.isNullable) - } - - object Sequ { - /** Sequ( R,R* ) */ - def apply(rs: _regexpT*) = if (rs.isEmpty) Eps else new Sequ(rs: _*) - def unapplySeq(x: Sequ) = Some(x.rs) - } - - class Sequ private (val rs: _regexpT*) extends RegExp { - final val isNullable = rs forall (_.isNullable) - } - - case class Star(r: _regexpT) extends RegExp { - final lazy val isNullable = true - } - - // The empty Sequ. - case object Eps extends RegExp { - final lazy val isNullable = true - override def toString() = "Eps" - } - - /** this class can be used to add meta information to regexps. */ - class Meta(r1: _regexpT) extends RegExp { - final val isNullable = r1.isNullable - def r = r1 - } -} diff --git a/src/main/scala/scala/xml/dtd/impl/DetWordAutom.scala b/src/main/scala/scala/xml/dtd/impl/DetWordAutom.scala deleted file mode 100644 index 28490b11f..000000000 --- a/src/main/scala/scala/xml/dtd/impl/DetWordAutom.scala +++ /dev/null @@ -1,51 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml.dtd.impl - -import scala.collection.{ mutable, immutable } - -/** - * A deterministic automaton. States are integers, where - * 0 is always the only initial state. Transitions are represented - * in the delta function. A default transitions is one that - * is taken when no other transition can be taken. - * All states are reachable. Accepting states are those for which - * the partial function 'finals' is defined. - * - * @author Burak Emir - * @version 1.0 - */ -// TODO: still used in ContentModel -- @deprecated("This class will be removed", "2.10.0") -private[dtd] abstract class DetWordAutom[T <: AnyRef] { - val nstates: Int - val finals: Array[Int] - val delta: Array[mutable.Map[T, Int]] - val default: Array[Int] - - def isFinal(q: Int) = finals(q) != 0 - def isSink(q: Int) = delta(q).isEmpty && default(q) == q - def next(q: Int, label: T) = delta(q).getOrElse(label, default(q)) - - override def toString() = { - val sb = new StringBuilder("[DetWordAutom nstates=") - sb.append(nstates) - sb.append(" finals=") - val map = Map(finals.zipWithIndex map (_.swap): _*) - sb.append(map.toString()) - sb.append(" delta=\n") - - for (i <- 0 until nstates) { - sb append "%d->%s\n".format(i, delta(i)) - if (i < default.length) - sb append "_>%s\n".format(default(i)) - } - sb.toString - } -} diff --git a/src/main/scala/scala/xml/dtd/impl/Inclusion.scala b/src/main/scala/scala/xml/dtd/impl/Inclusion.scala deleted file mode 100644 index a609a6e55..000000000 --- a/src/main/scala/scala/xml/dtd/impl/Inclusion.scala +++ /dev/null @@ -1,69 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml.dtd.impl - -/** - * A fast test of language inclusion between minimal automata. - * inspired by the ''AMoRE automata library''. - * - * @author Burak Emir - * @version 1.0 - */ -@deprecated("This class will be removed", "2.10.0") -private[dtd] trait Inclusion[A <: AnyRef] { - - val labels: Seq[A] - - /** - * Returns true if `dfa1` is included in `dfa2`. - */ - def inclusion(dfa1: DetWordAutom[A], dfa2: DetWordAutom[A]) = { - - def encode(q1: Int, q2: Int) = 1 + q1 + q2 * dfa1.nstates - def decode2(c: Int) = (c - 1) / (dfa1.nstates) //integer division - def decode1(c: Int) = (c - 1) % (dfa1.nstates) - - var q1 = 0 //dfa1.initstate; // == 0 - var q2 = 0 //dfa2.initstate; // == 0 - - val max = 1 + dfa1.nstates * dfa2.nstates - val mark = new Array[Int](max) - - var result = true - var current = encode(q1, q2) - var last = current - mark(last) = max // mark (q1,q2) - while (current != 0 && result) { - //Console.println("current = [["+q1+" "+q2+"]] = "+current); - for (letter <- labels) { - val r1 = dfa1.next(q1, letter) - val r2 = dfa2.next(q2, letter) - if (dfa1.isFinal(r1) && !dfa2.isFinal(r2)) - result = false - val test = encode(r1, r2) - //Console.println("test = [["+r1+" "+r2+"]] = "+test); - if (mark(test) == 0) { - mark(last) = test - mark(test) = max - last = test - } - } - val ncurrent = mark(current) - if (ncurrent != max) { - q1 = decode1(ncurrent) - q2 = decode2(ncurrent) - current = ncurrent - } else { - current = 0 - } - } - result - } -} diff --git a/src/main/scala/scala/xml/dtd/impl/PointedHedgeExp.scala b/src/main/scala/scala/xml/dtd/impl/PointedHedgeExp.scala deleted file mode 100644 index 4b8026206..000000000 --- a/src/main/scala/scala/xml/dtd/impl/PointedHedgeExp.scala +++ /dev/null @@ -1,36 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml.dtd.impl - -/** - * Pointed regular hedge expressions, a useful subclass of regular hedge expressions. - * - * @author Burak Emir - * @version 1.0 - */ -@deprecated("This class will be removed", "2.10.0") -private[dtd] abstract class PointedHedgeExp extends Base { - - type _regexpT <: RegExp - type _labelT - - case class Node(label: _labelT, r: _regexpT) extends RegExp { - final val isNullable = false - } - - case class TopIter(r1: _regexpT, r2: _regexpT) extends RegExp { - final val isNullable = r1.isNullable && r2.isNullable //? - } - - case object Point extends RegExp { - final val isNullable = false - } - -} diff --git a/src/main/scala/scala/xml/dtd/impl/SubsetConstruction.scala b/src/main/scala/scala/xml/dtd/impl/SubsetConstruction.scala deleted file mode 100644 index 9501155b5..000000000 --- a/src/main/scala/scala/xml/dtd/impl/SubsetConstruction.scala +++ /dev/null @@ -1,108 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml.dtd.impl - -import scala.collection.{ mutable, immutable } - -// TODO: still used in ContentModel -- @deprecated("This class will be removed", "2.10.0") -private[dtd] class SubsetConstruction[T <: AnyRef](val nfa: NondetWordAutom[T]) { - import nfa.labels - - def selectTag(Q: immutable.BitSet, finals: Array[Int]) = - (Q map finals filter (_ > 0)).min - - def determinize: DetWordAutom[T] = { - // for assigning numbers to bitsets - var indexMap = scala.collection.Map[immutable.BitSet, Int]() - var invIndexMap = scala.collection.Map[Int, immutable.BitSet]() - var ix = 0 - - // we compute the dfa with states = bitsets - val q0 = immutable.BitSet(0) // the set { 0 } - val sink = immutable.BitSet.empty // the set { } - - var states = Set(q0, sink) // initial set of sets - val delta = new mutable.HashMap[immutable.BitSet, mutable.HashMap[T, immutable.BitSet]] - var deftrans = mutable.Map(q0 -> sink, sink -> sink) // initial transitions - var finals: mutable.Map[immutable.BitSet, Int] = mutable.Map() - val rest = new mutable.Stack[immutable.BitSet] - - rest.push(sink, q0) - - def addFinal(q: immutable.BitSet) { - if (nfa containsFinal q) - finals = finals.updated(q, selectTag(q, nfa.finals)) - } - def add(Q: immutable.BitSet) { - if (!states(Q)) { - states += Q - rest push Q - addFinal(Q) - } - } - - addFinal(q0) // initial state may also be a final state - - while (!rest.isEmpty) { - val P = rest.pop() - // assign a number to this bitset - indexMap = indexMap.updated(P, ix) - invIndexMap = invIndexMap.updated(ix, P) - ix += 1 - - // make transition map - val Pdelta = new mutable.HashMap[T, immutable.BitSet] - delta.update(P, Pdelta) - - labels foreach { label => - val Q = nfa.next(P, label) - Pdelta.update(label, Q) - add(Q) - } - - // collect default transitions - val Pdef = nfa nextDefault P - deftrans = deftrans.updated(P, Pdef) - add(Pdef) - } - - // create DetWordAutom, using indices instead of sets - val nstatesR = states.size - val deltaR = new Array[mutable.Map[T, Int]](nstatesR) - val defaultR = new Array[Int](nstatesR) - val finalsR = new Array[Int](nstatesR) - - for (Q <- states) { - val q = indexMap(Q) - val trans = delta(Q) - val transDef = deftrans(Q) - val qDef = indexMap(transDef) - val ntrans = new mutable.HashMap[T, Int]() - - for ((label, value) <- trans) { - val p = indexMap(value) - if (p != qDef) - ntrans.update(label, p) - } - - deltaR(q) = ntrans - defaultR(q) = qDef - } - - finals foreach { case (k, v) => finalsR(indexMap(k)) = v } - - new DetWordAutom[T] { - val nstates = nstatesR - val delta = deltaR - val default = defaultR - val finals = finalsR - } - } -} diff --git a/src/main/scala/scala/xml/dtd/impl/SyntaxError.scala b/src/main/scala/scala/xml/dtd/impl/SyntaxError.scala deleted file mode 100644 index 131d9550a..000000000 --- a/src/main/scala/scala/xml/dtd/impl/SyntaxError.scala +++ /dev/null @@ -1,20 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml.dtd.impl - -/** - * This runtime exception is thrown if an attempt to instantiate a - * syntactically incorrect expression is detected. - * - * @author Burak Emir - * @version 1.0 - */ -@deprecated("This class will be removed", "2.10.0") -private[dtd] class SyntaxError(e: String) extends RuntimeException(e) diff --git a/src/main/scala/scala/xml/factory/Binder.scala b/src/main/scala/scala/xml/factory/Binder.scala deleted file mode 100755 index 0189c8b72..000000000 --- a/src/main/scala/scala/xml/factory/Binder.scala +++ /dev/null @@ -1,59 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml -package factory - -import parsing.ValidatingMarkupHandler - -/** - * @author Burak Emir - */ -abstract class Binder(val preserveWS: Boolean) extends ValidatingMarkupHandler { - - var result: NodeBuffer = new NodeBuffer() - - def reportSyntaxError(pos: Int, str: String) = {} - - final def procInstr(pos: Int, target: String, txt: String) = - ProcInstr(target, txt) - - final def comment(pos: Int, txt: String) = - Comment(txt) - - final def entityRef(pos: Int, n: String) = - EntityRef(n) - - final def text(pos: Int, txt: String) = - Text(txt) - - final def traverse(n: Node): Unit = n match { - case x: ProcInstr => - result &+ procInstr(0, x.target, x.text) - case x: Comment => - result &+ comment(0, x.text) - case x: Text => - result &+ text(0, x.data) - case x: EntityRef => - result &+ entityRef(0, x.entityName) - case x: Elem => - elemStart(0, x.prefix, x.label, x.attributes, x.scope) - val old = result - result = new NodeBuffer() - for (m <- x.child) traverse(m) - result = old &+ elem(0, x.prefix, x.label, x.attributes, x.scope, x.minimizeEmpty, NodeSeq.fromSeq(result)).toList - elemEnd(0, x.prefix, x.label) - } - - final def validate(n: Node): Node = { - this.rootLabel = n.label - traverse(n) - result(0) - } -} diff --git a/src/main/scala/scala/xml/factory/LoggedNodeFactory.scala b/src/main/scala/scala/xml/factory/LoggedNodeFactory.scala deleted file mode 100644 index 27a90aac4..000000000 --- a/src/main/scala/scala/xml/factory/LoggedNodeFactory.scala +++ /dev/null @@ -1,90 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml -package factory - -/** - * This class logs what the nodefactory is actually doing. - * If you want to see what happens during loading, use it like this: - * {{{ - * object testLogged extends App { - * val x = new scala.xml.parsing.NoBindingFactoryAdapter - * with scala.xml.factory.LoggedNodeFactory[scala.xml.Elem] { - * override def log(s: String) = println(s) - * } - * - * Console.println("Start") - * val doc = x.load(new java.net.URL("http://example.com/file.xml")) - * Console.println("End") - * Console.println(doc) - * } - * }}} - * - * @author Burak Emir - * @version 1.0 - */ -@deprecated("This trait will be removed.", "2.11") -trait LoggedNodeFactory[A <: Node] extends NodeFactory[A] { - // configuration values - val logNode = true - val logText = false - val logComment = false - val logProcInstr = false - - final val NONE = 0 - final val CACHE = 1 - final val FULL = 2 - /** 0 = no logging, 1 = cache hits, 2 = detail */ - val logCompressLevel = 1 - - // methods of NodeFactory - - /** logged version of makeNode method */ - override def makeNode(pre: String, label: String, attrSeq: MetaData, - scope: NamespaceBinding, children: Seq[Node]): A = { - if (logNode) - log("[makeNode for " + label + "]") - - val hash = Utility.hashCode(pre, label, attrSeq.##, scope.##, children) - - /* - if(logCompressLevel >= FULL) { - log("[hashcode total:"+hash); - log(" elem name "+uname+" hash "+ ? )); - log(" attrs "+attrSeq+" hash "+attrSeq.hashCode()); - log(" children :"+children+" hash "+children.hashCode()); - } - */ - if (!cache.get(hash).isEmpty && (logCompressLevel >= CACHE)) - log("[cache hit !]") - - super.makeNode(pre, label, attrSeq, scope, children) - } - - override def makeText(s: String) = { - if (logText) - log("[makeText:\"" + s + "\"]") - super.makeText(s) - } - - override def makeComment(s: String): Seq[Comment] = { - if (logComment) - log("[makeComment:\"" + s + "\"]") - super.makeComment(s) - } - - override def makeProcInstr(t: String, s: String): Seq[ProcInstr] = { - if (logProcInstr) - log("[makeProcInstr:\"" + t + " " + s + "\"]") - super.makeProcInstr(t, s) - } - - def log(msg: String): Unit = {} -} diff --git a/src/main/scala/scala/xml/factory/XMLLoader.scala b/src/main/scala/scala/xml/factory/XMLLoader.scala deleted file mode 100644 index 4e3cacf7b..000000000 --- a/src/main/scala/scala/xml/factory/XMLLoader.scala +++ /dev/null @@ -1,61 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml -package factory - -import javax.xml.parsers.SAXParserFactory -import parsing.{ FactoryAdapter, NoBindingFactoryAdapter } -import java.io.{ InputStream, Reader, File, FileDescriptor } -import java.net.URL - -/** - * Presents collection of XML loading methods which use the parser - * created by "def parser". - */ -trait XMLLoader[T <: Node] { - import scala.xml.Source._ - def adapter: FactoryAdapter = new NoBindingFactoryAdapter() - - /* Override this to use a different SAXParser. */ - def parser: SAXParser = { - val f = SAXParserFactory.newInstance() - f.setNamespaceAware(false) - f.newSAXParser() - } - - /** - * Loads XML from the given InputSource, using the supplied parser. - * The methods available in scala.xml.XML use the XML parser in the JDK. - */ - def loadXML(source: InputSource, parser: SAXParser): T = { - val newAdapter = adapter - - newAdapter.scopeStack push TopScope - parser.parse(source, newAdapter) - newAdapter.scopeStack.pop() - - newAdapter.rootElem.asInstanceOf[T] - } - - /** Loads XML from the given file, file descriptor, or filename. */ - def loadFile(file: File): T = loadXML(fromFile(file), parser) - def loadFile(fd: FileDescriptor): T = loadXML(fromFile(fd), parser) - def loadFile(name: String): T = loadXML(fromFile(name), parser) - - /** loads XML from given InputStream, Reader, sysID, InputSource, or URL. */ - def load(is: InputStream): T = loadXML(fromInputStream(is), parser) - def load(reader: Reader): T = loadXML(fromReader(reader), parser) - def load(sysID: String): T = loadXML(fromSysId(sysID), parser) - def load(source: InputSource): T = loadXML(source, parser) - def load(url: URL): T = loadXML(fromInputStream(url.openStream()), parser) - - /** Loads XML from the given String. */ - def loadString(string: String): T = loadXML(fromString(string), parser) -} diff --git a/src/main/scala/scala/xml/include/CircularIncludeException.scala b/src/main/scala/scala/xml/include/CircularIncludeException.scala deleted file mode 100644 index 351f40300..000000000 --- a/src/main/scala/scala/xml/include/CircularIncludeException.scala +++ /dev/null @@ -1,25 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml -package include - -/** - * A `CircularIncludeException` is thrown when an included document attempts - * to include itself or one of its ancestor documents. - */ -class CircularIncludeException(message: String) extends XIncludeException { - - /** - * Constructs a `CircularIncludeException` with `'''null'''`. - * as its error detail message. - */ - def this() = this(null) - -} diff --git a/src/main/scala/scala/xml/include/UnavailableResourceException.scala b/src/main/scala/scala/xml/include/UnavailableResourceException.scala deleted file mode 100644 index 3298c2794..000000000 --- a/src/main/scala/scala/xml/include/UnavailableResourceException.scala +++ /dev/null @@ -1,20 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml -package include - -/** - * An `UnavailableResourceException` is thrown when an included document - * cannot be found or loaded. - */ -class UnavailableResourceException(message: String) - extends XIncludeException(message) { - def this() = this(null) -} diff --git a/src/main/scala/scala/xml/include/sax/XIncluder.scala b/src/main/scala/scala/xml/include/sax/XIncluder.scala deleted file mode 100644 index 4697a8c11..000000000 --- a/src/main/scala/scala/xml/include/sax/XIncluder.scala +++ /dev/null @@ -1,179 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml -package include.sax - -import scala.collection.mutable -import org.xml.sax.{ ContentHandler, XMLReader, Locator, Attributes } -import org.xml.sax.ext.LexicalHandler -import java.io.{ File, OutputStream, OutputStreamWriter, Writer, IOException } - -/** - * XIncluder is a SAX `ContentHandler` that writes its XML document onto - * an output stream after resolving all `xinclude:include` elements. - * - * Based on Eliotte Rusty Harold's SAXXIncluder. - */ -class XIncluder(outs: OutputStream, encoding: String) extends ContentHandler with LexicalHandler { - - var out = new OutputStreamWriter(outs, encoding) - - def setDocumentLocator(locator: Locator) {} - - def startDocument() { - try { - out.write("\r\n") - } catch { - case e: IOException => - throw new SAXException("Write failed", e) - } - } - - def endDocument() { - try { - out.flush() - } catch { - case e: IOException => - throw new SAXException("Flush failed", e) - } - } - - def startPrefixMapping(prefix: String, uri: String) {} - - def endPrefixMapping(prefix: String) {} - - def startElement(namespaceURI: String, localName: String, qualifiedName: String, atts: Attributes) = { - try { - out.write("<" + qualifiedName) - var i = 0; while (i < atts.getLength()) { - out.write(" ") - out.write(atts.getQName(i)) - out.write("='") - val value = atts.getValue(i) - // @todo Need to use character references if the encoding - // can't support the character - out.write(scala.xml.Utility.escape(value)) - out.write("'") - i += 1 - } - out.write(">") - } catch { - case e: IOException => - throw new SAXException("Write failed", e) - } - } - - def endElement(namespaceURI: String, localName: String, qualifiedName: String) { - try { - out.write("") - } catch { - case e: IOException => - throw new SAXException("Write failed", e) - } - } - - // need to escape characters that are not in the given - // encoding using character references???? - def characters(ch: Array[Char], start: Int, length: Int) { - try { - var i = 0; while (i < length) { - val c = ch(start + i) - if (c == '&') out.write("&") - else if (c == '<') out.write("<") - // This next fix is normally not necessary. - // However, it is required if text contains ]]> - // (The end CDATA section delimiter) - else if (c == '>') out.write(">") - else out.write(c.toInt) - i += 1 - } - } catch { - case e: IOException => - throw new SAXException("Write failed", e) - } - } - - def ignorableWhitespace(ch: Array[Char], start: Int, length: Int) { - this.characters(ch, start, length) - } - - // do I need to escape text in PI???? - def processingInstruction(target: String, data: String) { - try { - out.write("") - } catch { - case e: IOException => - throw new SAXException("Write failed", e) - } - } - - def skippedEntity(name: String) { - try { - out.write("&" + name + ";") - } catch { - case e: IOException => - throw new SAXException("Write failed", e) - } - } - - // LexicalHandler methods - private var inDTD: Boolean = false - private val entities = new mutable.Stack[String]() - - def startDTD(name: String, publicID: String, systemID: String) { - inDTD = true - // if this is the source document, output a DOCTYPE declaration - if (entities.isEmpty) { - var id = "" - if (publicID != null) id = " PUBLIC \"" + publicID + "\" \"" + systemID + '"' - else if (systemID != null) id = " SYSTEM \"" + systemID + '"' - try { - out.write("\r\n") - } catch { - case e: IOException => - throw new SAXException("Error while writing DOCTYPE", e) - } - } - } - def endDTD() {} - - def startEntity(name: String) { - entities push name - } - - def endEntity(name: String) { - entities.pop() - } - - def startCDATA() {} - def endCDATA() {} - - // Just need this reference so we can ask if a comment is - // inside an include element or not - private var filter: XIncludeFilter = null - - def setFilter(filter: XIncludeFilter) { - this.filter = filter - } - - def comment(ch: Array[Char], start: Int, length: Int) { - if (!inDTD && !filter.insideIncludeElement()) { - try { - out.write("") - } catch { - case e: IOException => - throw new SAXException("Write failed", e) - } - } - } -} diff --git a/src/main/scala/scala/xml/package.scala b/src/main/scala/scala/xml/package.scala deleted file mode 100644 index 7e90d80b0..000000000 --- a/src/main/scala/scala/xml/package.scala +++ /dev/null @@ -1,19 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala - -package object xml { - val XercesClassName = "org.apache.xerces.parsers.SAXParser" - - type SAXException = org.xml.sax.SAXException - type SAXParseException = org.xml.sax.SAXParseException - type EntityResolver = org.xml.sax.EntityResolver - type InputSource = org.xml.sax.InputSource - type SAXParser = javax.xml.parsers.SAXParser -} diff --git a/src/main/scala/scala/xml/parsing/ConstructingHandler.scala b/src/main/scala/scala/xml/parsing/ConstructingHandler.scala deleted file mode 100755 index 7de6cc419..000000000 --- a/src/main/scala/scala/xml/parsing/ConstructingHandler.scala +++ /dev/null @@ -1,32 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml -package parsing - -/** - * Implementation of MarkupHandler that constructs nodes. - * - * @author Burak Emir - * @version 1.0 - */ -abstract class ConstructingHandler extends MarkupHandler { - val preserveWS: Boolean - - def elem(pos: Int, pre: String, label: String, attrs: MetaData, - pscope: NamespaceBinding, empty: Boolean, nodes: NodeSeq): NodeSeq = - Elem(pre, label, attrs, pscope, empty, nodes: _*) - - def procInstr(pos: Int, target: String, txt: String) = - ProcInstr(target, txt) - - def comment(pos: Int, txt: String) = Comment(txt) - def entityRef(pos: Int, n: String) = EntityRef(n) - def text(pos: Int, txt: String) = Text(txt) -} diff --git a/src/main/scala/scala/xml/parsing/DefaultMarkupHandler.scala b/src/main/scala/scala/xml/parsing/DefaultMarkupHandler.scala deleted file mode 100755 index 796891341..000000000 --- a/src/main/scala/scala/xml/parsing/DefaultMarkupHandler.scala +++ /dev/null @@ -1,27 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml -package parsing - -/** Default implementation of markup handler always returns `NodeSeq.Empty` */ -abstract class DefaultMarkupHandler extends MarkupHandler { - - def elem(pos: Int, pre: String, label: String, attrs: MetaData, - scope: NamespaceBinding, empty: Boolean, args: NodeSeq) = NodeSeq.Empty - - def procInstr(pos: Int, target: String, txt: String) = NodeSeq.Empty - - def comment(pos: Int, comment: String): NodeSeq = NodeSeq.Empty - - def entityRef(pos: Int, n: String) = NodeSeq.Empty - - def text(pos: Int, txt: String) = NodeSeq.Empty - -} diff --git a/src/main/scala/scala/xml/parsing/ExternalSources.scala b/src/main/scala/scala/xml/parsing/ExternalSources.scala deleted file mode 100644 index 13a88ae40..000000000 --- a/src/main/scala/scala/xml/parsing/ExternalSources.scala +++ /dev/null @@ -1,36 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml -package parsing - -import java.net.URL -import java.io.File.separator - -import scala.io.Source - -/** - * @author Burak Emir - * @version 1.0 - */ -trait ExternalSources { - self: ExternalSources with MarkupParser with MarkupHandler => - - def externalSource(systemId: String): Source = { - if (systemId startsWith "http:") - return Source fromURL new URL(systemId) - - val fileStr: String = input.descr match { - case x if x startsWith "file:" => x drop 5 - case x => x take ((x lastIndexOf separator) + 1) - } - - Source.fromFile(fileStr + systemId) - } -} diff --git a/src/main/scala/scala/xml/parsing/FactoryAdapter.scala b/src/main/scala/scala/xml/parsing/FactoryAdapter.scala deleted file mode 100644 index ba57434ab..000000000 --- a/src/main/scala/scala/xml/parsing/FactoryAdapter.scala +++ /dev/null @@ -1,194 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml -package parsing - -import java.io.{ InputStream, Reader, File, FileDescriptor, FileInputStream } -import scala.collection.{ mutable, Iterator } -import org.xml.sax.Attributes -import org.xml.sax.helpers.DefaultHandler - -// can be mixed into FactoryAdapter if desired -trait ConsoleErrorHandler extends DefaultHandler { - // ignore warning, crimson warns even for entity resolution! - override def warning(ex: SAXParseException): Unit = {} - override def error(ex: SAXParseException): Unit = printError("Error", ex) - override def fatalError(ex: SAXParseException): Unit = printError("Fatal Error", ex) - - protected def printError(errtype: String, ex: SAXParseException): Unit = - Console.withOut(Console.err) { - val s = "[%s]:%d:%d: %s".format( - errtype, ex.getLineNumber, ex.getColumnNumber, ex.getMessage) - Console.println(s) - Console.flush() - } -} - -/** - * SAX adapter class, for use with Java SAX parser. Keeps track of - * namespace bindings, without relying on namespace handling of the - * underlying SAX parser. - */ -abstract class FactoryAdapter extends DefaultHandler with factory.XMLLoader[Node] { - var rootElem: Node = null - - val buffer = new StringBuilder() - val attribStack = new mutable.Stack[MetaData] - val hStack = new mutable.Stack[Node] // [ element ] contains siblings - val tagStack = new mutable.Stack[String] - var scopeStack = new mutable.Stack[NamespaceBinding] - - var curTag: String = null - var capture: Boolean = false - - // abstract methods - - /** - * Tests if an XML element contains text. - * @return true if element named `localName` contains text. - */ - def nodeContainsText(localName: String): Boolean // abstract - - /** - * creates an new non-text(tree) node. - * @param elemName - * @param attribs - * @param chIter - * @return a new XML element. - */ - def createNode(pre: String, elemName: String, attribs: MetaData, - scope: NamespaceBinding, chIter: List[Node]): Node // abstract - - /** - * creates a Text node. - * @param text - * @return a new Text node. - */ - def createText(text: String): Text // abstract - - /** - * creates a new processing instruction node. - */ - def createProcInstr(target: String, data: String): Seq[ProcInstr] - - // - // ContentHandler methods - // - - val normalizeWhitespace = false - - /** - * Characters. - * @param ch - * @param offset - * @param length - */ - override def characters(ch: Array[Char], offset: Int, length: Int): Unit = { - if (!capture) return - // compliant: report every character - else if (!normalizeWhitespace) buffer.appendAll(ch, offset, length) - // normalizing whitespace is not compliant, but useful - else { - var it = ch.slice(offset, offset + length).iterator - while (it.hasNext) { - val c = it.next() - val isSpace = c.isWhitespace - buffer append (if (isSpace) ' ' else c) - if (isSpace) - it = it dropWhile (_.isWhitespace) - } - } - } - - private def splitName(s: String) = { - val idx = s indexOf ':' - if (idx < 0) (null, s) - else (s take idx, s drop (idx + 1)) - } - - /* ContentHandler methods */ - - /* Start element. */ - override def startElement( - uri: String, - _localName: String, - qname: String, - attributes: Attributes): Unit = - { - captureText() - tagStack push curTag - curTag = qname - - val localName = splitName(qname)._2 - capture = nodeContainsText(localName) - - hStack push null - var m: MetaData = Null - var scpe: NamespaceBinding = - if (scopeStack.isEmpty) TopScope - else scopeStack.top - - for (i <- 0 until attributes.getLength()) { - val qname = attributes getQName i - val value = attributes getValue i - val (pre, key) = splitName(qname) - def nullIfEmpty(s: String) = if (s == "") null else s - - if (pre == "xmlns" || (pre == null && qname == "xmlns")) { - val arg = if (pre == null) null else key - scpe = new NamespaceBinding(arg, nullIfEmpty(value), scpe) - } else - m = Attribute(Option(pre), key, Text(value), m) - } - - scopeStack push scpe - attribStack push m - } - - /** - * captures text, possibly normalizing whitespace - */ - def captureText(): Unit = { - if (capture && buffer.length > 0) - hStack push createText(buffer.toString) - - buffer.clear() - } - - /** - * End element. - * @param uri - * @param _localName - * @param qname - * @throws org.xml.sax.SAXException if .. - */ - override def endElement(uri: String, _localName: String, qname: String): Unit = { - captureText() - val metaData = attribStack.pop() - - // reverse order to get it right - val v = (Iterator continually hStack.pop takeWhile (_ != null)).toList.reverse - val (pre, localName) = splitName(qname) - val scp = scopeStack.pop() - - // create element - rootElem = createNode(pre, localName, metaData, scp, v) - hStack push rootElem - curTag = tagStack.pop() - capture = curTag != null && nodeContainsText(curTag) // root level - } - - /** - * Processing instruction. - */ - override def processingInstruction(target: String, data: String) { - hStack pushAll createProcInstr(target, data) - } -} diff --git a/src/main/scala/scala/xml/parsing/FatalError.scala b/src/main/scala/scala/xml/parsing/FatalError.scala deleted file mode 100644 index 700d68c5c..000000000 --- a/src/main/scala/scala/xml/parsing/FatalError.scala +++ /dev/null @@ -1,16 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml -package parsing - -/** - * !!! This is poorly named, but I guess it's in the API. - */ -case class FatalError(msg: String) extends java.lang.RuntimeException(msg) diff --git a/src/main/scala/scala/xml/parsing/NoBindingFactoryAdapter.scala b/src/main/scala/scala/xml/parsing/NoBindingFactoryAdapter.scala deleted file mode 100644 index 7b3bfe061..000000000 --- a/src/main/scala/scala/xml/parsing/NoBindingFactoryAdapter.scala +++ /dev/null @@ -1,37 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml -package parsing - -import factory.NodeFactory - -/** - * nobinding adaptor providing callbacks to parser to create elements. - * implements hash-consing - */ -class NoBindingFactoryAdapter extends FactoryAdapter with NodeFactory[Elem] { - /** True. Every XML node may contain text that the application needs */ - def nodeContainsText(label: String) = true - - /** From NodeFactory. Constructs an instance of scala.xml.Elem -- TODO: deprecate as in Elem */ - protected def create(pre: String, label: String, attrs: MetaData, scope: NamespaceBinding, children: Seq[Node]): Elem = - Elem(pre, label, attrs, scope, children.isEmpty, children: _*) - - /** From FactoryAdapter. Creates a node. never creates the same node twice, using hash-consing. - TODO: deprecate as in Elem, or forward to create?? */ - def createNode(pre: String, label: String, attrs: MetaData, scope: NamespaceBinding, children: List[Node]): Elem = - Elem(pre, label, attrs, scope, children.isEmpty, children: _*) - - /** Creates a text node. */ - def createText(text: String) = Text(text) - - /** Creates a processing instruction. */ - def createProcInstr(target: String, data: String) = makeProcInstr(target, data) -} diff --git a/src/main/scala/scala/xml/parsing/ValidatingMarkupHandler.scala b/src/main/scala/scala/xml/parsing/ValidatingMarkupHandler.scala deleted file mode 100644 index fcd2d0884..000000000 --- a/src/main/scala/scala/xml/parsing/ValidatingMarkupHandler.scala +++ /dev/null @@ -1,102 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml -package parsing - -import scala.xml.dtd._ - -abstract class ValidatingMarkupHandler extends MarkupHandler { - - var rootLabel: String = _ - var qStack: List[Int] = Nil - var qCurrent: Int = -1 - - var declStack: List[ElemDecl] = Nil - var declCurrent: ElemDecl = null - - final override val isValidating = true - - override def endDTD(n: String) = { - rootLabel = n - } - override def elemStart(pos: Int, pre: String, label: String, attrs: MetaData, scope: NamespaceBinding) { - - def advanceDFA(dm: DFAContentModel) = { - val trans = dm.dfa.delta(qCurrent) - // println("advanceDFA(dm): " + dm) - // println("advanceDFA(trans): " + trans) - trans.get(ContentModel.ElemName(label)) match { - case Some(qNew) => qCurrent = qNew - case _ => reportValidationError(pos, "DTD says, wrong element, expected one of " + trans.keys) - } - } - // advance in current automaton - // println("[qCurrent = " + qCurrent + " visiting " + label + "]") - - if (qCurrent == -1) { // root - // println(" checking root") - if (label != rootLabel) - reportValidationError(pos, "this element should be " + rootLabel) - } else { - // println(" checking node") - declCurrent.contentModel match { - case ANY => - case EMPTY => - reportValidationError(pos, "DTD says, no elems, no text allowed here") - case PCDATA => - reportValidationError(pos, "DTD says, no elements allowed here") - case m@MIXED(r) => - advanceDFA(m) - case e@ELEMENTS(r) => - advanceDFA(e) - } - } - // push state, decl - qStack = qCurrent :: qStack - declStack = declCurrent :: declStack - - declCurrent = lookupElemDecl(label) - qCurrent = 0 - // println(" done now") - } - - override def elemEnd(pos: Int, pre: String, label: String) { - // println(" elemEnd") - qCurrent = qStack.head - qStack = qStack.tail - declCurrent = declStack.head - declStack = declStack.tail - // println(" qCurrent now" + qCurrent) - // println(" declCurrent now" + declCurrent) - } - - final override def elemDecl(name: String, cmstr: String) { - decls = ElemDecl(name, ContentModel.parse(cmstr)) :: decls - } - - final override def attListDecl(name: String, attList: List[AttrDecl]) { - decls = AttListDecl(name, attList) :: decls - } - - final override def unparsedEntityDecl(name: String, extID: ExternalID, notat: String) { - decls = UnparsedEntityDecl(name, extID, notat) :: decls - } - - final override def notationDecl(notat: String, extID: ExternalID) { - decls = NotationDecl(notat, extID) :: decls - } - - final override def peReference(name: String) { - decls = PEReference(name) :: decls - } - - /** report a syntax error */ - def reportValidationError(pos: Int, str: String): Unit -} diff --git a/src/main/scala/scala/xml/parsing/XhtmlParser.scala b/src/main/scala/scala/xml/parsing/XhtmlParser.scala deleted file mode 100644 index a463f49a8..000000000 --- a/src/main/scala/scala/xml/parsing/XhtmlParser.scala +++ /dev/null @@ -1,33 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml -package parsing - -import scala.io.Source - -/** - * An XML Parser that preserves `CDATA` blocks and knows about - * [[scala.xml.parsing.XhtmlEntities]]. - * - * @author (c) David Pollak, 2007 WorldWide Conferencing, LLC. - */ -class XhtmlParser(val input: Source) extends ConstructingHandler with MarkupParser with ExternalSources { - val preserveWS = true - ent ++= XhtmlEntities() -} - -/** - * Convenience method that instantiates, initializes and runs an `XhtmlParser`. - * - * @author Burak Emir - */ -object XhtmlParser { - def apply(source: Source): NodeSeq = new XhtmlParser(source).initialize.document() -} diff --git a/src/main/scala/scala/xml/persistent/CachedFileStorage.scala b/src/main/scala/scala/xml/persistent/CachedFileStorage.scala deleted file mode 100644 index 8a6cc6787..000000000 --- a/src/main/scala/scala/xml/persistent/CachedFileStorage.scala +++ /dev/null @@ -1,136 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml -package persistent - -import java.io.{ File, FileOutputStream } -import java.nio.ByteBuffer -import java.nio.channels.Channels -import java.lang.Thread - -import scala.collection.Iterator - -/** - * Mutable storage of immutable xml trees. Everything is kept in memory, - * with a thread periodically checking for changes and writing to file. - * - * To ensure atomicity, two files are used, `filename1` and `'$'+filename1`. - * The implementation switches between the two, deleting the older one - * after a complete dump of the database has been written. - * - * @author Burak Emir - */ -abstract class CachedFileStorage(private val file1: File) extends Thread { - - private val file2 = new File(file1.getParent, file1.getName + "$") - - /** - * Either equals `file1` or `file2`, references the next file in which - * updates will be stored. - */ - private var theFile: File = null - - private def switch() = { theFile = if (theFile == file1) file2 else file1; } - - /** this storage modified since last modification check */ - protected var dirty = false - - /** period between modification checks, in milliseconds */ - protected val interval = 1000 - - /** - * finds and loads the storage file. subclasses should call this method - * prior to any other, but only once, to obtain the initial sequence of nodes. - */ - protected def initialNodes: Iterator[Node] = (file1.exists, file2.exists) match { - case (false, false) => - theFile = file1 - Iterator.empty - case (true, true) if (file1.lastModified < file2.lastModified) => - theFile = file2 - load - case (true, _) => - theFile = file1 - load - case _ => - theFile = file2 - load - } - - /** returns an iterator over the nodes in this storage */ - def nodes: Iterator[Node] - - /** adds a node, setting this.dirty to true as a side effect */ - def +=(e: Node): Unit - - /** removes a tree, setting this.dirty to true as a side effect */ - def -=(e: Node): Unit - - /* loads and parses XML from file */ - private def load: Iterator[Node] = { - import scala.io.Source - import scala.xml.parsing.ConstructingParser - // println("[load]\nloading " + theFile) - val src = Source.fromFile(theFile) - // println("parsing " + theFile) - val res = ConstructingParser.fromSource(src, preserveWS = false).document.docElem(0) - switch() - // println("[load done]") - res.child.iterator - } - - /** saves the XML to file */ - private def save() = if (this.dirty) { - // println("[save]\ndeleting " + theFile) - theFile.delete() - // println("creating new " + theFile) - theFile.createNewFile() - val fos = new FileOutputStream(theFile) - val c = fos.getChannel() - - // @todo: optimize - val storageNode = { nodes.toList } - val w = Channels.newWriter(c, "utf-8") - XML.write(w, storageNode, "utf-8", xmlDecl = true, doctype = null) - - // println("writing to " + theFile) - - w.close - c.close - fos.close - dirty = false - switch() - // println("[save done]") - } - - /** - * Run method of the thread. remember to use `start()` to start a thread, - * not `run`. - */ - override def run = { - // println("[run]\nstarting storage thread, checking every " + interval + " ms") - while (true) { - Thread.sleep(this.interval.toLong) - save() - } - } - - /** - * Force writing of contents to the file, even if there has not been any - * update. - */ - def flush() = { - this.dirty = true - save() - } - - @deprecated("This method and its usages will be removed. Use a debugger to debug code.", "2.11") - def log(msg: String): Unit = {} -} diff --git a/src/main/scala/scala/xml/persistent/Index.scala b/src/main/scala/scala/xml/persistent/Index.scala deleted file mode 100644 index 312f5c797..000000000 --- a/src/main/scala/scala/xml/persistent/Index.scala +++ /dev/null @@ -1,16 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml -package persistent - -/** - * an Index returns some unique key that is part of a node - */ -abstract class Index[A] extends Function1[Node, A] {} diff --git a/src/main/scala/scala/xml/persistent/SetStorage.scala b/src/main/scala/scala/xml/persistent/SetStorage.scala deleted file mode 100644 index 8dd4d3ee7..000000000 --- a/src/main/scala/scala/xml/persistent/SetStorage.scala +++ /dev/null @@ -1,42 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml -package persistent - -import scala.collection.mutable -import java.io.File - -/** - * A persistent store with set semantics. This class allows to add and remove - * trees, but never contains two structurally equal trees. - * - * @author Burak Emir - */ -class SetStorage(file: File) extends CachedFileStorage(file) { - - private val theSet = mutable.HashSet[Node]() - - // initialize - - { - val it = super.initialNodes - dirty = it.hasNext - theSet ++= it - } - - /* forwarding methods to hashset*/ - - def +=(e: Node): Unit = synchronized { this.dirty = true; theSet += e } - - def -=(e: Node): Unit = synchronized { this.dirty = true; theSet -= e } - - def nodes = synchronized { theSet.iterator } - -} diff --git a/src/main/scala/scala/xml/pull/XMLEvent.scala b/src/main/scala/scala/xml/pull/XMLEvent.scala deleted file mode 100644 index 2c1ae267e..000000000 --- a/src/main/scala/scala/xml/pull/XMLEvent.scala +++ /dev/null @@ -1,60 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml -package pull - -/** - * An XML event for pull parsing. All events received during - * parsing will be one of the subclasses of this trait. - */ -trait XMLEvent - -/** - * An Element's start tag was encountered. - * @param pre prefix, if any, on the element. This is the `xs` in `foo`. - * @param label the name of the element, not including the prefix - * @param attrs any attributes on the element - */ -case class EvElemStart(pre: String, label: String, attrs: MetaData, scope: NamespaceBinding) extends XMLEvent - -/** - * An Element's end tag was encountered. - * @param pre prefix, if any, on the element. This is the `xs` in `foo`. - * @param label the name of the element, not including the prefix - */ -case class EvElemEnd(pre: String, label: String) extends XMLEvent - -/** - * A text node was encountered. - * @param text the text that was found - */ -case class EvText(text: String) extends XMLEvent - -/** - * An entity reference was encountered. - * @param entity the name of the entity, e.g. `gt` when encountering the entity `>` - */ -case class EvEntityRef(entity: String) extends XMLEvent - -/** - * A processing instruction was encountered. - * @param target the "PITarget" of the processing instruction. For the instruction ``, the target would - * be `foo` - * @param text the remainder of the instruction. For the instruction ``, the text would - * be `bar="baz"` - * @see [[http://www.w3.org/TR/REC-xml/#sec-pi]] - */ -case class EvProcInstr(target: String, text: String) extends XMLEvent - -/** - * A comment was encountered - * @param text the text of the comment - */ -case class EvComment(text: String) extends XMLEvent diff --git a/src/main/scala/scala/xml/pull/XMLEventReader.scala b/src/main/scala/scala/xml/pull/XMLEventReader.scala deleted file mode 100755 index b26301466..000000000 --- a/src/main/scala/scala/xml/pull/XMLEventReader.scala +++ /dev/null @@ -1,158 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml -package pull - -import scala.io.Source -import java.lang.Thread -import java.util.concurrent.LinkedBlockingQueue -import java.nio.channels.ClosedChannelException -import scala.xml.parsing.{ ExternalSources, MarkupHandler, MarkupParser } - -/** - * Main entry point into creating an event-based XML parser. Treating this - * as a [[scala.collection.Iterator]] will provide access to the generated events. - * @param src A [[scala.io.Source]] for XML data to parse - * - * @author Burak Emir - * @author Paul Phillips - */ -class XMLEventReader(src: Source) - extends scala.collection.AbstractIterator[XMLEvent] - with ProducerConsumerIterator[XMLEvent] { - - // We implement a pull parser as an iterator, but since we may be operating on - // a stream (e.g. XML over a network) there may be arbitrarily long periods when - // the queue is empty. Fortunately the ProducerConsumerIterator is ideally - // suited to this task, possibly because it was written for use by this class. - - // to override as necessary - val preserveWS = true - - override val MaxQueueSize = 1000 - protected case object POISON extends XMLEvent - val EndOfStream = POISON - - // thread machinery - private[this] val parser = new Parser(src) - private[this] val parserThread = new Thread(parser, "XMLEventReader") - parserThread.start - // enqueueing the poison object is the reliable way to cause the - // iterator to terminate; hasNext will return false once it sees it. - // Calling interrupt() on the parserThread is the only way we can get - // it to stop producing tokens since it's lost deep in document() - - // we cross our fingers the interrupt() gets to its target, but if it - // fails for whatever reason the iterator correctness is not impacted, - // only performance (because it will finish the entire XML document, - // or at least as much as it can fit in the queue.) - def stop() = { - produce(POISON) - parserThread.interrupt() - } - - private class Parser(val input: Source) extends MarkupHandler with MarkupParser with ExternalSources with Runnable { - val preserveWS = XMLEventReader.this.preserveWS - // track level for elem memory usage optimization - private var level = 0 - - // this is Parser's way to add to the queue - the odd return type - // is to conform to MarkupHandler's interface - def setEvent(es: XMLEvent*): NodeSeq = { - es foreach produce - NodeSeq.Empty - } - - override def elemStart(pos: Int, pre: String, label: String, attrs: MetaData, scope: NamespaceBinding) { - level += 1 - setEvent(EvElemStart(pre, label, attrs, scope)) - } - override def elemEnd(pos: Int, pre: String, label: String) { - setEvent(EvElemEnd(pre, label)) - level -= 1 - } - - // this is a dummy to satisfy MarkupHandler's API - // memory usage optimization return one for top level to satisfy - // MarkupParser.document() otherwise NodeSeq.Empty - private var ignoreWritten = false - final def elem(pos: Int, pre: String, label: String, attrs: MetaData, pscope: NamespaceBinding, empty: Boolean, nodes: NodeSeq): NodeSeq = - if (level == 1 && !ignoreWritten) { ignoreWritten = true; } else NodeSeq.Empty - - def procInstr(pos: Int, target: String, txt: String) = setEvent(EvProcInstr(target, txt)) - def comment(pos: Int, txt: String) = setEvent(EvComment(txt)) - def entityRef(pos: Int, n: String) = setEvent(EvEntityRef(n)) - def text(pos: Int, txt: String) = setEvent(EvText(txt)) - - override def run() { - curInput = input - interruptibly { this.initialize.document() } - setEvent(POISON) - } - } -} - -// An iterator designed for one or more producers to generate -// elements, and a single consumer to iterate. Iteration will continue -// until closeIterator() is called, after which point producers -// calling produce() will receive interruptions. -// -// Since hasNext may block indefinitely if nobody is producing, -// there is also an available() method which will return true if -// the next call hasNext is guaranteed not to block. -// -// This is not thread-safe for multiple consumers! -trait ProducerConsumerIterator[T >: Null] extends Iterator[T] { - // abstract - iterator-specific distinguished object for marking eos - val EndOfStream: T - - // defaults to unbounded - override to positive Int if desired - val MaxQueueSize = -1 - - def interruptibly[T](body: => T): Option[T] = try Some(body) catch { - case _: InterruptedException => - Thread.currentThread.interrupt(); None - case _: ClosedChannelException => None - } - - private[this] lazy val queue = - if (MaxQueueSize < 0) new LinkedBlockingQueue[T]() - else new LinkedBlockingQueue[T](MaxQueueSize) - private[this] var buffer: T = _ - private def fillBuffer() = { - buffer = interruptibly(queue.take) getOrElse EndOfStream - isElement(buffer) - } - private def isElement(x: T) = x != null && x != EndOfStream - private def eos() = buffer == EndOfStream - - // public producer interface - this is the only method producers call, so - // LinkedBlockingQueue's synchronization is all we need. - def produce(x: T): Unit = if (!eos) interruptibly(queue put x) - - // consumer/iterator interface - we need not synchronize access to buffer - // because we required there to be only one consumer. - def hasNext = !eos && (buffer != null || fillBuffer) - - def next() = { - if (eos()) throw new NoSuchElementException("ProducerConsumerIterator") - if (buffer == null) fillBuffer() - - drainBuffer() - } - - def available() = isElement(buffer) || isElement(queue.peek) - - private def drainBuffer() = { - assert(!eos) - val res = buffer - buffer = null - res - } -} diff --git a/src/main/scala/scala/xml/pull/package.scala b/src/main/scala/scala/xml/pull/package.scala deleted file mode 100644 index 0e3019446..000000000 --- a/src/main/scala/scala/xml/pull/package.scala +++ /dev/null @@ -1,42 +0,0 @@ -package scala -package xml - -/** - * Classes needed to view an XML document as a series of events. The document - * is parsed by an [[scala.xml.pull.XMLEventReader]] instance. You can treat it as - * an [[scala.collection.Iterator]] to retrieve the events, which are all - * subclasses of [[scala.xml.pull.XMLEvent]]. - * - * {{{ - * scala> val source = Source.fromString(""" - * - * - * ]>Hello&bar;>""") - * - * source: scala.io.Source = non-empty iterator - * - * scala> val reader = new XMLEventReader(source) - * reader: scala.xml.pull.XMLEventReader = non-empty iterator - * - * scala> reader.foreach{ println(_) } - * EvProcInstr(instruction,custom value="customvalue") - * EvText( - * ) - * EvElemStart(null,foo,,) - * EvText(Hello) - * EvComment( this is a comment ) - * EvElemStart(null,bar,,) - * EvText(BAR) - * EvElemEnd(null,bar) - * EvElemStart(null,bar,,) - * EvEntityRef(gt) - * EvElemEnd(null,bar) - * EvElemEnd(null,foo) - * EvText( - * - * ) - * - * }}} - */ -package object pull diff --git a/src/main/scala/scala/xml/transform/BasicTransformer.scala b/src/main/scala/scala/xml/transform/BasicTransformer.scala deleted file mode 100644 index 8cad1ef22..000000000 --- a/src/main/scala/scala/xml/transform/BasicTransformer.scala +++ /dev/null @@ -1,60 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml -package transform - -/** - * A class for XML transformations. - * - * @author Burak Emir - * @version 1.0 - */ -abstract class BasicTransformer extends Function1[Node, Node] { - protected def unchanged(n: Node, ns: Seq[Node]) = - ns.length == 1 && (ns.head == n) - - /** - * Call transform(Node) for each node in ns, append results - * to NodeBuffer. - */ - def transform(it: Iterator[Node], nb: NodeBuffer): Seq[Node] = - it.foldLeft(nb)(_ ++= transform(_)).toSeq - - /** - * Call transform(Node) to each node in ns, yield ns if nothing changes, - * otherwise a new sequence of concatenated results. - */ - def transform(ns: Seq[Node]): Seq[Node] = { - val (xs1, xs2) = ns span (n => unchanged(n, transform(n))) - - if (xs2.isEmpty) ns - else xs1 ++ transform(xs2.head) ++ transform(xs2.tail) - } - - def transform(n: Node): Seq[Node] = { - if (n.doTransform) n match { - case Group(xs) => Group(transform(xs)) // un-group the hack Group tag - case _ => - val ch = n.child - val nch = transform(ch) - - if (ch eq nch) n - else Elem(n.prefix, n.label, n.attributes, n.scope, nch.isEmpty, nch: _*) - } - else n - } - - def apply(n: Node): Node = { - val seq = transform(n) - if (seq.length > 1) - throw new UnsupportedOperationException("transform must return single node for root") - else seq.head - } -} diff --git a/src/main/scala/scala/xml/transform/RewriteRule.scala b/src/main/scala/scala/xml/transform/RewriteRule.scala deleted file mode 100644 index eee3c6315..000000000 --- a/src/main/scala/scala/xml/transform/RewriteRule.scala +++ /dev/null @@ -1,27 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml -package transform - -/** - * A RewriteRule, when applied to a term, yields either - * the result of rewriting the term or the term itself if the rule - * is not applied. - * - * @author Burak Emir - * @version 1.0 - */ -abstract class RewriteRule extends BasicTransformer { - /** a name for this rewrite rule */ - val name = this.toString() - override def transform(ns: Seq[Node]): Seq[Node] = super.transform(ns) - override def transform(n: Node): Seq[Node] = n -} - diff --git a/src/main/scala/scala/xml/transform/RuleTransformer.scala b/src/main/scala/scala/xml/transform/RuleTransformer.scala deleted file mode 100644 index 3a222ba75..000000000 --- a/src/main/scala/scala/xml/transform/RuleTransformer.scala +++ /dev/null @@ -1,16 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml -package transform - -class RuleTransformer(rules: RewriteRule*) extends BasicTransformer { - override def transform(n: Node): Seq[Node] = - rules.foldLeft(super.transform(n)) { (res, rule) => rule transform res } -} diff --git a/test/files/jvm/backendBugUnapply.check b/test/files/jvm/backendBugUnapply.check deleted file mode 100644 index 9d1e7b29c..000000000 --- a/test/files/jvm/backendBugUnapply.check +++ /dev/null @@ -1,2 +0,0 @@ -baz -null diff --git a/test/files/jvm/backendBugUnapply.scala b/test/files/jvm/backendBugUnapply.scala deleted file mode 100644 index 45ee6f7d4..000000000 --- a/test/files/jvm/backendBugUnapply.scala +++ /dev/null @@ -1,17 +0,0 @@ -object Test { - import scala.xml.{Node,UnprefixedAttribute} - - def domatch(x:Node) = - x match { - case Node("foo", UnprefixedAttribute("bar", z, _), _*) => z - case _ => null - } - - def main(args: Array[String]): Unit = { - println(domatch()) - println(domatch()) - // - // assert(domatch().toString == "baz") - // assert(domatch() == null)//, domatch()) - } -} diff --git a/test/files/jvm/t0632.check b/test/files/jvm/t0632.check deleted file mode 100755 index 681bc9da9..000000000 --- a/test/files/jvm/t0632.check +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/test/files/jvm/t0632.scala b/test/files/jvm/t0632.scala deleted file mode 100644 index a2bb5aa7f..000000000 --- a/test/files/jvm/t0632.scala +++ /dev/null @@ -1,22 +0,0 @@ -object Test { -import scala.io.Source.fromString -import scala.xml.parsing.ConstructingParser.fromSource -import scala.xml.TopScope - def parse(s:String) = fromSource(fromString(s), false).element(TopScope) - def main(argv : Array[String]) : Unit = { - - println(parse("")) - println(xml.XML.loadString("")) - println() - println() - - println(xml.XML.loadString("")) - println(parse("")) - println() - println() - println(xml.XML.loadString("")) - println(parse("")) - println() - println() - } -} diff --git a/test/files/jvm/t1118.check b/test/files/jvm/t1118.check deleted file mode 100755 index d676b413c..000000000 --- a/test/files/jvm/t1118.check +++ /dev/null @@ -1,11 +0,0 @@ - - - - - -is pretty cool - - - - - diff --git a/test/files/jvm/t1118.scala b/test/files/jvm/t1118.scala deleted file mode 100755 index 3c8654724..000000000 --- a/test/files/jvm/t1118.scala +++ /dev/null @@ -1,21 +0,0 @@ -import scala.xml._ - -object Test { - def main(args: Array[String]) { - println( - - - - -is pretty cool -) - - println(Elem(null, "emptiness", Null, TopScope, false) ++ Text(" ") ++ Comment("programmatic long")) - - println(Elem(null, "vide", Null, TopScope, true) ++ Text(" ") ++ Comment("programmatic short")) - - println(Elem(null, "elem", Attribute("attr", Text("value"), Null), TopScope, true) ++ Text(" ") ++ Comment ("programmatic short with attribute")) - - println(Elem(null, "elem2", Attribute("attr2", Text("value2"), Null), TopScope, false) ++ Text(" ") ++ Comment ("programmatic long with attribute")) - } -} \ No newline at end of file diff --git a/test/files/jvm/t560bis.check b/test/files/jvm/t560bis.check deleted file mode 100644 index 91eb4c19a..000000000 --- a/test/files/jvm/t560bis.check +++ /dev/null @@ -1,2 +0,0 @@ -cool! -cool! diff --git a/test/files/jvm/t560bis.scala b/test/files/jvm/t560bis.scala deleted file mode 100644 index 21eb8dde2..000000000 --- a/test/files/jvm/t560bis.scala +++ /dev/null @@ -1,21 +0,0 @@ -object Test { -import scala.xml._; - - def bar(args: Seq[String]) = args match { - case Seq(a,b,c,d @ _*) => Console.println("cool!") - case _ => Console.println("bah") - } - def foo(args: List[String]) = - Elem(null,"bla",Null, TopScope, minimizeEmpty = true, (args map {x => Text(x)}):_*) match { - case Elem(_,_,_,_,Text("1"),_*) => - Console.println("cool!") - case _ => - Console.println("bah") - } - - def main(args: Array[String]) = { - val li = List("1","2","3","4") - bar(li) - foo(li) - } -} diff --git a/test/files/jvm/unittest_xml.scala b/test/files/jvm/unittest_xml.scala deleted file mode 100644 index 106334e62..000000000 --- a/test/files/jvm/unittest_xml.scala +++ /dev/null @@ -1,101 +0,0 @@ -import scala.xml.{ MetaData, Null, Utility, PrefixedAttribute, UnprefixedAttribute } - -object Test { - - def main(args:Array[String]) = { - MetaDataTest.run() - UtilityTest.run() - } - - object MetaDataTest { - - import scala.xml.{ TopScope, NamespaceBinding, Node, Atom, Text } - - def domatch(x:Node): Node = { - x match { - case Node("foo", md @ UnprefixedAttribute(_, value, _), _*) if !value.isEmpty => - md("bar")(0) - case _ => new Atom(3) - } - } - - def run() { - - var x: MetaData = Null - var s: NamespaceBinding = TopScope - - // testing method def apply(uri:String, scp:NamespaceBinding, k:String): Seq[Node] - // def apply(k:String): Seq[Node] - - assert(null == x("za://foo.com", s, "bar" ), "absent element (prefixed) 1") - assert(null == x("bar"), "absent element (unprefix) 1") - - assert(None == x.get("za://foo.com", s, "bar" ), "absent element (prefixed) 2") - assert(None == x.get("bar"), "absent element (unprefix) 2") - - x = new PrefixedAttribute("zo","bar", new Atom(42), x) - s = new NamespaceBinding("zo","za://foo.com",s) - - assert(new Atom(42) == x("za://foo.com", s, "bar" ), "present element (prefixed) 3") - assert(null == x("bar"), "present element (unprefix) 3") - - assert(Some(new Atom(42)) == x.get("za://foo.com", s, "bar" ), "present element (prefixed) 4") - assert(None == x.get("bar"), "present element (unprefix) 4") - - x = new UnprefixedAttribute("bar","meaning", x) - - assert(null == x(null, s, "bar"), "present element (prefixed) 5") - assert(Text("meaning") == x("bar"), "present element (unprefix) 5") - - assert(None == x.get(null, s, "bar" ), "present element (prefixed) 6") - assert(Some(Text("meaning")) == x.get("bar"), "present element (unprefix) 6") - - val z = - val z2 = - - assert(Text("gar") == domatch(z), "attribute extractor 1") - assert(new Atom(3) == domatch(z2), "attribute extractor 2") - - } - } - - object UtilityTest { - def run() { - assert(Utility.isNameStart('b')) - assert(!Utility.isNameStart(':')) - - val x = - - - - val y = xml.Utility.trim(x) - - assert(1 == (y match { case => 1 }), "trim 1") - - val x2 = - a b b a - - - val y2 = xml.Utility.trim(x2) - - assert(2 == (y2 match { case a b b a => 2 }), "trim 2") - - val z = '' - val z1 = z.toString - - assert("''" == z1, "apos unescaped") - - val q = xml.Utility.sort() - assert(" a=\"2\" g=\"3\" j=\"2\" oo=\"2\"" == xml.Utility.sort(q.attributes).toString) - - val pp = new xml.PrettyPrinter(80,5) - assert("" == pp.format(q)) - - - - - .hashCode // Bug #777 - } - } - -} diff --git a/test/files/jvm/xml01.check b/test/files/jvm/xml01.check deleted file mode 100755 index d78e6df41..000000000 --- a/test/files/jvm/xml01.check +++ /dev/null @@ -1,8 +0,0 @@ -equality -xpath \ -xpath \\ DESCENDANTS -Peter BunemanDan SuciuData on ze web --- group nodes - - -attribute value normalization diff --git a/test/files/jvm/xml01.scala b/test/files/jvm/xml01.scala deleted file mode 100644 index 2b456f5ff..000000000 --- a/test/files/jvm/xml01.scala +++ /dev/null @@ -1,182 +0,0 @@ -import java.io.StringReader -import org.xml.sax.InputSource - -import scala.xml._ - -object Test extends App { - def Elem(prefix: String, label: String, attributes: MetaData, scope: NamespaceBinding, child: Node*): Elem = - scala.xml.Elem.apply(prefix, label, attributes, scope, minimizeEmpty = true, child: _*) - val e: scala.xml.MetaData = Null //Node.NoAttributes - val sc: scala.xml.NamespaceBinding = TopScope - - val xmlFile1 = ""; - val isrc1 = new InputSource(new StringReader(xmlFile1)) - val parsedxml1 = XML.load(isrc1) - val isrc11 = new InputSource(new StringReader(xmlFile1)) - val parsedxml11 = XML.load(isrc11) - - val c = new Node { - def label = "hello" - override def hashCode() = - Utility.hashCode(prefix, label, attributes.hashCode(), scope.hashCode(), child); - def child = Elem(null, "world", e, sc); - //def attributes = e; - override def text = "" - } - - println("equality") - assert(c == parsedxml11) - assert(parsedxml1 == parsedxml11) - assert(List(parsedxml1) sameElements List(parsedxml11)) - assert(Array(parsedxml1).toList sameElements List(parsedxml11)) - - val x2 = "Peter BunemanDan SuciuData on ze web"; - - val i = new InputSource(new StringReader(x2)) - val x2p = XML.load(i) - - assert(x2p == Elem(null, "book" , e, sc, - Elem(null, "author", e, sc,Text("Peter Buneman")), - Elem(null, "author", e, sc,Text("Dan Suciu")), - Elem(null, "title" , e, sc,Text("Data on ze web")))) - - val xmlFile2 = "Peter BunemanDan SuciuData on ze webJohn MitchellFoundations of Programming Languages"; - val isrc2 = new InputSource(new StringReader(xmlFile2)) - val parsedxml2 = XML.load(isrc2) - - println("xpath \\") - - assert(parsedxml1 \ "_" sameElements List(Elem(null,"world", e, sc))) - - assert(parsedxml1 \ "world" sameElements List(Elem(null,"world", e, sc))) - - assert( - (parsedxml2 \ "_") sameElements List( - Elem(null,"book", e, sc, - Elem(null,"author", e, sc, Text("Peter Buneman")), - Elem(null,"author", e, sc, Text("Dan Suciu")), - Elem(null,"title" , e, sc, Text("Data on ze web"))), - Elem(null,"book",e,sc, - Elem(null,"author",e,sc,Text("John Mitchell")), - Elem(null,"title",e,sc,Text("Foundations of Programming Languages")))) - ) - assert((parsedxml2 \ "author").isEmpty) - - assert( - (parsedxml2 \ "book") sameElements List( - Elem(null,"book",e,sc, - Elem(null,"author", e, sc, Text("Peter Buneman")), - Elem(null,"author", e, sc, Text("Dan Suciu")), - Elem(null,"title" , e, sc, Text("Data on ze web"))), - Elem(null,"book",e,sc, - Elem(null,"author", e, sc, Text("John Mitchell")), - Elem(null,"title" , e, sc, Text("Foundations of Programming Languages"))) - ) - ) - - assert( - (parsedxml2 \ "_" \ "_") sameElements List( - Elem(null,"author", e, sc, Text("Peter Buneman")), - Elem(null,"author", e, sc, Text("Dan Suciu")), - Elem(null,"title" , e, sc, Text("Data on ze web")), - Elem(null,"author", e, sc, Text("John Mitchell")), - Elem(null,"title" , e, sc, Text("Foundations of Programming Languages")) - ) - ) - - assert( - (parsedxml2 \ "_" \ "author") sameElements List( - Elem(null,"author", e, sc, Text("Peter Buneman")), - Elem(null,"author", e, sc, Text("Dan Suciu")), - Elem(null,"author", e, sc, Text("John Mitchell")) - ) - ) - - assert((parsedxml2 \ "_" \ "_" \ "author").isEmpty) - - Console.println("xpath \\\\ DESCENDANTS"); - - assert( - (parsedxml2 \\ "author") sameElements List( - Elem(null,"author", e, sc, Text("Peter Buneman")), - Elem(null,"author", e, sc, Text("Dan Suciu")), - Elem(null,"author", e, sc, Text("John Mitchell")) - ) - ) - - assert( - (parsedxml2 \\ "title") sameElements List( - Elem(null,"title", e, sc, Text("Data on ze web")), - Elem(null,"title", e, sc, Text("Foundations of Programming Languages"))) - ) - - - println( - (parsedxml2 \\ "book" ){ n:Node => (n \ "title") xml_== "Data on ze web" } - ) - - assert( - ((new NodeSeq { val theSeq = List( parsedxml2 ) }) \\ "_") sameElements List( - Elem(null,"bib",e,sc, - Elem(null,"book",e,sc, - Elem(null, "author", e, sc, Text("Peter Buneman")), - Elem(null, "author", e, sc, Text("Dan Suciu")), - Elem(null, "title" , e, sc, Text("Data on ze web"))), - Elem(null,"book",e,sc, - Elem(null,"author",e,sc,Text("John Mitchell")), - Elem(null,"title",e,sc,Text("Foundations of Programming Languages")))), - Elem(null,"book",e,sc, - Elem(null,"author",e,sc,Text("Peter Buneman")), - Elem(null,"author",e,sc,Text("Dan Suciu")), - Elem(null,"title",e,sc,Text("Data on ze web"))), - Elem(null,"author",e,sc,Text("Peter Buneman")), - Elem(null,"author",e,sc,Text("Dan Suciu")), - Elem(null,"title",e,sc,Text("Data on ze web")), - Elem(null,"book",e,sc, - Elem(null,"author",e,sc,Text("John Mitchell")), - Elem(null,"title",e,sc,Text("Foundations of Programming Languages"))), - Elem(null,"author",e,sc,Text("John Mitchell")), - Elem(null,"title",e,sc,Text("Foundations of Programming Languages")) - ) - ) - - // test group node - Console println "-- group nodes" - val zx1: Node = Group { } - val zy1 = {zx1} - Console println zy1.toString() - - val zx2: Node = Group { List(,zy1,zx1) } - Console println zx2.toString() - - val zz1 = - - assert(zx1 xml_== zz1) - assert(zz1.length == 3) - - // unparsed - - println("attribute value normalization") - val xmlAttrValueNorm = ""; - { - val isrcA = new InputSource( new StringReader(xmlAttrValueNorm) ); - val parsedxmlA = XML.load(isrcA); - val c = (parsedxmlA \ "@nom").text.charAt(0); - assert(c == '\u015e'); - } - // buraq: if the following test fails with 'character x not allowed', it is - // related to the mutable variable in a closures in MarkupParser.parsecharref - { - val isr = scala.io.Source.fromString(xmlAttrValueNorm); - val pxmlB = scala.xml.parsing.ConstructingParser.fromSource(isr,false); - val parsedxmlB = pxmlB.element(TopScope); - val c = (parsedxmlB \ "@nom").text.charAt(0); - assert(c == '\u015e'); - } - - // #60 test by round trip - - val p = scala.xml.parsing.ConstructingParser.fromSource(scala.io.Source.fromString(""),true) - val n = p.element(new scala.xml.NamespaceBinding("bar","BAR",scala.xml.TopScope))(0) - assert( n.attributes.get("BAR", n, "attr").nonEmpty) -} diff --git a/test/files/jvm/xml02.scala b/test/files/jvm/xml02.scala deleted file mode 100644 index b830a0e69..000000000 --- a/test/files/jvm/xml02.scala +++ /dev/null @@ -1,78 +0,0 @@ -object Test { - - def main(args: Array[String]) { - XmlEx.run() - XmlEy.run() - XmlPat.run() - DodgyNamespace.run() - } - - import scala.xml.{NodeSeq, Utility} - import NodeSeq.seqToNodeSeq - - val ax = - - - - val cx = - crazy text world - - - val bx = - - object XmlEx { - - def run() { - assert((ax \ "@foo") xml_== "bar") // uses NodeSeq.view! - assert((ax \ "@foo") xml_== xml.Text("bar")) // dto. - assert((bx \ "@foo") xml_== "bar&x") // dto. - assert((bx \ "@foo") xml_sameElements List(xml.Text("bar&x"))) - assert("" == bx.toString) - } - } - - object XmlEy { - def run() { - val z = ax \ "@{the namespace from outer space}foo" - assert((ax \ "@{the namespace from outer space}foo") xml_== "baz") - assert((cx \ "@{the namespace from outer space}foo") xml_== "baz") - - try { - ax \ "@" - assert(false) - } catch { - case _: IllegalArgumentException => - } - try { - ax \ "@{" - assert(false) - } catch { - case _: IllegalArgumentException => - } - try { - ax \ "@{}" - assert(false) - } catch { - case _: IllegalArgumentException => - } - - } - } - - object XmlPat { - def run() { - assert( match { case => true; case _ => false; }) - assert( match { case => true; case _ => false; }); - assert(Utility.trim(cx) match { case n @ crazy text world if (n \ "@foo") xml_== "bar" => true; }) - assert(Utility.trim(cx) match { case n @ crazy text world if (n \ "@foo") xml_== "bar" => true; }) - } - } - - object DodgyNamespace { - def run() { - val x = - assert(x.toString.matches(".*xmlns:dog=\"http://dog.com\".*")); - } - } - -} diff --git a/test/files/jvm/xml03syntax.check b/test/files/jvm/xml03syntax.check deleted file mode 100755 index 599cbad68..000000000 --- a/test/files/jvm/xml03syntax.check +++ /dev/null @@ -1,27 +0,0 @@ -true -true -true -world -true -1.5 -true -5 -true -true -true -5 -true -27 -true -1 2 3 4 -1 -2 -3 -4 -2 4 -2 -4 - -node=, key=Some(hello) -node=, key=None -Š diff --git a/test/files/jvm/xml03syntax.scala b/test/files/jvm/xml03syntax.scala deleted file mode 100644 index 41663681c..000000000 --- a/test/files/jvm/xml03syntax.scala +++ /dev/null @@ -1,97 +0,0 @@ -import scala.xml._ - -object Test { - - private def handle[A](x: Node): A = { - println(x) - x.child(0).asInstanceOf[Atom[A]].data - } - - def main(args: Array[String]) { - test1() - test2() - test3() - } - - private def test1() { - val xNull = {null} // these used to be Atom(unit), changed to empty children - - println(xNull.child sameElements Nil) - - val x0 = {} // these used to be Atom(unit), changed to empty children - val x00 = { } // dto. - - val xa = { "world" } - - - println(x0.child sameElements Nil) - println(x00.child sameElements Nil) - println(handle[String](xa) == "world") - - val xb = { 1.5 } - - println(handle[Double](xb) == 1.5) - - val xc = { 5 } - - println(handle[Int](xc) == 5) - - val xd = { true } - - println(handle[Boolean](xd) == true) - - val xe = { 5:Short } - - println(handle[Short](xe) == (5:Short)) - - val xf = { val x = 27; x } - - println(handle[Int](xf) == 27) - - val xg = { List(1,2,3,4) } - - println(xg) - for (z <- xg.child) { - println(z.toString() + {if (z.isInstanceOf[Text]) "(is text node ' ')" else ""}) - } - - val xh = { for(x <- List(1,2,3,4) if x % 2 == 0) yield x } - - println(xh) - for (z <- xh.child) { - println(z.toString() + {if (z.isInstanceOf[Text]) "(is text node ' ')" else ""}) - } - println - } - - /** see SVN r13821 (emir): support for , - * so that Options can be used for optional attributes. - */ - private def test2() { - val x1: Option[Seq[Node]] = Some(hello) - val n1 = ; - println("node="+n1+", key="+n1.attribute("key")) - - val x2: Option[Seq[Node]] = None - val n2 = ; - println("node="+n2+", key="+n2.attribute("key")) - } - - private def test3() { - // this demonstrates how to handle entities - val s = io.Source.fromString(" ") - object parser extends xml.parsing.ConstructingParser(s, false /*ignore ws*/) { - override def replacementText(entityName: String): io.Source = { - entityName match { - case "nbsp" => io.Source.fromString("\u0160"); - case _ => super.replacementText(entityName); - } - } - nextch; // !!important, to initialize the parser - } - val parsed = parser.element(TopScope) // parse the source as element - // alternatively, we could call document() - println(parsed) - } - -} diff --git a/test/files/jvm/xml04embed.check b/test/files/jvm/xml04embed.check deleted file mode 100644 index e71e64514..000000000 --- a/test/files/jvm/xml04embed.check +++ /dev/null @@ -1,3 +0,0 @@ -{ -} -{}{}{} diff --git a/test/files/jvm/xml04embed.scala b/test/files/jvm/xml04embed.scala deleted file mode 100644 index fa453e429..000000000 --- a/test/files/jvm/xml04embed.scala +++ /dev/null @@ -1,10 +0,0 @@ -object Test { - def main(args: Array[String]) { - val ya = {{ - println(ya.text) - val ua = }} - println(ua.text) - val za = {{}}{{}}{{}} - println(za.text) - } -} diff --git a/test/files/jvm/xmlattr.check b/test/files/jvm/xmlattr.check deleted file mode 100644 index a87420d86..000000000 --- a/test/files/jvm/xmlattr.check +++ /dev/null @@ -1,18 +0,0 @@ -true -true -true -true -true -true -removal of duplicates for unprefixed attributes in append = 1 -true -true -true -true -true -true -true -true -true - - diff --git a/test/files/jvm/xmlattr.scala b/test/files/jvm/xmlattr.scala deleted file mode 100644 index 6423268ba..000000000 --- a/test/files/jvm/xmlattr.scala +++ /dev/null @@ -1,70 +0,0 @@ -import xml.{ NodeSeq, Null, Text, UnprefixedAttribute } - -object Test { - - def main(args: Array[String]) { - UnprefixedAttributeTest() - AttributeWithOptionTest() - AttributeOutputTest() - AttributeOperatorTest() - } - - object UnprefixedAttributeTest { - def apply() { - val x = new UnprefixedAttribute("foo","bar", Null) - println(Some(Text("bar")) == x.get("foo")) - println(Text("bar") == x("foo")) - println(None == x.get("no_foo")) - println(null == x("no_foo")) - - val y = x.remove("foo") - println(Null == y) - - val z = new UnprefixedAttribute("foo", null:NodeSeq, x) - println(None == z.get("foo")) - - var appended = x append x append x append x - var len = 0; while (appended ne Null) { - appended = appended.next - len = len + 1 - } - println("removal of duplicates for unprefixed attributes in append = " + len) - } - } - - object AttributeWithOptionTest { - def apply() { - val x = new UnprefixedAttribute("foo", Some(Text("bar")), Null) - - println(Some(Text("bar")) == x.get("foo")) - println(Text("bar") == x("foo")) - println(None == x.get("no_foo")) - println(null == x("no_foo")) - - val attr1 = Some(Text("foo value")) - val attr2 = None - val y = - println(Some(Text("foo value")) == y.attributes.get("foo")); - println(Text("foo value") == y.attributes("foo")) - println(None == y.attributes.get("bar")) - println(null == y.attributes("bar")) - - val z = new UnprefixedAttribute("foo", None, x) - println(None == z.get("foo")) - } - } - - object AttributeOutputTest { - def apply() { - println() - println() - } - } - - object AttributeOperatorTest { - def apply() { - val xml = - assert(xml \@ "bar" == "apple") - } - } -} diff --git a/test/files/jvm/xmlmore.check b/test/files/jvm/xmlmore.check deleted file mode 100644 index 29f144c89..000000000 --- a/test/files/jvm/xmlmore.check +++ /dev/null @@ -1,10 +0,0 @@ - - - - "Come, come again, whoever you are, come! -Heathen, fire worshipper or idolatrous, come! -Come even if you broke your penitence a hundred times, -Ours is the portal of hope, come as you are." - Mevlana Celaleddin Rumi -
-End Test diff --git a/test/files/jvm/xmlmore.scala b/test/files/jvm/xmlmore.scala deleted file mode 100644 index 04d0a6c75..000000000 --- a/test/files/jvm/xmlmore.scala +++ /dev/null @@ -1,29 +0,0 @@ -object myBreak extends scala.xml.Unparsed("
") - -object Test extends App { - val com = - val pi = - val crz = - - val nazim = {myBreak} // shows use of unparsed - - Console println com - Console println pi - Console println crz // this guy will escaped, and rightly so - Console println nazim - Console println "End Test" - - match { - case scala.xml.QNode("gaga","foo",md,child@_*) => - } - - match { - case scala.xml.Node("foo",md,child@_*) => - } - -} diff --git a/test/files/jvm/xmlpull.scala b/test/files/jvm/xmlpull.scala deleted file mode 100644 index 9ba7d4cf0..000000000 --- a/test/files/jvm/xmlpull.scala +++ /dev/null @@ -1,31 +0,0 @@ -import scala.xml._ -import scala.xml.pull._ -import scala.io.Source - -object Test { - - val src = Source.fromString("!") - - def main(args: Array[String]) { - var er = new XMLEventReader(src) - er.next match { - case EvElemStart(_, "hello", _, _) => //println("1") - } - er.next match { - case EvElemStart(_, "world", _, _) => //println("2") - } - er.next match { - case EvElemEnd(_, "world") => //println("3") - } - er.next match { - case EvText("!") => //println("4") - } - er.next match { - case EvElemEnd(_, "hello") => //println("5") - } - // you get the picture... - er.stop // allow thread to be garbage-collected - //println("6") - } -} - diff --git a/test/files/jvm/xmlstuff.check b/test/files/jvm/xmlstuff.check deleted file mode 100644 index e1222479f..000000000 --- a/test/files/jvm/xmlstuff.check +++ /dev/null @@ -1,22 +0,0 @@ -NodeSeq - - Blabla - Hallo Welt. - - Blubabla - Hello Blu - - Blubabla - rem 2 - -List(Blabla) - - John - Elm Street - Dolphin City - +41 21 693 68 67 - +41 79 602 23 23 - -namespaces -validation - elements -validation - attributes diff --git a/test/files/jvm/xmlstuff.scala b/test/files/jvm/xmlstuff.scala deleted file mode 100644 index 45234c713..000000000 --- a/test/files/jvm/xmlstuff.scala +++ /dev/null @@ -1,181 +0,0 @@ -import java.io.StringReader -import org.xml.sax.InputSource -import scala.xml.{Node, NodeSeq, Elem, Text, XML} - -object Test { - - /** returns true if exception was thrown */ - def catcher(att: Function1[Unit, scala.xml.MetaData]): Boolean = { - var ex = false - try { - att.apply({}) - } catch { - case scala.xml.MalformedAttributeException(msg) => - println(msg) - ex = true - } - ex - } - - def main(args: Array[String]) { - - println("NodeSeq") - - val p = - - - - ; - - val pelems_1 = for (x <- p \ "bar"; y <- p \ "baz" ) yield { - Text(x.attributes("value").toString + y.attributes("bazValue").toString+ "!") - }; - val pelems_2 = new NodeSeq { val theSeq = List(Text("38!"),Text("58!")) }; - assert(pelems_1 sameElements pelems_2) - - assert(Text("8") sameElements (p \\ "@bazValue")) - - val books = - - Blabla - Blubabla - Baaaaaaalabla - ; - - val reviews = - - Blabla - - Hallo Welt. - - - Blubabla - - Hello Blu - - - Blubabla - - rem 2 - - - ; - - println( new scala.xml.PrettyPrinter(80, 5).formatNodes ( - for (t <- books \\ "title"; - r <- reviews \\ "entry" - if (r \ "title") xml_== t) yield - - { t } - { r \ "remarks" } - - )); - - // example - println( - for (t @ Blabla <- new NodeSeq { val theSeq = books.child }.toList) - yield t - ); - val phoneBook = - - - This is the phonebook of the - ACME corporation. - - - John - +41 21 693 68 67 - +41 79 602 23 23 - - ; - - - val addrBook = - - - This is the addressbook of the - ACME corporation. - - - John - Elm Street - Dolphin City - - ; - - println( new scala.xml.PrettyPrinter(80, 5).formatNodes ( - for (t <- addrBook \\ "entry"; - r <- phoneBook \\ "entry" - if (t \ "name") xml_== (r \ "name")) yield - - { t.child } - { r \ "phone" } - - )); - - - /* namespaces */ - // begin tmp - println("namespaces") - val cuckoo = - - - ; - assert(cuckoo.namespace == "http://cuckoo.com") - for (n <- cuckoo \ "_" ) { - //println("n = "+n); - //println("n.prefix = "+n.prefix); - //.println("n.scope = "+n.scope); - assert( n.namespace == "http://cuckoo.com") - } - - println("validation - elements") - val vtor = new scala.xml.dtd.ElementValidator(); - { - import scala.xml.dtd.ELEMENTS - import scala.xml.dtd.ContentModel._ - vtor.setContentModel( - ELEMENTS( - Sequ( - Letter(ElemName("bar")), - Star(Letter(ElemName("baz"))) ))); - - } - assert(vtor( )) - - { - import scala.xml.dtd.MIXED - import scala.xml.dtd.ContentModel._ - - vtor.setContentModel( - MIXED( - Alt(Letter(ElemName("bar")), - Letter(ElemName("baz")), - Letter(ElemName("bal"))))); - } - - assert(vtor( )) - assert(vtor(abcdedgh )) - assert(!vtor( )) - - println("validation - attributes") - vtor.setContentModel(null) - vtor.setMetaData(List()) - assert(!vtor( )) - - { - import scala.xml.dtd._ - vtor setMetaData List(AttrDecl("bar", "CDATA", IMPLIED)) - } - assert(!vtor()) - assert(vtor()) - - { - import scala.xml.dtd._ - vtor.setMetaData(List(AttrDecl("bar","CDATA",REQUIRED))) - } - assert(!vtor( )) - assert( vtor( )) - - } -} diff --git a/test/files/neg/t1011.check b/test/files/neg/t1011.check deleted file mode 100644 index d9c812354..000000000 --- a/test/files/neg/t1011.check +++ /dev/null @@ -1,4 +0,0 @@ -t1011.scala:8: error: not found: value entity -
{Text(entity)} - ^ -one error found diff --git a/test/files/neg/t1011.scala b/test/files/neg/t1011.scala deleted file mode 100644 index 57a6ad7b4..000000000 --- a/test/files/neg/t1011.scala +++ /dev/null @@ -1,127 +0,0 @@ -package test; -import scala.xml._; - -abstract class Test { - //val entity : String; - def primitiveHeader : NodeSeq = - Group({ -
{Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)} - {Text(entity)}
- } ++ // 3 seconds - {}++ // 5 seconds - {}++ // 10 seconds - {}++ // 20 seconds - {}++ // 40 seconds - {}++ // 40 seconds - {}++ // 40 seconds - {}++ // 40 seconds - {}++ // 40 seconds - {}++ // 40 seconds - {}++ // 40 seconds - {}++ // 40 seconds - {}++ // 40 seconds - {}++ // 40 seconds - {}++ // 40 seconds - {}++ // 40 seconds - {}++ // 40 seconds - {}++ // 5 seconds - {}++ // 10 seconds - {}++ // 20 seconds - {}++ // 40 seconds - {}++ // 40 seconds - {}++ // 40 seconds - {}++ // 40 seconds - {}++ // 40 seconds - {}++ // 40 seconds - {}++ // 40 seconds - {}++ // 40 seconds - {}++ // 40 seconds - {}++ // 40 seconds - {}++ // 40 seconds - {}++ // 40 seconds - {}++ // 40 seconds - {}++ // 5 seconds - {}++ // 10 seconds - {}++ // 20 seconds - {}++ // 40 seconds - {}++ // 40 seconds - {}++ // 40 seconds - {}++ // 40 seconds - {}++ // 40 seconds - {}++ // 40 seconds - {}++ // 40 seconds - {}++ // 40 seconds - {}++ // 40 seconds - {}++ // 40 seconds - {}++ // 40 seconds - {}++ // 40 seconds - {}++ // 40 seconds -
); -} diff --git a/test/files/neg/t1017.check b/test/files/neg/t1017.check deleted file mode 100644 index 52101c7f6..000000000 --- a/test/files/neg/t1017.check +++ /dev/null @@ -1,4 +0,0 @@ -t1017.scala:3: error: not found: value foo -{ foo } - ^ -one error found diff --git a/test/files/neg/t1017.scala b/test/files/neg/t1017.scala deleted file mode 100644 index e389f308c..000000000 --- a/test/files/neg/t1017.scala +++ /dev/null @@ -1,4 +0,0 @@ -// 'foo' is not defined -object Test { -{ foo } -} diff --git a/test/files/neg/t1878-typer.check b/test/files/neg/t1878-typer.check deleted file mode 100644 index e3a20d0be..000000000 --- a/test/files/neg/t1878-typer.check +++ /dev/null @@ -1,4 +0,0 @@ -t1878-typer.scala:4: error: _* may only come last - case

{ _* }

=> - ^ -one error found diff --git a/test/files/neg/t1878-typer.scala b/test/files/neg/t1878-typer.scala deleted file mode 100644 index 1eb0cb7df..000000000 --- a/test/files/neg/t1878-typer.scala +++ /dev/null @@ -1,6 +0,0 @@ -object Test extends App { - // illegal - bug #1764 - null match { - case

{ _* }

=> - } -} diff --git a/test/files/neg/t7185.check b/test/files/neg/t7185.check deleted file mode 100644 index 46f2cc797..000000000 --- a/test/files/neg/t7185.check +++ /dev/null @@ -1,7 +0,0 @@ -t7185.scala:2: error: overloaded method value apply with alternatives: - (f: scala.xml.Node => Boolean)scala.xml.NodeSeq - (i: Int)scala.xml.Node - cannot be applied to () - () - ^ -one error found diff --git a/test/files/neg/t7185.scala b/test/files/neg/t7185.scala deleted file mode 100644 index 2f9284bc5..000000000 --- a/test/files/neg/t7185.scala +++ /dev/null @@ -1,3 +0,0 @@ -object Test { - () -} diff --git a/test/files/pos/matchStarlift.scala b/test/files/pos/matchStarlift.scala deleted file mode 100644 index dab46eada..000000000 --- a/test/files/pos/matchStarlift.scala +++ /dev/null @@ -1,7 +0,0 @@ -object Tet { - import scala.xml._; - def fooz(x: Node=>String) = {} - def foo( m:Node ):Unit = fooz { - case Elem(_,_,_,_,n,_*) if (n == m) => "gaga" - } -} diff --git a/test/files/pos/t0422.scala b/test/files/pos/t0422.scala deleted file mode 100644 index 2adfa392d..000000000 --- a/test/files/pos/t0422.scala +++ /dev/null @@ -1,16 +0,0 @@ -package scala.xml.dtd.impl - -object BoolWordExp extends WordExp { - type _labelT = MyLabels; - type _regexpT = RegExp; - abstract class MyLabels extends Label ; - case class MyLabel(c:Char) extends MyLabels; -} - -object MyTranslator extends WordBerrySethi { - override val lang = BoolWordExp; - import lang._; - override protected def seenLabel( r:RegExp, i:Int, label: _labelT ): Unit = { - super.seenLabel(r,i,label) - } -} diff --git a/test/files/pos/t0646.scala b/test/files/pos/t0646.scala deleted file mode 100644 index 6146e6002..000000000 --- a/test/files/pos/t0646.scala +++ /dev/null @@ -1,21 +0,0 @@ -object xfor { - - import scala.xml.NodeSeq - - val books = - - Blabla - Blubabla - Baaaaaaalabla - ; - - new NodeSeq { val theSeq = books.child } match { - case t @ Seq(Blabla) => t - } - - //val n: NodeSeq = new NodeSeq { val theSeq = books.child } - //n match { - // case t @ Blabla => t - //} - -} diff --git a/test/files/pos/t1014.scala b/test/files/pos/t1014.scala deleted file mode 100644 index 3fc10d10d..000000000 --- a/test/files/pos/t1014.scala +++ /dev/null @@ -1,15 +0,0 @@ -import scala.xml.{NodeSeq, Elem} - -class EO extends App with Moo { - // return type is Flog, inherited from overridden method. - // implicit conversions are applied because expected type `pt` is `Flog` when `computeType(rhs, pt)`. - def cat = dog - - implicit def nodeSeqToFlog(in: Elem): Flog = new Flog(in) -} - -trait Moo { - def cat: Flog -} - -class Flog(val in: NodeSeq) diff --git a/test/files/pos/t1059.scala b/test/files/pos/t1059.scala deleted file mode 100644 index bcd8f0374..000000000 --- a/test/files/pos/t1059.scala +++ /dev/null @@ -1,28 +0,0 @@ -package com; - -import scala.xml._ - -object Main { - - def main(args : Array[String]) : Unit = { - - var m : PartialFunction[Any, Any] = { - - case SafeNodeSeq(s @ _*) => println(s) } - - println(m( ++ )) - println(m.isDefinedAt( ++ )) - - } - -} - -object SafeNodeSeq { - - def unapplySeq(any: Any) : Option[Seq[Node]] = any match { case s: Seq[_] => Some(s flatMap ( _ match { - - case n: Node => n case _ => NodeSeq.Empty - - })) case _ => None } - -} diff --git a/test/files/pos/t1203a.scala b/test/files/pos/t1203a.scala deleted file mode 100644 index 062ef93fc..000000000 --- a/test/files/pos/t1203a.scala +++ /dev/null @@ -1,7 +0,0 @@ -case class ant(t: String) extends scala.annotation.Annotation -object Test { - def main(args: Array[String]): Unit = { - val a: scala.xml.NodeSeq @ant("12") = Nil - println(a) - } -} diff --git a/test/files/pos/t1626.scala b/test/files/pos/t1626.scala deleted file mode 100644 index 200be4743..000000000 --- a/test/files/pos/t1626.scala +++ /dev/null @@ -1,4 +0,0 @@ -object o { - val n = - n.namespace == null -} diff --git a/test/files/pos/t1761.scala b/test/files/pos/t1761.scala deleted file mode 100644 index 2af728073..000000000 --- a/test/files/pos/t1761.scala +++ /dev/null @@ -1,10 +0,0 @@ -import scala.xml._ - -class Foo { - val elements: Seq[Node] = Nil - val innerTransform: PartialFunction[Elem, String] = { - case Elem(_, l: String, _, _, _ @ _*) if elements.exists(_.label == l) => - l - } -} - diff --git a/test/files/pos/t2281.scala b/test/files/pos/t2281.scala deleted file mode 100644 index 3515d2e2e..000000000 --- a/test/files/pos/t2281.scala +++ /dev/null @@ -1,41 +0,0 @@ -import scala.collection.mutable.ArrayBuffer - -class A { - def f(x: Boolean) = if (x)

else
-} - -class B { - def splitSentences(text : String) : ArrayBuffer[String] = { - val outarr = new ArrayBuffer[String] - var outstr = new StringBuffer - var prevspace = false - val ctext = text.replaceAll("\n+","\n") - ctext foreach {c => - outstr append c - if(c == '.' || c == '!' || c == '?' || c == '\n' || c == ':' || c == ';' || (prevspace && c == '-') ){ - outarr += outstr.toString - outstr = new StringBuffer - } - if(c == '\n'){ - outarr += "\n\n" - } - prevspace = c == ' ' - } - if(outstr.length > 0){ - outarr += outstr.toString - } - outarr - } - - def spanForSentence(x : String,picktext : String) = - if(x == "\n\n"){ -

- }else{ - {x} - } - - def selectableSentences(text : String, picktext : String) = { - val sentences = splitSentences(text) - sentences.map(x => spanForSentence(x,picktext)) - } -} \ No newline at end of file diff --git a/test/files/pos/t2698.scala b/test/files/pos/t2698.scala deleted file mode 100644 index 7de50a13d..000000000 --- a/test/files/pos/t2698.scala +++ /dev/null @@ -1,11 +0,0 @@ -package scala.xml.dtd.impl - -import scala.collection._ - -abstract class S2 { - val lang: WordExp - type __labelT = lang._labelT - - var deltaq: Array[__labelT] = _ - def delta1 = immutable.Map(deltaq.zipWithIndex: _*) -} diff --git a/test/files/pos/t3160.scala b/test/files/pos/t3160.scala deleted file mode 100644 index 3309ece16..000000000 --- a/test/files/pos/t3160.scala +++ /dev/null @@ -1,6 +0,0 @@ -import scala.collection.mutable._ -import scala.xml._ - -class A { - def f(x: Node): Node = ??? -} diff --git a/test/files/pos/t5858.scala b/test/files/pos/t5858.scala deleted file mode 100644 index f2b0f58d7..000000000 --- a/test/files/pos/t5858.scala +++ /dev/null @@ -1,3 +0,0 @@ -object Test { - new xml.Elem(null, null, xml.Null, xml.TopScope, Nil: _*) // was ambiguous -} diff --git a/test/files/pos/t6201.scala b/test/files/pos/t6201.scala deleted file mode 100644 index 366c1f26e..000000000 --- a/test/files/pos/t6201.scala +++ /dev/null @@ -1,13 +0,0 @@ -class Test { - class Foo1 { - def must(x: scala.xml.Elem) = () - } - - class Foo2 { - def must(x: Int) = () - } - implicit def toFoo1(s: scala.xml.Elem) = new Foo1() - implicit def toFoo2(s: scala.xml.Elem) = new Foo2() - - def is: Unit = { (
{"a"}).must({"b"}) } -} \ No newline at end of file diff --git a/test/files/pos/t6897.scala b/test/files/pos/t6897.scala deleted file mode 100644 index a7a03a1d3..000000000 --- a/test/files/pos/t6897.scala +++ /dev/null @@ -1,6 +0,0 @@ -class A { - val html = (null: Any) match { - case 1 => - case 2 =>

- } -} diff --git a/test/files/pos/t715/meredith_1.scala b/test/files/pos/t715/meredith_1.scala deleted file mode 100644 index c28afb4a9..000000000 --- a/test/files/pos/t715/meredith_1.scala +++ /dev/null @@ -1,98 +0,0 @@ -package com.sap.dspace.model.othello; - -import scala.xml._ - -trait XMLRenderer { - type T <: Any {def getClass(): java.lang.Class[_]} - val valueTypes = - List( - classOf[java.lang.Boolean], - classOf[java.lang.Integer], - classOf[java.lang.Float], - classOf[java.lang.String] - // more to come - ) - - def value2XML( - value: Object, - field: java.lang.reflect.Field, - pojo: T - ): Node = { - value match { - case null => Text("null") - case vUnmatched => - if (value.isInstanceOf[java.lang.Boolean]) - Text(value.asInstanceOf[java.lang.Boolean].toString) - else if (value.isInstanceOf[java.lang.Integer]) - Text(value.asInstanceOf[java.lang.Integer].toString) - else if (value.isInstanceOf[java.lang.Float]) - Text(value.asInstanceOf[java.lang.Float].toString) - // else if (value.isInstanceOf[T]) - // pojo2XML(value.asInstanceOf[T]) - else - - - {vUnmatched.getClass.toString} - - - {vUnmatched.toString} - - - } - } - - def field2XML( - field: java.lang.reflect.Field, - pojo: T - ): Elem = { - - val accessible = field.isAccessible - field.setAccessible(true) - // BUGBUG lgm need to disambiguate on type and possibly make - // recursive call to pojo2XML - val fldValXML = value2XML(field.get( pojo ), field, pojo) - field.setAccessible( accessible ) - - Elem( - null, - field.getName, - null, - TopScope, - fldValXML - ) - } - - def pojo2XML(pojo: T): Elem = { - val progeny = - for (field <- pojo.getClass.getDeclaredFields) - yield field2XML(field, pojo) - - Elem( - null, - pojo.getClass.getName, - null, - TopScope, - progeny.asInstanceOf[Array[scala.xml.Node]]: _* - ) - } -} - -case class POJO2XMLRenderer(recurse: Boolean) - extends XMLRenderer { - type T = java.io.Serializable - override def value2XML( - value: Object, - field: java.lang.reflect.Field, - pojo: java.io.Serializable - ): Node = { - if (recurse) super.value2XML(value, field, pojo) - else Text(value + "") - } -} - -object thePOJO2XMLRenderer extends POJO2XMLRenderer(true) { -} - -object Test extends App { - println(com.sap.dspace.model.othello.thePOJO2XMLRenderer) -} diff --git a/test/files/pos/t715/runner_2.scala b/test/files/pos/t715/runner_2.scala deleted file mode 100644 index d54805629..000000000 --- a/test/files/pos/t715/runner_2.scala +++ /dev/null @@ -1,3 +0,0 @@ -object Test extends App { - println(com.sap.dspace.model.othello.thePOJO2XMLRenderer) -} diff --git a/test/files/pos/t880.scala b/test/files/pos/t880.scala deleted file mode 100644 index cceb53c39..000000000 --- a/test/files/pos/t880.scala +++ /dev/null @@ -1,6 +0,0 @@ -import scala.xml.Null - -class Test[A >: Null] -{ - val x : A = null -} diff --git a/test/files/run/fors.check b/test/files/run/fors.check deleted file mode 100644 index 08ecc8ed5..000000000 --- a/test/files/run/fors.check +++ /dev/null @@ -1,46 +0,0 @@ - -testOld -1 2 3 -2 -2 -3 -1 2 3 -1 2 3 -0 1 2 3 4 5 6 7 8 9 -0 2 4 6 8 -0 2 4 6 8 -a b c -b c -b c - - -Scala - - -1 2 3 - - -Scala - -testNew -3 -1 2 3 -1 2 3 -0 1 2 3 4 5 6 7 8 9 -0 2 4 6 8 -0 2 4 6 8 -0 2 4 6 8 -0 2 4 6 8 -0 2 4 6 8 -0 2 4 6 8 -0 2 4 6 8 -a b c - - -Scala - - -1 2 3 - - -Scala diff --git a/test/files/run/fors.scala b/test/files/run/fors.scala deleted file mode 100644 index 54afdc710..000000000 --- a/test/files/run/fors.scala +++ /dev/null @@ -1,97 +0,0 @@ -//############################################################################ -// for-comprehensions (old and new syntax) -//############################################################################ - -//############################################################################ - -object Test extends App { - val xs = List(1, 2, 3) - val ys = List('a, 'b, 'c) - - def it = 0 until 10 - - val ar = "abc".toCharArray - - val xml = - - Scala - {xs} - ; - - /////////////////// old syntax /////////////////// - - def testOld { - println("\ntestOld") - - // lists - for (x <- xs) print(x + " "); println - for (x <- xs; - if x % 2 == 0) print(x + " "); println - for {x <- xs - if x % 2 == 0} print(x + " "); println - var n = 0 - for (_ <- xs) n += 1; println(n) - for ((x, y) <- xs zip ys) print(x + " "); println - for (p @ (x, y) <- xs zip ys) print(p._1 + " "); println - - // iterators - for (x <- it) print(x + " "); println - for (x <- it; - if x % 2 == 0) print(x + " "); println - for {x <- it - if x % 2 == 0} print(x + " "); println - - // arrays - for (x <- ar) print(x + " "); println - for (x <- ar; - if x.toInt > 97) print(x + " "); println - for {x <- ar - if x.toInt > 97} print(x + " "); println - - // sequences - for (x <- xml.child) println(x) - for (x <- xml.child; - if x.label == "head") println(x) - } - - /////////////////// new syntax /////////////////// - - def testNew { - println("\ntestNew") - - // lists - var n = 0 - for (_ <- xs) n += 1; println(n) - for ((x, y) <- xs zip ys) print(x + " "); println - for (p @ (x, y) <- xs zip ys) print(p._1 + " "); println - - // iterators - for (x <- it) print(x + " "); println - for (x <- it if x % 2 == 0) print(x + " "); println - for (x <- it; if x % 2 == 0) print(x + " "); println - for (x <- it; - if x % 2 == 0) print(x + " "); println - for (x <- it - if x % 2 == 0) print(x + " "); println - for {x <- it - if x % 2 == 0} print(x + " "); println - for (x <- it; - y = 2 - if x % y == 0) print(x + " "); println - for {x <- it - y = 2 - if x % y == 0} print(x + " "); println - - // arrays - for (x <- ar) print(x + " "); println - - // sequences - for (x <- xml.child) println(x) - for (x <- xml.child if x.label == "head") println(x) - } - - //////////////////////////////////////////////////// - - testOld - testNew -} diff --git a/test/files/run/io-position.check b/test/files/run/io-position.check deleted file mode 100644 index 09f743d75..000000000 Binary files a/test/files/run/io-position.check and /dev/null differ diff --git a/test/files/run/io-position.scala b/test/files/run/io-position.scala deleted file mode 100644 index b227846fb..000000000 --- a/test/files/run/io-position.scala +++ /dev/null @@ -1,11 +0,0 @@ -object Test { - def main(args: Array[String]): Unit = Console.withErr(Console.out) { - try { - xml.parsing.ConstructingParser.fromSource(io.Source.fromString(""), false).document() - } catch { - case e:Exception => println(e.getMessage) - } - } - -} - diff --git a/test/files/run/nodebuffer-array.check b/test/files/run/nodebuffer-array.check deleted file mode 100644 index 49f8bfaf8..000000000 --- a/test/files/run/nodebuffer-array.check +++ /dev/null @@ -1,3 +0,0 @@ - - abc - diff --git a/test/files/run/nodebuffer-array.scala b/test/files/run/nodebuffer-array.scala deleted file mode 100644 index 4e1ffe1e5..000000000 --- a/test/files/run/nodebuffer-array.scala +++ /dev/null @@ -1,15 +0,0 @@ -object Test { - - def f(s: String) = { - - { - for (item <- s split ',') yield - { item } - } - - } - - def main(args: Array[String]): Unit = { - println(f("a,b,c")) - } -} diff --git a/test/files/run/repl-backticks.check b/test/files/run/repl-backticks.check deleted file mode 100644 index c0561abd7..000000000 --- a/test/files/run/repl-backticks.check +++ /dev/null @@ -1,2 +0,0 @@ -import java.lang.Thread.`yield` -import scala.`package`.Throwable diff --git a/test/files/run/repl-backticks.scala b/test/files/run/repl-backticks.scala deleted file mode 100644 index ec2691d9c..000000000 --- a/test/files/run/repl-backticks.scala +++ /dev/null @@ -1,18 +0,0 @@ -import scala.tools.nsc._ - -object Test { - val testCode = - import java.lang.Thread.`yield` - import scala.`package`.Throwable - - `yield` - .text - - def main(args: Array[String]) { - val settings = new Settings() - settings.classpath.value = System.getProperty("java.class.path") - val repl = new interpreter.IMain(settings) - repl.interpret(testCode) - } -} - diff --git a/test/files/run/serialize.check b/test/files/run/serialize.check deleted file mode 100644 index c1d1360a9..000000000 --- a/test/files/run/serialize.check +++ /dev/null @@ -1,54 +0,0 @@ -x = xml:src="hello" -y = xml:src="hello" -x equals y: true, y equals x: true - -x = -y = -x equals y: true, y equals x: true - -x = title -y = title -x equals y: true, y equals x: true - -x = - - - - - - - - - - - - - - - - -
Last NameFirst Name
Tom 20
Bob 22
James 19
- - -y = - - - - - - - - - - - - - - - - -
Last NameFirst Name
Tom 20
Bob 22
James 19
- - -x equals y: true, y equals x: true - diff --git a/test/files/run/serialize.scala b/test/files/run/serialize.scala deleted file mode 100644 index fd84bab8d..000000000 --- a/test/files/run/serialize.scala +++ /dev/null @@ -1,98 +0,0 @@ -object Test { - def main(args: Array[String]): Unit = { - Test4_xml - } -} - -//############################################################################ -// Serialization -//############################################################################ - -object Serialize { - @throws(classOf[java.io.IOException]) - def write[A](o: A): Array[Byte] = { - val ba = new java.io.ByteArrayOutputStream(512) - val out = new java.io.ObjectOutputStream(ba) - out.writeObject(o) - out.close() - ba.toByteArray() - } - @throws(classOf[java.io.IOException]) - @throws(classOf[ClassNotFoundException]) - def read[A](buffer: Array[Byte]): A = { - val in = - new java.io.ObjectInputStream(new java.io.ByteArrayInputStream(buffer)) - in.readObject().asInstanceOf[A] - } - def check[A, B](x: A, y: B) { - println("x = " + x) - println("y = " + y) - println("x equals y: " + (x equals y) + ", y equals x: " + (y equals x)) - assert((x equals y) && (y equals x)) - println() - } -} -import Serialize._ - -//############################################################################ -// Test classes in package "scala.xml" - -object Test4_xml { - import scala.xml.{Attribute, Document, Elem, Null, PrefixedAttribute, Text} - - case class Person(name: String, age: Int) - - try { - // Attribute - val a1 = new PrefixedAttribute("xml", "src", Text("hello"), Null) - val _a1: Attribute = read(write(a1)) - check(a1, _a1) - - // Document - val d1 = new Document - d1.docElem = - d1.encoding = Some("UTF-8") - val _d1: Document = read(write(d1)) - check(d1, _d1) - - // Elem - val e1 = title; - val _e1: Elem = read(write(e1)) - check(e1, _e1) - - class AddressBook(a: Person*) { - private val people: List[Person] = a.toList - def toXHTML = - - - - - - { for (p <- people) yield - - - - } -
Last NameFirst Name
{ p.name } { p.age.toString() }
; - } - - val people = new AddressBook( - Person("Tom", 20), - Person("Bob", 22), - Person("James", 19)) - - val e2 = - - - { people.toXHTML } - - ; - val _e2: Elem = read(write(e2)) - check(e2, _e2) - } - catch { - case e: Exception => - println("Error in Test4_xml: " + e) - throw e - } -} \ No newline at end of file diff --git a/test/files/run/t0486.check b/test/files/run/t0486.check deleted file mode 100644 index dd1ec2822..000000000 --- a/test/files/run/t0486.check +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/test/files/run/t0486.scala b/test/files/run/t0486.scala deleted file mode 100644 index d3ed8f422..000000000 --- a/test/files/run/t0486.scala +++ /dev/null @@ -1,24 +0,0 @@ -object Test extends App { - import scala.xml._ - - def wsdlTemplate1(serviceName: String): Node = - - ; - - def wsdlTemplate2(serviceName: String, targetNamespace: String): Node = - - ; - - def wsdlTemplate3(serviceName: String): Node = - - ; - - def wsdlTemplate4(serviceName: String, targetNamespace: () => String): Node = - - ; - - println(wsdlTemplate1("service1")) - println(wsdlTemplate2("service2", "target2")) - println(wsdlTemplate3("service3")) - println(wsdlTemplate4("service4", () => "target4")) -} diff --git a/test/files/run/t0663.check b/test/files/run/t0663.check deleted file mode 100755 index dd9be2af7..000000000 --- a/test/files/run/t0663.check +++ /dev/null @@ -1 +0,0 @@ - diff --git a/test/files/run/t0663.scala b/test/files/run/t0663.scala deleted file mode 100644 index dd0326d4e..000000000 --- a/test/files/run/t0663.scala +++ /dev/null @@ -1,6 +0,0 @@ -object Test extends App { - val src = scala.io.Source.fromString("") - val parser = xml.parsing.ConstructingParser.fromSource(src, true) - println(parser.document) -} - diff --git a/test/files/run/t1079.check b/test/files/run/t1079.check deleted file mode 100644 index c508d5366..000000000 --- a/test/files/run/t1079.check +++ /dev/null @@ -1 +0,0 @@ -false diff --git a/test/files/run/t1079.scala b/test/files/run/t1079.scala deleted file mode 100644 index ce435d254..000000000 --- a/test/files/run/t1079.scala +++ /dev/null @@ -1,3 +0,0 @@ -object Test extends App { - println( == ) -} diff --git a/test/files/run/t1500.check b/test/files/run/t1500.check deleted file mode 100644 index 94a169333..000000000 --- a/test/files/run/t1500.check +++ /dev/null @@ -1,3 +0,0 @@ -defined class posingAs -resolve: [A, B](x: A @posingAs[B])B -x: Any = 7 diff --git a/test/files/run/t1500.scala b/test/files/run/t1500.scala deleted file mode 100644 index 75a6e31cd..000000000 --- a/test/files/run/t1500.scala +++ /dev/null @@ -1,46 +0,0 @@ -import scala.tools.nsc._ - -object Test { - - /** - * Type inference overlooks constraints posed by type parameters in annotations on types. - */ - - val testCode = - - class posingAs[A] extends annotation.TypeConstraint - - def resolve[A,B](x: A @posingAs[B]): B = x.asInstanceOf[B] - - val x = resolve(7: @posingAs[Any]) - - .text - - def main(args: Array[String]) { - - val settings = new Settings() - settings.classpath.value = System.getProperty("java.class.path") - val tool = new interpreter.IMain(settings) - val global = tool.global - - import global._ - import definitions._ - - object checker extends AnnotationChecker { - - /** Check annotations to decide whether tpe1 <:< tpe2 */ - def annotationsConform(tpe1: Type, tpe2: Type): Boolean = { - - tpe1.annotations.forall(a1 => tpe2.annotations.forall(a2 => a1.atp <:< a2.atp)) - - } - } - - global.addAnnotationChecker(checker) - - tool.interpret(testCode) - - } - -} - diff --git a/test/files/run/t1501.check b/test/files/run/t1501.check deleted file mode 100644 index f0fa9112a..000000000 --- a/test/files/run/t1501.check +++ /dev/null @@ -1,3 +0,0 @@ -defined class xyz -loopWhile: [T](cond: => Boolean)(body: => Unit @xyz[T])Unit @xyz[T] -test: ()Unit @xyz[Int] diff --git a/test/files/run/t1501.scala b/test/files/run/t1501.scala deleted file mode 100644 index 71ad0aeb5..000000000 --- a/test/files/run/t1501.scala +++ /dev/null @@ -1,56 +0,0 @@ -import scala.tools.nsc._ - -object Test { - - /** - * ... - */ - - val testCode = - - class xyz[A] extends annotation.TypeConstraint - - def loopWhile[T](cond: =>Boolean)(body: =>(Unit @xyz[T])): Unit @ xyz[T] = {{ - if (cond) {{ - body - loopWhile[T](cond)(body) - }} - }} - - def test() = {{ - var x = 7 - loopWhile(x != 0) {{ - x = x - 1 - (): @xyz[Int] - }} - }} - - .text - - def main(args: Array[String]) { - val settings = new Settings() - settings.classpath.value = System.getProperty("java.class.path") - val tool = new interpreter.IMain(settings) - val global = tool.global - - import global._ - import definitions._ - - object checker extends AnnotationChecker { - - /** Check annotations to decide whether tpe1 <:< tpe2 */ - def annotationsConform(tpe1: Type, tpe2: Type): Boolean = { - - tpe1.annotations.forall(a1 => tpe2.annotations.forall(a2 => a1.atp <:< a2.atp)) - - } - } - - global.addAnnotationChecker(checker) - - tool.interpret(testCode) - - } - -} - diff --git a/test/files/run/t1620.check b/test/files/run/t1620.check deleted file mode 100755 index afa1e6acd..000000000 --- a/test/files/run/t1620.check +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/test/files/run/t1620.scala b/test/files/run/t1620.scala deleted file mode 100644 index e8ea06eb5..000000000 --- a/test/files/run/t1620.scala +++ /dev/null @@ -1,16 +0,0 @@ -import java.io.PrintWriter -import scala.xml.XML -import scala.xml.dtd.{DocType, PublicID} - -object Test extends App { - val dt = DocType("foo", PublicID("-//Foo Corp//DTD 1.0//EN", "foo.dtd"), Seq()) - val pw = new PrintWriter(System.out) - XML.write(pw, , "utf-8", true, dt) - pw.println() - pw.flush() - - val dt2 = DocType("foo", PublicID("-//Foo Corp//DTD 1.0//EN", null), Seq()) - XML.write(pw, , "utf-8", true, dt2) - pw.println() - pw.flush() -} diff --git a/test/files/run/t1773.scala b/test/files/run/t1773.scala deleted file mode 100644 index c50b62512..000000000 --- a/test/files/run/t1773.scala +++ /dev/null @@ -1,12 +0,0 @@ -object Test extends App -{ - val xs = List( - , - , - { xml.NodeSeq.Empty }, - {""}, - { if (true) "" else "I like turtles" } - ) - - for (x1 <- xs; x2 <- xs) assert (x1 xml_== x2) -} diff --git a/test/files/run/t2124.check b/test/files/run/t2124.check deleted file mode 100755 index 51b40469a..000000000 --- a/test/files/run/t2124.check +++ /dev/null @@ -1 +0,0 @@ -

diff --git a/test/files/run/t2124.scala b/test/files/run/t2124.scala deleted file mode 100644 index a4fd654d7..000000000 --- a/test/files/run/t2124.scala +++ /dev/null @@ -1,25 +0,0 @@ -import scala.xml._ - -import scala.xml.transform._ - -object Test { - val sampleXml =

- - def main(args: scala.Array[String]) { - - println(new RuleTransformer(new RewriteRule { - - override def transform(n: Node): NodeSeq = { - val result = n match { - case {_*} => - - case n => n - - } -// println ("Rewriting '" +n+ "' to: '" + result+ "'") - - result - } - }).transform(sampleXml)) - } -} diff --git a/test/files/run/t2125.check b/test/files/run/t2125.check deleted file mode 100755 index 51b40469a..000000000 --- a/test/files/run/t2125.check +++ /dev/null @@ -1 +0,0 @@ -

diff --git a/test/files/run/t2125.scala b/test/files/run/t2125.scala deleted file mode 100644 index a10ed9827..000000000 --- a/test/files/run/t2125.scala +++ /dev/null @@ -1,25 +0,0 @@ -import scala.xml._ - -import scala.xml.transform._ - -object Test { - - val sampleXml =

- - def main(args: scala.Array[String]) { - println(new RuleTransformer(new RewriteRule { - - override def transform(n: Node): NodeSeq = { - - val result = n match { - - case {_*} => - - case n => n - } -// println ("Rewriting '" +n+ "' to: '" + result+ "'") - result - } - }).transform(sampleXml)) - } -} diff --git a/test/files/run/t2276.check b/test/files/run/t2276.check deleted file mode 100644 index 95f51c8e2..000000000 --- a/test/files/run/t2276.check +++ /dev/null @@ -1,8 +0,0 @@ - - - 2 - - - 2 - - diff --git a/test/files/run/t2276.scala b/test/files/run/t2276.scala deleted file mode 100644 index f0404e5fa..000000000 --- a/test/files/run/t2276.scala +++ /dev/null @@ -1,24 +0,0 @@ -import scala.xml._ -import scala.xml.transform._ - -object Test extends App { - val inputXml : Node = - - - 1 - - - 1 - - - - object t1 extends RewriteRule { - override def transform(n: Node): Seq[Node] = n match { - case {x} if x.toString.toInt < 4 => {x.toString.toInt+1} - case other => other - } - } - - val ruleTransformer = new RuleTransformer(t1) - println(ruleTransformer(inputXml)) -} diff --git a/test/files/run/t2354.scala b/test/files/run/t2354.scala deleted file mode 100644 index 5419911ac..000000000 --- a/test/files/run/t2354.scala +++ /dev/null @@ -1,17 +0,0 @@ -import scala.xml.parsing._ -import scala.io.Source - -object Test -{ - val xml_good = "<![CDATA[Hello [tag]]]>" - val xml_bad = "<![CDATA[Hello [tag] ]]>" - - val parser1 = ConstructingParser.fromSource(Source.fromString(xml_good),false) - val parser2 = ConstructingParser.fromSource(Source.fromString(xml_bad),false) - - def main(args: Array[String]): Unit = { - parser1.document - parser2.document - } -} - diff --git a/test/files/run/t2721.check b/test/files/run/t2721.check deleted file mode 100644 index 2bd7656b3..000000000 --- a/test/files/run/t2721.check +++ /dev/null @@ -1,2 +0,0 @@ -root:-rootVal-sub:-subVal- -root:-rootVal-sub:-subVal- diff --git a/test/files/run/t2721.scala b/test/files/run/t2721.scala deleted file mode 100644 index 93af884a6..000000000 --- a/test/files/run/t2721.scala +++ /dev/null @@ -1,12 +0,0 @@ -object Test -{ - val xml1 = - val xml2= scala.xml.XML.loadString("""""") - - def backslashSearch(x: xml.Elem) = "root:-"+(x \ "@{nsUri}at") +"-sub:-"+(x \ "sub" \ "@{nsUri}at") +"-" - - def main(args: Array[String]): Unit = { - println(backslashSearch(xml1)) - println(backslashSearch(xml2)) - } -} diff --git a/test/files/run/t3705.scala b/test/files/run/t3705.scala deleted file mode 100644 index 3ebf6fc95..000000000 --- a/test/files/run/t3705.scala +++ /dev/null @@ -1,17 +0,0 @@ -// package foo - -import scala.xml._ -object Test { - // guard caused verifyerror in oldpatmat - def updateNodes(ns: Seq[Node]): Seq[Node] = - for(subnode <- ns) yield subnode match { - case {_} if true => abc - case Elem(prefix, label, attribs, scope, children @ _*) => - Elem(prefix, label, attribs, scope, minimizeEmpty = true, updateNodes(children) : _*) - case other => other - } - def main(args: Array[String]): Unit = { - updateNodes() - } -} - diff --git a/test/files/run/t3886.scala b/test/files/run/t3886.scala deleted file mode 100644 index 1e8e7ad25..000000000 --- a/test/files/run/t3886.scala +++ /dev/null @@ -1,11 +0,0 @@ -object Test { - def main(args: Array[String]) { - assert( == ) - assert( != ) - assert( != ) - - assert( != ) - assert( != ) - assert( != ) - } -} diff --git a/test/files/run/t4124.check b/test/files/run/t4124.check deleted file mode 100644 index 66a0092d9..000000000 --- a/test/files/run/t4124.check +++ /dev/null @@ -1,4 +0,0 @@ -hi -hi -bye -bye diff --git a/test/files/run/t4124.scala b/test/files/run/t4124.scala deleted file mode 100644 index 9f35b57ce..000000000 --- a/test/files/run/t4124.scala +++ /dev/null @@ -1,24 +0,0 @@ -import xml.Node - -object Test extends App { - val body: Node = hi - println ((body: AnyRef, "foo") match { - case (node: Node, "bar") => "bye" - case (ser: Serializable, "foo") => "hi" - }) - - println ((body, "foo") match { - case (node: Node, "bar") => "bye" - case (ser: Serializable, "foo") => "hi" - }) - - println ((body: AnyRef, "foo") match { - case (node: Node, "foo") => "bye" - case (ser: Serializable, "foo") => "hi" - }) - - println ((body: AnyRef, "foo") match { - case (node: Node, "foo") => "bye" - case (ser: Serializable, "foo") => "hi" - }) -} diff --git a/test/files/run/t4387.scala b/test/files/run/t4387.scala deleted file mode 100644 index 68cbe97d0..000000000 --- a/test/files/run/t4387.scala +++ /dev/null @@ -1,12 +0,0 @@ -object Test { - import xml.XML.loadString - def mkElem(arg: String) = - - val x1 = mkElem("5") - val x2 = mkElem("50") - - def main(args: Array[String]): Unit = { - assert(x1 == loadString("" + x1)) - assert(x2 != loadString("" + x1)) - } -} diff --git a/test/files/run/t5052.scala b/test/files/run/t5052.scala deleted file mode 100644 index 9e418e8ac..000000000 --- a/test/files/run/t5052.scala +++ /dev/null @@ -1,6 +0,0 @@ -object Test extends App { - assert( xml_== ) - assert( xml_== ) - assert( xml_== ) - assert( xml_== ) -} diff --git a/test/files/run/t5115.scala b/test/files/run/t5115.scala deleted file mode 100644 index cf2521471..000000000 --- a/test/files/run/t5115.scala +++ /dev/null @@ -1,14 +0,0 @@ -import scala.collection.Iterable - -object Test extends App { - def assertHonorsIterableContract(i: Iterable[_]) = assert(i.size == i.iterator.size) - - assertHonorsIterableContract(.attributes) - assertHonorsIterableContract(.attributes) - assertHonorsIterableContract(.attributes) - assertHonorsIterableContract(.attributes) - assertHonorsIterableContract(.attributes) - assertHonorsIterableContract(.attributes) - assertHonorsIterableContract(.attributes) - assertHonorsIterableContract(.attributes) -} diff --git a/test/files/run/t5843.check b/test/files/run/t5843.check deleted file mode 100644 index 2bf97f4cd..000000000 --- a/test/files/run/t5843.check +++ /dev/null @@ -1,9 +0,0 @@ - foo="1" - bar="2" foo="1" -null - bar="2" - foo="1" - bar="2" - foo="1" - bar="2" foo="1" - bar="2" foo="1" diff --git a/test/files/run/t5843.scala b/test/files/run/t5843.scala deleted file mode 100644 index 43d588c7b..000000000 --- a/test/files/run/t5843.scala +++ /dev/null @@ -1,15 +0,0 @@ -object Test extends App { - val foo = scala.xml.Attribute(null, "foo", "1", scala.xml.Null) - val bar = scala.xml.Attribute(null, "bar", "2", foo) - println(foo) - println(bar) - println(scala.xml.TopScope.getURI(foo.pre)) - println(bar remove "foo") - println(bar remove "bar") - println(bar remove (null, scala.xml.TopScope, "foo")) - println(bar remove (null, scala.xml.TopScope, "bar")) - - val ns = scala.xml.NamespaceBinding(null, "uri", scala.xml.TopScope) - println(bar remove (null, ns, "foo")) - println(bar remove (null, ns, "bar")) -} diff --git a/test/files/run/t6939.scala b/test/files/run/t6939.scala deleted file mode 100644 index 9fe721555..000000000 --- a/test/files/run/t6939.scala +++ /dev/null @@ -1,13 +0,0 @@ -object Test extends App { - val foo = - assert(foo.child.head.scope.toString == """ xmlns:x="http://bar.com/"""") - - val fooDefault = - assert(fooDefault.child.head.scope.toString == """ xmlns="http://bar.com/"""") - - val foo2 = scala.xml.XML.loadString("""""") - assert(foo2.child.head.scope.toString == """ xmlns:x="http://bar.com/"""") - - val foo2Default = scala.xml.XML.loadString("""""") - assert(foo2Default.child.head.scope.toString == """ xmlns="http://bar.com/"""") -} diff --git a/test/files/run/t7074.check b/test/files/run/t7074.check deleted file mode 100644 index ab9cf11f1..000000000 --- a/test/files/run/t7074.check +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/test/files/run/t7074.scala b/test/files/run/t7074.scala deleted file mode 100644 index 693a076a7..000000000 --- a/test/files/run/t7074.scala +++ /dev/null @@ -1,15 +0,0 @@ -import scala.xml.Utility.sort - -object Test extends App { - println(sort()) - println(sort()) - println(sort()) - println(sort()) - println(sort()) - - println(sort()) - println(sort()) - println(sort()) - println(sort()) -} - diff --git a/test/files/run/xml-attribute.check b/test/files/run/xml-attribute.check deleted file mode 100644 index 3cfe3779f..000000000 --- a/test/files/run/xml-attribute.check +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/test/files/run/xml-attribute.scala b/test/files/run/xml-attribute.scala deleted file mode 100644 index eb3956c41..000000000 --- a/test/files/run/xml-attribute.scala +++ /dev/null @@ -1,37 +0,0 @@ -import xml.Node - -object Test { - def main(args: Array[String]): Unit = { - val noAttr = - val attrNull = - val attrNone = - val preAttrNull = - val preAttrNone = - assert(noAttr == attrNull) - assert(noAttr == attrNone) - assert(noAttr == preAttrNull) - assert(noAttr == preAttrNone) - - println(noAttr) - println(attrNull) - println(attrNone) - println(preAttrNull) - println(preAttrNone) - - val xml1 = - val xml2 = - val xml3 = - assert(xml1 == xml2) - assert(xml1 == xml3) - - println(xml1) - println(xml2) - println(xml3) - - // Check if attribute order is retained - println() - println() - println() - println() - } -} diff --git a/test/files/run/xml-loop-bug.scala b/test/files/run/xml-loop-bug.scala deleted file mode 100644 index dc155dbb0..000000000 --- a/test/files/run/xml-loop-bug.scala +++ /dev/null @@ -1,14 +0,0 @@ -import java.io.{ Console => _, _ } -import scala.io._ -import scala.xml.parsing._ -object Test { - def main(args: Array[String]): Unit = { - val xml = " " - val sink = new PrintStream(new ByteArrayOutputStream()) - (Console withOut sink) { - (Console withErr sink) { - ConstructingParser.fromSource((Source fromString xml), true).document.docElem - } - } - } -}