summaryrefslogtreecommitdiff
path: root/src/library/scala/collection/TraversableLike.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/library/scala/collection/TraversableLike.scala')
-rw-r--r--src/library/scala/collection/TraversableLike.scala71
1 files changed, 59 insertions, 12 deletions
diff --git a/src/library/scala/collection/TraversableLike.scala b/src/library/scala/collection/TraversableLike.scala
index be2f427ea4..c9482fe0a2 100644
--- a/src/library/scala/collection/TraversableLike.scala
+++ b/src/library/scala/collection/TraversableLike.scala
@@ -605,22 +605,69 @@ trait TraversableLike[+A, +Repr] extends Any
* applied to this $coll. By default the string prefix is the
* simple name of the collection class $coll.
*/
- def stringPrefix : String = {
+ def stringPrefix: String = {
+ /* This method is written in a style that avoids calling `String.split()`
+ * as well as methods of java.lang.Character that require the Unicode
+ * database information. This is mostly important for Scala.js, so that
+ * using the collection library does automatically bring java.util.regex.*
+ * and the Unicode database in the generated code.
+ *
+ * This algorithm has the additional benefit that it won't allocate
+ * anything except the result String in the common case, where the class
+ * is not an inner class (i.e., when the result contains no '.').
+ */
val fqn = repr.getClass.getName
- val cls = {
- val idx1 = fqn.lastIndexOf('.' : Int)
- if (idx1 != -1) fqn.substring(idx1 + 1) else fqn
+ var pos: Int = fqn.length - 1
+
+ // Skip trailing $'s
+ while (pos != -1 && fqn.charAt(pos) == '$') {
+ pos -= 1
+ }
+ if (pos == -1 || fqn.charAt(pos) == '.') {
+ return ""
}
- val parts = cls.split('$')
- val last = parts.length - 1
- parts.zipWithIndex.foldLeft("") { case (z, (s, i)) =>
- if (s.isEmpty) z
- else if (i != last && s.forall(java.lang.Character.isDigit)) "" // drop prefix in method-local classes
- else if (i == 0 || java.lang.Character.isUpperCase(s.charAt(0))) {
- if (z.isEmpty) s else z + '.' + s
+
+ var result: String = ""
+ while (true) {
+ // Invariant: if we enter the loop, there is a non-empty part
+
+ // Look for the beginning of the part, remembering where was the last non-digit
+ val partEnd = pos + 1
+ while (pos != -1 && fqn.charAt(pos) <= '9' && fqn.charAt(pos) >= '0') {
+ pos -= 1
+ }
+ val lastNonDigit = pos
+ while (pos != -1 && fqn.charAt(pos) != '$' && fqn.charAt(pos) != '.') {
+ pos -= 1
+ }
+ val partStart = pos + 1
+
+ // A non-last part which contains only digits marks a method-local part -> drop the prefix
+ if (pos == lastNonDigit && partEnd != fqn.length) {
+ return result
+ }
+
+ // Skip to the next part, and determine whether we are the end
+ while (pos != -1 && fqn.charAt(pos) == '$') {
+ pos -= 1
+ }
+ val atEnd = pos == -1 || fqn.charAt(pos) == '.'
+
+ // Handle the actual content of the part (we ignore parts that are likely synthetic)
+ def isPartLikelySynthetic = {
+ val firstChar = fqn.charAt(partStart)
+ (firstChar > 'Z' && firstChar < 0x7f) || (firstChar < 'A')
+ }
+ if (atEnd || !isPartLikelySynthetic) {
+ val part = fqn.substring(partStart, partEnd)
+ result = if (result.isEmpty) part else part + '.' + result
+ if (atEnd)
+ return result
}
- else z
}
+
+ // dead code
+ result
}
/** Creates a non-strict view of this $coll.