Skip to content

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
importsbtcrossproject.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+
lazyvalconfigSettings:Seq[Setting[?]] =Seq(
13+
unmanagedSourceDirectories ++={
14+
unmanagedSourceDirectories.value.flatMap{dir =>
15+
defforVersion(version: String):File= file(dir.getPath ++"-"++ version)
16+
CrossVersion.partialVersion(scalaVersion.value) match{
17+
caseSome((2, 13)) =>Seq(forVersion("2.13"))
18+
case _ =>Seq(forVersion("2.11-2.12"))
19+
}
20+
}
21+
}
22+
)
23+
324
lazyvalxml= 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-
elseSome("1.2.0")
20-
},
38+
Test/ scalacOptions +="-Xxml:coalescing",
2139

22-
unmanagedSourceDirectories in Compile++={
23-
(unmanagedSourceDirectories in Compile).value.map{dir =>
24-
valsv= scalaVersion.value
25-
CrossVersion.partialVersion(sv) match{
26-
caseSome((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
packagescala
1010
packagexml
1111

12+
importscala.annotation.tailrec
1213
importscala.collection.mutable
1314
importscala.language.implicitConversions
1415
importscala.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.AlwayselseMinimizeMode.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-
casec: Comment=>if (!stripComments) c buildString sb; sb
213-
cases: SpecialNode=> s buildString sb
214-
caseg: Group=>
215-
for (c <- g.nodes) serialize(c, g.scope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags); sb
216-
caseel: 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 _ =>thrownewIllegalArgumentException("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+
privatedefserializeImpl(
215+
ns: Seq[Node],
216+
pscope: NamespaceBinding,
217+
spaced: Boolean,
218+
stripComments: Boolean,
219+
minimizeTags: MinimizeMode.Value,
220+
sb: StringBuilder
221+
):Unit={
222+
@tailrec defser(nss: List[List[Node]], pscopes: List[NamespaceBinding], spaced: List[Boolean], toClose: List[Node]):Unit= nss match{
223+
caseList(Nil) =>
224+
caseNil:: 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+
defsp():Unit=if (ns.nonEmpty && spaced.head) sb.append(' ')
233+
n match{
234+
casec: Comment=>
235+
if (!stripComments){
236+
c.buildString(sb)
237+
sp()
238+
}
239+
ser(ns :: r, pscopes, spaced, toClose)
240+
cases: SpecialNode=>
241+
s.buildString(sb)
242+
sp()
243+
ser(ns :: r, pscopes, spaced, toClose)
244+
caseg: Group=>
245+
ser(g.nodes.toList :: ns :: r, g.scope :: pscopes, false:: spaced, null:: toClose)
246+
casee: 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+
valcsp= e.child.forall(isAtomAndNotText)
261+
ser(e.child.toList :: ns :: r, e.scope :: pscopes, csp :: spaced, e :: toClose)
262+
}
263+
case n =>thrownewIllegalArgumentException("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
defsequenceToXML(
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-
elseif (children forall isAtomAndNotText){// add space
250-
valit= children.iterator
251-
valf= it.next()
252-
serialize(f, pscope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags)
253-
while (it.hasNext){
254-
valx= 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+
valspaced= 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)