Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 8f697e5

Browse files
authored
Merge pull request #684 from lrytz/safeToString-backport
Backport stack safe toString to 1.x
2 parents a2d6d9a + c9c8f21 commit 8f697e5

File tree

9 files changed

+174
-161
lines changed

9 files changed

+174
-161
lines changed

‎.github/workflows/ci.yml‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: test
2+
on:
3+
push:
4+
branches:
5+
- main
6+
pull_request:
7+
jobs:
8+
test:
9+
strategy:
10+
fail-fast: false
11+
matrix:
12+
java: [8, 11, 17]
13+
scala: [2.11.x, 2.12.x, 2.13.x]
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: actions/checkout@v2
17+
with:
18+
fetch-depth: 0
19+
- uses: actions/setup-java@v3
20+
with:
21+
distribution: temurin
22+
java-version: ${{matrix.java}}
23+
cache: sbt
24+
- name: Test
25+
run: sbt ++${{matrix.scala}} test versionPolicyCheck package

‎.github/workflows/release.yml‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: Release
2+
on:
3+
push:
4+
tags: ["*"]
5+
jobs:
6+
publish:
7+
runs-on: ubuntu-latest
8+
steps:
9+
- uses: actions/checkout@v2
10+
with:
11+
fetch-depth: 0
12+
- uses: actions/setup-java@v2
13+
with:
14+
distribution: temurin
15+
java-version: 8
16+
- run: sbt versionCheck ci-release
17+
env:
18+
PGP_PASSPHRASE: ${{secrets.PGP_PASSPHRASE}}
19+
PGP_SECRET: ${{secrets.PGP_SECRET}}
20+
SONATYPE_PASSWORD: ${{secrets.SONATYPE_PASSWORD}}
21+
SONATYPE_USERNAME: ${{secrets.SONATYPE_USERNAME}}

‎.travis.yml‎

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

‎build.sbt‎

Lines changed: 50 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,55 @@
11
import sbtcrossproject.CrossPlugin.autoImport.{crossProject, CrossType}
22

3+
publish / skip := true // root project
4+
5+
ThisBuild / startYear := Some(2002)
6+
ThisBuild / licenses += (("Apache-2.0", url("https://www.apache.org/licenses/LICENSE-2.0")))
7+
8+
// because it doesn't declare it itself
9+
ThisBuild / libraryDependencySchemes += "org.scala-js" %% "scalajs-library" % "semver-spec"
10+
ThisBuild / apiURL := Some(url("https://javadoc.io/doc/org.scala-lang.modules/scala-xml_2.13/"))
11+
12+
lazy val configSettings: Seq[Setting[?]] = Seq(
13+
unmanagedSourceDirectories ++= {
14+
unmanagedSourceDirectories.value.flatMap { dir =>
15+
def forVersion(version: String): File = file(dir.getPath ++ "-" ++ version)
16+
CrossVersion.partialVersion(scalaVersion.value) match {
17+
case Some((2, 13)) => Seq(forVersion("2.13"))
18+
case _ => Seq(forVersion("2.11-2.12"))
19+
}
20+
}
21+
}
22+
)
23+
324
lazy val xml = crossProject(JSPlatform, JVMPlatform)
425
.withoutSuffixFor(JVMPlatform)
526
.crossType(CrossType.Full)
627
.in(file("."))
728
.settings(ScalaModulePlugin.scalaModuleSettings)
8-
.jvmSettings(ScalaModulePlugin.scalaModuleSettingsJVM)
29+
.jvmSettings(ScalaModulePlugin.scalaModuleOsgiSettings)
930
.settings(
1031
name := "scala-xml",
32+
scalaModuleAutomaticModuleName := Some("scala.xml"),
33+
crossScalaVersions := Seq("2.13.11", "2.12.18", "2.11.12"),
34+
scalaVersion := "2.12.18",
1135

12-
// Compiler team advised avoiding the -Xfuture option for releases.
13-
// The output with -Xfuture should be periodically checked, though.
14-
scalacOptions ++= "-deprecation:false -feature -Xlint:-stars-align,-nullary-unit,_".split("\\s+").to[Seq],
15-
scalacOptions in Test += "-Xxml:coalescing",
36+
scalacOptions ++= Seq("-deprecation:false", "-feature", "-Xlint:-stars-align,-nullary-unit,_"),
1637

17-
scalaModuleMimaPreviousVersion := {
18-
if (System.getenv("SCALAJS_VERSION") == "1.0.1") None
19-
else Some("1.2.0")
20-
},
38+
Test / scalacOptions += "-Xxml:coalescing",
2139

22-
unmanagedSourceDirectories in Compile ++= {
23-
(unmanagedSourceDirectories in Compile).value.map { dir =>
24-
val sv = scalaVersion.value
25-
CrossVersion.partialVersion(sv) match {
26-
case Some((2, 13)) => file(dir.getPath ++ "-2.13")
27-
case _ => file(dir.getPath ++ "-2.11-2.12")
28-
}
29-
}
30-
},
40+
headerLicense := Some(HeaderLicense.Custom(
41+
s"""|Scala (https://www.scala-lang.org)
42+
|
43+
|Copyright EPFL and Lightbend, Inc.
44+
|
45+
|Licensed under Apache License 2.0
46+
|(http://www.apache.org/licenses/LICENSE-2.0).
47+
|
48+
|See the NOTICE file distributed with this work for
49+
|additional information regarding copyright ownership.
50+
|""".stripMargin)),
3151

32-
apiURL := Some(
33-
url(s"""https://scala.github.io/scala-xml/api/${"-.*".r.replaceAllIn(version.value, "")}/""")
34-
),
52+
versionPolicyIntention := Compatibility.BinaryCompatible,
3553

3654
apiMappings ++= scalaInstance.value.libraryJars.filter { file =>
3755
file.getName.startsWith("scala-library") && file.getName.endsWith(".jar")
@@ -47,7 +65,7 @@ lazy val xml = crossProject(JSPlatform, JVMPlatform)
4765
file(jarPath)
4866
-> url("http://docs.oracle.com/javase/8/docs/api")
4967
)
50-
}getOrElse {
68+
}.getOrElse {
5169
// If everything fails, jam in Java 11 modules.
5270
Map(
5371
file("/modules/java.base")
@@ -58,16 +76,20 @@ lazy val xml = crossProject(JSPlatform, JVMPlatform)
5876
}
5977
}
6078
)
79+
.settings(
80+
inConfig(Compile)(configSettings) ++ inConfig(Test)(configSettings)
81+
)
6182
.jvmSettings(
6283
OsgiKeys.exportPackage := Seq(s"scala.xml.*;version=${version.value}"),
6384

64-
libraryDependencies += "junit" % "junit" % "4.13" % "test",
65-
libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % "test",
66-
libraryDependencies += "org.apache.commons" % "commons-lang3" % "3.9" % "test",
67-
libraryDependencies += ("org.scala-lang" % "scala-compiler" % scalaVersion.value % "test").exclude("org.scala-lang.modules", s"scala-xml_${scalaBinaryVersion.value}")
85+
libraryDependencies += "junit" % "junit" % "4.13.2" % Test,
86+
libraryDependencies += "com.github.sbt" % "junit-interface" % "0.13.3" % Test,
87+
libraryDependencies += "org.apache.commons" % "commons-lang3" % "3.12.0" % Test,
88+
libraryDependencies += ("org.scala-lang" % "scala-compiler" % scalaVersion.value % Test).exclude("org.scala-lang.modules", s"scala-xml_${scalaBinaryVersion.value}")
6889
)
6990
.jsSettings(
91+
crossScalaVersions := crossScalaVersions.value.filterNot(_.startsWith("2.11")),
7092
// Scala.js cannot run forked tests
71-
fork in Test := false
93+
Test/ fork := false
7294
)
73-
.jsConfigure(_.enablePlugins(ScalaJSJUnitPlugin))
95+
.jsEnablePlugins(ScalaJSJUnitPlugin)

‎build.sh‎

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

‎project/build.properties‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
sbt.version=1.3.13
1+
sbt.version=1.9.2

‎project/plugins.sbt‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
valscalaJSVersion=
2-
Option(System.getenv("SCALAJS_VERSION")).filter(_.nonEmpty).getOrElse("0.6.33")
3-
4-
addSbtPlugin("org.scala-lang.modules" % "sbt-scala-module" % "2.1.3")
5-
addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.0.0")
6-
addSbtPlugin("org.scala-js" % "sbt-scalajs" % scalaJSVersion)
1+
addSbtPlugin("org.scala-lang.modules"%"sbt-scala-module"%"3.1.0")
2+
addSbtPlugin("org.portable-scala"%"sbt-scalajs-crossproject"%"1.3.2")
3+
addSbtPlugin("org.portable-scala"%"sbt-scala-native-crossproject"%"1.3.2")
4+
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.13.2")
5+
addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.14")
6+
addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.10.0")

‎shared/src/main/scala/scala/xml/Utility.scala‎

Lines changed: 65 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
package scala
1010
package xml
1111

12+
import scala.annotation.tailrec
1213
import scala.collection.mutable
1314
import scala.language.implicitConversions
1415
import scala.collection.Seq
@@ -187,9 +188,7 @@ object Utility extends AnyRef with parsing.TokenTests {
187188
decodeEntities: Boolean = true,
188189
preserveWhitespace: Boolean = false,
189190
minimizeTags: Boolean = false): StringBuilder =
190-
{
191191
serialize(x, pscope, sb, stripComments, decodeEntities, preserveWhitespace, if (minimizeTags) MinimizeMode.Always else MinimizeMode.Never)
192-
}
193192

194193
/**
195194
* Serialize an XML Node to a StringBuilder.
@@ -206,35 +205,66 @@ object Utility extends AnyRef with parsing.TokenTests {
206205
stripComments: Boolean = false,
207206
decodeEntities: Boolean = true,
208207
preserveWhitespace: Boolean = false,
209-
minimizeTags: MinimizeMode.Value = MinimizeMode.Default): StringBuilder =
210-
{
211-
x match {
212-
case c: Comment => if (!stripComments) c buildString sb; sb
213-
case s: SpecialNode => s buildString sb
214-
case g: Group =>
215-
for (c <- g.nodes) serialize(c, g.scope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags); sb
216-
case el: Elem =>
217-
// print tag with namespace declarations
218-
sb.append('<')
219-
el.nameToString(sb)
220-
if (el.attributes ne null) el.attributes.buildString(sb)
221-
el.scope.buildString(sb, pscope)
222-
if (el.child.isEmpty &&
223-
(minimizeTags == MinimizeMode.Always ||
224-
(minimizeTags == MinimizeMode.Default && el.minimizeEmpty))) {
225-
// no children, so use short form: <xyz .../>
226-
sb.append("/>")
227-
} else {
228-
// children, so use long form: <xyz ...>...</xyz>
229-
sb.append('>')
230-
sequenceToXML(el.child, el.scope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags)
231-
sb.append("</")
232-
el.nameToString(sb)
233-
sb.append('>')
234-
}
235-
case _ => throw new IllegalArgumentException("Don't know how to serialize a " + x.getClass.getName)
236-
}
208+
minimizeTags: MinimizeMode.Value = MinimizeMode.Default
209+
): StringBuilder = {
210+
serializeImpl(List(x), pscope, false, stripComments, minimizeTags, sb)
211+
sb
212+
}
213+
214+
private def serializeImpl(
215+
ns: Seq[Node],
216+
pscope: NamespaceBinding,
217+
spaced: Boolean,
218+
stripComments: Boolean,
219+
minimizeTags: MinimizeMode.Value,
220+
sb: StringBuilder
221+
): Unit = {
222+
@tailrec def ser(nss: List[List[Node]], pscopes: List[NamespaceBinding], spaced: List[Boolean], toClose: List[Node]): Unit = nss match {
223+
case List(Nil) =>
224+
case Nil :: rests =>
225+
if (toClose.head != null) {
226+
sb.append("</")
227+
toClose.head.nameToString(sb)
228+
sb.append('>')
229+
}
230+
ser(rests, pscopes.tail, spaced.tail, toClose.tail)
231+
case (n :: ns) :: r =>
232+
def sp(): Unit = if (ns.nonEmpty && spaced.head) sb.append(' ')
233+
n match {
234+
case c: Comment =>
235+
if (!stripComments) {
236+
c.buildString(sb)
237+
sp()
238+
}
239+
ser(ns :: r, pscopes, spaced, toClose)
240+
case s: SpecialNode =>
241+
s.buildString(sb)
242+
sp()
243+
ser(ns :: r, pscopes, spaced, toClose)
244+
case g: Group =>
245+
ser(g.nodes.toList :: ns :: r, g.scope :: pscopes, false :: spaced, null :: toClose)
246+
case e: Elem =>
247+
sb.append('<')
248+
e.nameToString(sb)
249+
if (e.attributes.ne(null)) e.attributes.buildString(sb)
250+
e.scope.buildString(sb, pscopes.head)
251+
if (e.child.isEmpty &&
252+
(minimizeTags == MinimizeMode.Always ||
253+
(minimizeTags == MinimizeMode.Default && e.minimizeEmpty))) {
254+
// no children, so use short form: <xyz .../>
255+
sb.append("/>")
256+
sp()
257+
ser(ns :: r, pscopes, spaced, toClose)
258+
} else {
259+
sb.append('>')
260+
val csp = e.child.forall(isAtomAndNotText)
261+
ser(e.child.toList :: ns :: r, e.scope :: pscopes, csp :: spaced, e :: toClose)
262+
}
263+
case n => throw new IllegalArgumentException("Don't know how to serialize a " + n.getClass.getName)
264+
}
237265
}
266+
ser(List(ns.toList), List(pscope), List(spaced), Nil)
267+
}
238268

239269
def sequenceToXML(
240270
children: Seq[Node],
@@ -243,20 +273,11 @@ object Utility extends AnyRef with parsing.TokenTests {
243273
stripComments: Boolean = false,
244274
decodeEntities: Boolean = true,
245275
preserveWhitespace: Boolean = false,
246-
minimizeTags: MinimizeMode.Value = MinimizeMode.Default): Unit =
247-
{
248-
if (children.isEmpty) return
249-
else if (children forall isAtomAndNotText) { // add space
250-
val it = children.iterator
251-
val f = it.next()
252-
serialize(f, pscope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags)
253-
while (it.hasNext) {
254-
val x = it.next()
255-
sb.append(' ')
256-
serialize(x, pscope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags)
257-
}
258-
} else children foreach { serialize(_, pscope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags) }
259-
}
276+
minimizeTags: MinimizeMode.Value = MinimizeMode.Default
277+
): Unit = if (children.nonEmpty) {
278+
val spaced = children.forall(isAtomAndNotText)
279+
serializeImpl(children, pscope, spaced, stripComments, minimizeTags, sb)
280+
}
260281

261282
/**
262283
* Returns prefix of qualified name if any.

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /