@@ -30,6 +30,13 @@ class HjsonWriter {
3030
3131 private IHjsonDsfProvider [] dsfProviders ;
3232
33+ static String commonRange = "\\ x7f-\\ x9f\\ x{00ad}\\ x{0600}-\\ x{0604}\\ x{070f}\\ x{17b4}\\ x{17b5}\\ x{200c}-\\ x{200f}\\ x{2028}-\\ x{202f}\\ x{2060}-\\ x{206f}\\ x{feff}\\ x{fff0}-\\ x{ffff}" ;
34+ // needsEscape tests if the string can be written without escapes
35+ static Pattern needsEscape = Pattern .compile ("[\\ \\ \\ \" \\ x00-\\ x1f" + commonRange + "]" );
36+ // needsQuotes tests if the string can be written as a quoteless string (includes needsEscape but without \\\\ and \\")
37+ static Pattern needsQuotes = Pattern .compile ("^\\ s|^\" |^'|^#|^/\\ *|^//|^\\ {|^\\ }|^\\ [|^\\ ]|^:|^,|\\ s$|[\\ x00-\\ x1f\\ x7f-\\ x9f\\ x{00ad}\\ x{0600}-\\ x{0604}\\ x{070f}\\ x{17b4}\\ x{17b5}\\ x{200c}-\\ x{200f}\\ x{2028}-\\ x{202f}\\ x{2060}-\\ x{206f}\\ x{feff}\\ x{fff0}-\\ x{ffff}]" );
38+ // needsEscapeML tests if the string can be written as a multiline string (like needsEscape but without \\n, \\\\, \\", \\t)
39+ static Pattern needsEscapeML = Pattern .compile ("'''|^[\\ s]+$|[\\ x00-\\ x08\\ x0b-\\ x1f" + commonRange + "]" );
3340 static Pattern needsEscapeName =Pattern .compile ("[,\\ {\\ [\\ }\\ ]\\ s:#\" ']|//|/\\ *" );
3441
3542 public HjsonWriter (HjsonOptions options ) {
@@ -104,22 +111,21 @@ public void save(JsonValue value, Writer tw, int level, String separator, boolea
104111 }
105112
106113 static String escapeName (String name ) {
107- if (name .length ()==0 || needsEscapeName .matcher (name ).find ())
114+ if (name .length ()==0 || needsEscapeName .matcher (name ).find () ||
115+ needsEscape .matcher (name ).find ())
116+ {
108117 return "\" " +JsonWriter .escapeString (name )+"\" " ;
109- else
118+ } else {
110119 return name ;
120+ }
111121 }
112122
113123 void writeString (String value , Writer tw , int level , String separator ) throws IOException {
114124 if (value .length ()==0 ) { tw .write (separator +"\" \" " ); return ; }
115125
116126 char left =value .charAt (0 ), right =value .charAt (value .length ()-1 );
117127 char left1 =value .length ()>1 ?value .charAt (1 ):'\0' , left2 =value .length ()>2 ?value .charAt (2 ):'\0' ;
118- boolean doEscape =false ;
119- char [] valuec =value .toCharArray ();
120- for (char ch : valuec ) {
121- if (needsQuotes (ch )) { doEscape =true ; break ; }
122- }
128+ boolean doEscape =needsQuotes .matcher (value ).find ();
123129
124130 if (doEscape ||
125131 HjsonParser .isWhiteSpace (left ) || HjsonParser .isWhiteSpace (right ) ||
@@ -136,17 +142,13 @@ void writeString(String value, Writer tw, int level, String separator) throws IO
136142 // format or we must replace the offending characters with safe escape
137143 // sequences.
138144
139- boolean noEscape =true ;
140- for (char ch : valuec ) { if (needsEscape (ch )) { noEscape =false ; break ; } }
141- if (noEscape ) { tw .write (separator +"\" " +value +"\" " ); return ; }
142-
143- boolean noEscapeML =true , allWhite =true ;
144- for (char ch : valuec ) {
145- if (needsEscapeML (ch )) { noEscapeML =false ; break ; }
146- else if (!HjsonParser .isWhiteSpace (ch )) allWhite =false ;
145+ if (!needsEscape .matcher (value ).find ()) {
146+ tw .write (separator +"\" " +value +"\" " );
147+ } else if (!needsEscapeML .matcher (value ).find ()) {
148+ writeMLString (value , tw , level , separator );
149+ } else {
150+ tw .write (separator +"\" " +JsonWriter .escapeString (value )+"\" " );
147151 }
148- if (noEscapeML && !allWhite && !value .contains ("'''" )) writeMLString (value , tw , level , separator );
149- else tw .write (separator +"\" " +JsonWriter .escapeString (value )+"\" " );
150152 }
151153 else tw .write (separator +value );
152154 }
@@ -183,38 +185,4 @@ static boolean startsWithKeyword(String text) {
183185 char ch =text .charAt (p );
184186 return ch ==',' || ch =='}' || ch ==']' || ch =='#' || ch =='/' && (text .length ()>p +1 && (text .charAt (p +1 )=='/' || text .charAt (p +1 )=='*' ));
185187 }
186-
187- static boolean needsQuotes (char c ) {
188- switch (c ) {
189- case '\t' :
190- case '\f' :
191- case '\b' :
192- case '\n' :
193- case '\r' :
194- return true ;
195- default :
196- return false ;
197- }
198- }
199-
200- static boolean needsEscape (char c ) {
201- switch (c ) {
202- case '\"' :
203- case '\\' :
204- return true ;
205- default :
206- return needsQuotes (c );
207- }
208- }
209-
210- static boolean needsEscapeML (char c ) {
211- switch (c ) {
212- case '\n' :
213- case '\r' :
214- case '\t' :
215- return false ;
216- default :
217- return needsQuotes (c );
218- }
219- }
220188}
0 commit comments