11package scoverage
22
3- import java .io ._
3+ import java .io .{ BufferedWriter , File , FileOutputStream , OutputStreamWriter , Writer }
44
5- import scala .io .Source
6- import scala .xml .{Utility , XML }
5+ import scala .io .{Codec , Source }
76
87object Serializer {
98
@@ -12,147 +11,109 @@ object Serializer {
1211
1312 // Write out coverage data to given file.
1413 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+ }
1822 }
1923
2024 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+
2149 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)
7868 }
79- writer.write(" <statements>\n " )
69+
70+ writeHeader(writer)
8071 coverage.statements.foreach(stmt => writeStatement(stmt, writer))
81- writer.write(" </statements>" )
8272 }
8373
8474 def coverageFile (dataDir : File ): File = coverageFile(dataDir.getAbsolutePath)
8575 def coverageFile (dataDir : String ): File = new File (dataDir, Constants .CoverageFileName )
8676
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-
12977 def deserialize (file : File ): Coverage = {
130- val str = Source .fromFile(file).mkString
131- deserialize(str)
78+ deserialize(Source .fromFile(file)(Codec .UTF8 ).getLines)
13279 }
13380
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)
155115 }
156- out.mkString
116+ coverage
157117 }
118+
158119}
0 commit comments