1
1
package scoverage
2
2
3
- import java .io ._
3
+ import java .io .{ BufferedWriter , File , FileOutputStream , OutputStreamWriter , Writer }
4
4
5
- import scala .io .Source
6
- import scala .xml .{Utility , XML }
5
+ import scala .io .{Codec , Source }
7
6
8
7
object Serializer {
9
8
@@ -12,147 +11,109 @@ object Serializer {
12
11
13
12
// Write out coverage data to given file.
14
13
def serialize (coverage : Coverage , file : File ): Unit = {
15
- val writer = new BufferedWriter (new FileWriter (file))
16
- serialize(coverage, writer)
17
- writer.close()
14
+ val writer : Writer = new BufferedWriter (new OutputStreamWriter (new FileOutputStream (file), Codec .UTF8 .name))
15
+ try {
16
+ serialize(coverage, writer)
17
+ }
18
+ finally {
19
+ writer.flush()
20
+ writer.close()
21
+ }
18
22
}
19
23
20
24
def serialize (coverage : Coverage , writer : Writer ): Unit = {
25
+ def writeHeader (writer : Writer ): Unit = {
26
+ writer.write(s """ # Coverage data, format version: 2.0
27
+ |# Statement data:
28
+ |# - id
29
+ |# - source path
30
+ |# - package name
31
+ |# - class name
32
+ |# - class type (Class, Object or Trait)
33
+ |# - full class name
34
+ |# - method name
35
+ |# - start offset
36
+ |# - end offset
37
+ |# - line number
38
+ |# - symbol name
39
+ |# - tree name
40
+ |# - is branch
41
+ |# - invocations count
42
+ |# - is ignored
43
+ |# - description (can be multi-line)
44
+ |# '\f' sign
45
+ |# ------------------------------------------
46
+ | """ .stripMargin)
47
+ }
48
+
21
49
def writeStatement (stmt : Statement , writer : Writer ): Unit = {
22
- writer.write {
23
- val xml = <statement >
24
- <source >
25
- {stmt.source}
26
- </source >
27
- <package >
28
- {stmt.location.packageName}
29
- </package >
30
- <class >
31
- {stmt.location.className}
32
- </class >
33
- <classType >
34
- {stmt.location.classType.toString}
35
- </classType >
36
- <fullClassName >
37
- {stmt.location.fullClassName}
38
- </fullClassName >
39
- <method >
40
- {stmt.location.method}
41
- </method >
42
- <path >
43
- {stmt.location.sourcePath}
44
- </path >
45
- <id >
46
- {stmt.id.toString}
47
- </id >
48
- <start >
49
- {stmt.start.toString}
50
- </start >
51
- <end >
52
- {stmt.end.toString}
53
- </end >
54
- <line >
55
- {stmt.line.toString}
56
- </line >
57
- <description >
58
- {escape(stmt.desc)}
59
- </description >
60
- <symbolName >
61
- {escape(stmt.symbolName)}
62
- </symbolName >
63
- <treeName >
64
- {escape(stmt.treeName)}
65
- </treeName >
66
- <branch >
67
- {stmt.branch.toString}
68
- </branch >
69
- <count >
70
- {stmt.count.toString}
71
- </count >
72
- <ignored >
73
- {stmt.ignored.toString}
74
- </ignored >
75
- </statement >
76
- Utility .trim(xml) + " \n "
77
- }
50
+ writer.write(s """ ${stmt.id}
51
+ | ${stmt.location.sourcePath}
52
+ | ${stmt.location.packageName}
53
+ | ${stmt.location.className}
54
+ | ${stmt.location.classType}
55
+ | ${stmt.location.fullClassName}
56
+ | ${stmt.location.method}
57
+ | ${stmt.start}
58
+ | ${stmt.end}
59
+ | ${stmt.line}
60
+ | ${stmt.symbolName}
61
+ | ${stmt.treeName}
62
+ | ${stmt.branch}
63
+ | ${stmt.count}
64
+ | ${stmt.ignored}
65
+ | ${stmt.desc}
66
+ |\f
67
+ | """ .stripMargin)
78
68
}
79
- writer.write(" <statements>\n " )
69
+
70
+ writeHeader(writer)
80
71
coverage.statements.foreach(stmt => writeStatement(stmt, writer))
81
- writer.write(" </statements>" )
82
72
}
83
73
84
74
def coverageFile (dataDir : File ): File = coverageFile(dataDir.getAbsolutePath)
85
75
def coverageFile (dataDir : String ): File = new File (dataDir, Constants .CoverageFileName )
86
76
87
- def deserialize (str : String ): Coverage = {
88
- val xml = XML .loadString(str)
89
- val statements = xml \ " statement" map (node => {
90
- val source = (node \ " source" ).text
91
- val count = (node \ " count" ).text.toInt
92
- val ignored = (node \ " ignored" ).text.toBoolean
93
- val branch = (node \ " branch" ).text.toBoolean
94
- val _package = (node \ " package" ).text
95
- val _class = (node \ " class" ).text
96
- val fullClassName = (node \ " fullClassName" ).text
97
- val method = (node \ " method" ).text
98
- val path = (node \ " path" ).text
99
- val treeName = (node \ " treeName" ).text
100
- val symbolName = (node \ " symbolName" ).text
101
- val id = (node \ " id" ).text.toInt
102
- val line = (node \ " line" ).text.toInt
103
- val desc = (node \ " description" ).text
104
- val start = (node \ " start" ).text.toInt
105
- val end = (node \ " end" ).text.toInt
106
- val classType = (node \ " classType" ).text match {
107
- case " Trait" => ClassType .Trait
108
- case " Object" => ClassType .Object
109
- case _ => ClassType .Class
110
- }
111
- Statement (
112
- Location (_package, _class, fullClassName, classType, method, path),
113
- id,
114
- start,
115
- end,
116
- line,
117
- desc,
118
- symbolName,
119
- treeName, branch, count, ignored)
120
- })
121
-
122
- val coverage = Coverage ()
123
- for ( statement <- statements )
124
- if (statement.ignored) coverage.addIgnoredStatement(statement)
125
- else coverage.add(statement)
126
- coverage
127
- }
128
-
129
77
def deserialize (file : File ): Coverage = {
130
- val str = Source .fromFile(file).mkString
131
- deserialize(str)
78
+ deserialize(Source .fromFile(file)(Codec .UTF8 ).getLines)
132
79
}
133
80
134
- /**
135
- * This method ensures that the output String has only
136
- * valid XML unicode characters as specified by the
137
- * XML 1.0 standard. For reference, please see
138
- * <a href="http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char">the
139
- * standard</a>. This method will return an empty
140
- * String if the input is null or empty.
141
- *
142
- * @param in The String whose non-valid characters we want to remove.
143
- * @return The in String, stripped of non-valid characters.
144
- * @see http://blog.mark-mclaren.info/2007/02/invalid-xml-characters-when-valid-utf8_5873.html
145
- *
146
- */
147
- def escape (in : String ): String = {
148
- val out = new StringBuilder ()
149
- for ( current <- Option (in).getOrElse(" " ).toCharArray ) {
150
- if ((current == 0x9 ) || (current == 0xA ) || (current == 0xD ) ||
151
- ((current >= 0x20 ) && (current <= 0xD7FF )) ||
152
- ((current >= 0xE000 ) && (current <= 0xFFFD )) ||
153
- ((current >= 0x10000 ) && (current <= 0x10FFFF )))
154
- out.append(current)
81
+ def deserialize (lines : Iterator [String ]): Coverage = {
82
+ def toStatement (lines : Iterator [String ]): Statement = {
83
+ val id : Int = lines.next.toInt
84
+ val sourcePath = lines.next
85
+ val packageName = lines.next
86
+ val className = lines.next
87
+ val classType = lines.next
88
+ val fullClassName = lines.next
89
+ val method = lines.next
90
+ val loc = Location (packageName, className, fullClassName, ClassType .fromString(classType), method, sourcePath)
91
+ val start : Int = lines.next.toInt
92
+ val end : Int = lines.next.toInt
93
+ val lineNo : Int = lines.next.toInt
94
+ val symbolName : String = lines.next
95
+ val treeName : String = lines.next
96
+ val branch : Boolean = lines.next.toBoolean
97
+ val count : Int = lines.next.toInt
98
+ val ignored : Boolean = lines.next.toBoolean
99
+ val desc = lines.toList.mkString(" \n " )
100
+ Statement (loc, id, start, end, lineNo, desc, symbolName, treeName, branch, count, ignored)
101
+ }
102
+
103
+ val headerFirstLine = lines.next
104
+ require(headerFirstLine == " # Coverage data, format version: 2.0" , " Wrong file format" )
105
+
106
+ val linesWithoutHeader = lines.dropWhile(_.startsWith(" #" ))
107
+ val coverage = Coverage ()
108
+ while (! linesWithoutHeader.isEmpty) {
109
+ val oneStatementLines = linesWithoutHeader.takeWhile(_ != " \f " )
110
+ val statement = toStatement(oneStatementLines)
111
+ if (statement.ignored)
112
+ coverage.addIgnoredStatement(statement)
113
+ else
114
+ coverage.add(statement)
155
115
}
156
- out.mkString
116
+ coverage
157
117
}
118
+
158
119
}
0 commit comments