Skip to content

Commit 4ff40ea

Browse files
committed
Fix Invalid Comment Edge Case
According to [the XML specification](https://www.w3.org/TR/xml11//#IDA5CES "W3 XML") XML comments can not end in `--->`. Prior to this commit creating a comment of the form `Comment("invalid-")` would yield no error though it should, since it is rendered as `<!--invalid--->`. This commit makes such `String` values yield an `IllegalArgumentException`. A companion object was also added with two helper methods, `validated` and `validatedOpt`, which allow for a user of the library to check if a `String` is valid without having to `catch` the `IllegalArgumentException`.
1 parent 0125cf6 commit 4ff40ea

File tree

2 files changed

+111
-2
lines changed

2 files changed

+111
-2
lines changed

shared/src/main/scala/scala/xml/Comment.scala

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ package xml
1414
*
1515
* @author Burak Emir
1616
* @param commentText the text contained in this node, may not contain "--"
17+
* and the final character may not be `-` to prevent a closing span of `-->`
18+
* which is invalid. [[https://www.w3.org/TR/xml11//#IDA5CES]]
1719
*/
1820
case class Comment(commentText: String) extends SpecialNode {
1921

@@ -22,12 +24,81 @@ case class Comment(commentText: String) extends SpecialNode {
2224
final override def doCollectNamespaces = false
2325
final override def doTransform = false
2426

25-
if (commentText contains "--")
26-
throw new IllegalArgumentException("text contains \"--\"")
27+
// Validate the commentText. We call the `Comment.validate` method because
28+
// that shares code with the more safe constructs, e.g. `Comment.validated`
29+
// and `Comment.validatedOpt`.
30+
Comment.validate(commentText).fold(
31+
()
32+
)(e =>
33+
throw e
34+
)
2735

2836
/**
2937
* Appends &quot;<!-- text -->&quot; to this string buffer.
3038
*/
3139
override def buildString(sb: StringBuilder) =
3240
sb append "<!--" append commentText append "-->"
3341
}
42+
43+
// We have to extends `scala.runtime.AbstractFunction1[String, Comment]` and
44+
// override `apply` if we don't want to break binary compatibility.
45+
object Comment extends scala.runtime.AbstractFunction1[String, Comment]{
46+
47+
override final def apply(commentText: String): Comment =
48+
new Comment(commentText)
49+
50+
/** Validates if a given `String` can be a valid XML comment.
51+
*
52+
* There are two related cases where a `String` is invalid. The first is
53+
* when the `String` `"--"` occurs in the body of the text. The second is
54+
* when the final character in the comment is `'-'`. In the second case
55+
* this is invalid because it would yield a XML encoding of `-->` for the
56+
* final three characters of the rendered comment which invalidates the
57+
* first rule of not having `"--"` in the comment.
58+
*
59+
* @see [[https://www.w3.org/TR/xml11//#IDA5CES]]
60+
*/
61+
private def validate(commentText: String): Option[Throwable] =
62+
if (commentText.isEmpty) {
63+
None
64+
} else if (commentText.contains("--")) {
65+
Some(new IllegalArgumentException("text contains \"--\""))
66+
} else if (commentText.charAt(commentText.length - 1) == '-') {
67+
Some(new IllegalArgumentException("The final character of a XML comment may not be '-'. See https://www.w3.org/TR/xml11//#IDA5CES"))
68+
} else {
69+
None
70+
}
71+
72+
/** Validates if a given `String` can be a valid XML comment, yielding a
73+
* `Left` `Throwable` if it is invalid.
74+
*
75+
* There are two related cases where a `String` is invalid. The first is
76+
* when the `String` `"--"` occurs in the body of the text. The second is
77+
* when the final character in the comment is `'-'`. In the second case
78+
* this is invalid because it would yield a XML encoding of `-->` for the
79+
* final three characters of the rendered comment which invalidates the
80+
* first rule of not having `"--"` in the comment.
81+
*
82+
* @see [[https://www.w3.org/TR/xml11//#IDA5CES]]
83+
*/
84+
def validated(commentText: String): Either[Throwable, Comment] =
85+
validate(commentText).fold(
86+
Right(Comment(commentText)): Either[Throwable, Comment]
87+
)(t => Left(t))
88+
89+
90+
/** Validates if a given `String` can be a valid XML comment, `None` if it is
91+
* invalid.
92+
*
93+
* There are two related cases where a `String` is invalid. The first is
94+
* when the `String` `"--"` occurs in the body of the text. The second is
95+
* when the final character in the comment is `'-'`. In the second case
96+
* this is invalid because it would yield a XML encoding of `-->` for the
97+
* final three characters of the rendered comment which invalidates the
98+
* first rule of not having `"--"` in the comment.
99+
*
100+
* @see [[https://www.w3.org/TR/xml11//#IDA5CES]]
101+
*/
102+
def validatedOpt(commentText: String): Option[Comment] =
103+
validated(commentText).toOption
104+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package scala.xml
2+
3+
import org.junit.Assert.assertEquals
4+
import org.junit.Assert.assertTrue
5+
import org.junit.Test
6+
7+
final class CommentTest {
8+
9+
@Test(expected=classOf[IllegalArgumentException])
10+
def invalidCommentWithTwoDashes: Unit = {
11+
val invalid: String = "invalid--comment"
12+
assertTrue(Comment.validated(invalid).isLeft)
13+
assertTrue(Comment.validatedOpt(invalid).isEmpty)
14+
Comment(invalid)
15+
}
16+
17+
@Test(expected=classOf[IllegalArgumentException])
18+
def invalidCommentWithFinalDash: Unit = {
19+
val invalid: String = "invalid comment-"
20+
assertTrue(Comment.validated(invalid).isLeft)
21+
assertTrue(Comment.validatedOpt(invalid).isEmpty)
22+
Comment(invalid)
23+
}
24+
25+
@Test
26+
def validCommentWithDash: Unit = {
27+
val valid: String = "valid-comment"
28+
assertEquals(Right(Comment("valid-comment")), Comment.validated(valid))
29+
assertEquals(Some(Comment("valid-comment")), Comment.validatedOpt(valid))
30+
}
31+
32+
@Test
33+
def validEmptyComment: Unit = {
34+
val valid: String = ""
35+
assertEquals(Right(Comment("")), Comment.validated(valid))
36+
assertEquals(Some(Comment("")), Comment.validatedOpt(valid))
37+
}
38+
}

0 commit comments

Comments
 (0)