5656 */
5757public class AmdJsResource extends ResourceFilter {
5858
59- private static String KEY_LINE_PATTERN = "^\\ s*\" .+\" *:.*" ;
60- private static String ENTRY_END_LINE_PATTERN = ".*[\\ },]\\ s*" ;
61- private static String CLOSE_BRACE_PATTERN = ".*}.*" ;
59+ private static final String KEY_LINE_PATTERN = "^\\ s*\" .+\" *:.*" ;
60+ private static final String ENTRY_END_LINE_PATTERN = ".*[\\ },]\\ s*" ;
61+ private static final String CLOSE_BRACE_PATTERN = ".*}.*" ;
62+
63+ static final int MAX_COLUMNS = 80 ;
6264
6365 private static class KeyValueVisitor implements NodeVisitor {
6466 LinkedHashMap <String , String > elements = new LinkedHashMap <String , String >();
@@ -255,8 +257,10 @@ public void merge(InputStream baseStream, OutputStream outStream, LanguageBundle
255257
256258 if (kvMap .containsKey (key )) {
257259 String value = kvMap .get (key );
258- String tabPrefix = extractTabPrefix (left );
259- pw .write (formatEntry (key , value , tabPrefix , brkItr ));
260+ String baseIndent = extractTabPrefix (left );
261+ String indent = getTabStr (baseIndent );
262+
263+ pw .write (formatEntry (key , value ,'"' , MAX_COLUMNS , baseIndent , indent , brkItr ));
260264
261265 while (!line .matches (ENTRY_END_LINE_PATTERN ) && !in .hasNext (CLOSE_BRACE_PATTERN )) {
262266 line = in .nextLine ();
@@ -278,96 +282,133 @@ public void merge(InputStream baseStream, OutputStream outStream, LanguageBundle
278282 }
279283
280284 /**
281- * Formats the value into the format:
282- *
283- * \<tabPrefix\>"key" : "value",
284- *
285- * Entry may be split onto multiple lines in the form: "bear 1": "Brown " +
286- * "Bear"
285+ * Format a pair of resource key/value into the format:
286+ * "key1" : "val1"
287+ *
288+ * @param key The resource key
289+ * @param value The resource value
290+ * @param quote The String quotation character, usually " or '
291+ * @param maxColumn The maximum column to be used. Note, if a single word is longer
292+ * than this value, the line including the word may exceeds this maximum
293+ * column width.
294+ * @param baseIndent The base indent, inserted before key
295+ * @param indent The indent used for continuation line. The 2nd or later lines will
296+ * start with baseIndent + indent
297+ * @param brkItr The break iterator to be used for separating key/value text if necessary
298+ * @return A formatted resource key/value string
287299 */
288- private static String formatEntry (String key , String value , String tabPrefix , BreakIterator brkItr ) {
289- int maxLineLen = 80 ;
300+ static String formatEntry (String key , String value , char quote , int maxColumn , String baseIndent , String indent , BreakIterator brkItr ) {
301+ if (maxColumn < baseIndent .length () + indent .length () + 5 /* quotes and separator */ ) {
302+ throw new IllegalArgumentException ("Not enough columns to format key/value." );
303+ }
290304
291- StringBuilder output = new StringBuilder ();
305+ final int baseIndentWidth = getSpacesWidth (baseIndent );
306+ final int indentWidth = getSpacesWidth (indent );
292307
293- int keyLen = key .length ();
294- int valueLen = value .length ();
308+ StringBuilder output = new StringBuilder ();
309+ int remain = maxColumn ;
310+
311+ // Emit base indent
312+ output .append (baseIndent );
313+ remain -= baseIndentWidth ;
314+
315+ // Emit opening quote
316+ output .append (quote );
317+ remain --;
318+
319+ int idx = 0 ;
320+
321+ // Emit key
322+ String s = extractText (key , idx , remain - 1 /* space for closing quote */ , brkItr );
323+ idx += s .length ();
324+ output .append (s ).append (quote );
325+ remain -= (s .length () + 1 );
326+ if (idx < key .length ()) {
327+ // Process continuation lines if required
328+ while (idx < key .length ()) {
329+ output .append ('\n' ).append (baseIndent ).append (indent ).append ("+ " ).append (quote );
330+ remain = maxColumn - baseIndentWidth - indentWidth - 3 /* +<sp><quote> */ ;
331+ s = extractText (key , idx , remain - 1 , brkItr );
332+ idx += s .length ();
333+ output .append (s ).append (quote );
334+ remain -= (s .length () + 1 );
335+ }
336+ }
295337
296- // entry fits on one line
297- if (maxLineLen > keyLen + valueLen + tabPrefix .length () + 7 ) {
298- return output .append (tabPrefix ).append ('"' ).append (key ).append ("\" : \" " ).append (value ).append ('"' )
299- .toString ();
338+ // Emit separator
339+ if (remain < 3 /* <sp>:<sp>*/ ) {
340+ // emit separator to next line
341+ output .append ('\n' ).append (baseIndent ).append (indent ).append (": " );
342+ remain = maxColumn - baseIndentWidth - indentWidth - 2 /* :<sp> */ ;
343+ } else {
344+ // emit separator in the same line
345+ output .append (" : " );
346+ remain -= 3 ;
300347 }
301348
302- // message needs to be split onto multiple lines
303- output .append (tabPrefix );
304-
305- // the available char space once we account for the tabbing
306- // spaces and other necessary chars such as quotes
307- int available = maxLineLen - getSpacesSize (tabPrefix ) - 2 ;
308-
309- // the tab prefix for multi-lined entries
310- String tabStr = tabPrefix + getTabStr (tabPrefix );
311-
312- // actual size of prefix, i.e. tabs count as 4 spaces
313- int tabStrSize = getSpacesSize (tabStr );
314-
315- // process the key first
316- // splitting it into multiple lines if necessary
317- brkItr .setText (key );
318- int start = 0 ;
319- int end = brkItr .first ();
320- int prevEnd = end ;
321- boolean firstLine = true ;
322- while (end != BreakIterator .DONE ) {
323- prevEnd = end ;
324- end = brkItr .next ();
325- if (end - start > available ) {
326- output .append ('"' ).append (key .substring (start , prevEnd )).append ('"' ).append ('\n' ).append (tabStr )
327- .append ("+ " );
328- start = prevEnd ;
329-
330- // after first line, indent subsequent lines with 4 additional
331- // spaces
332- if (firstLine ) {
333- available = maxLineLen - tabStrSize - 5 ;
334- firstLine = false ;
335- }
336- } else if (end == keyLen ) {
337- output .append ('"' ).append (key .substring (start , end )).append ("\" : " );
338- available = available - 5 - (end - start );
339- }
349+ // Emit first substring from value
350+ idx = 0 ;
351+ s = extractText (value , idx , remain - 2 /* space for opening and closing quotes */ , brkItr );
352+ if (remain < s .length () + 2 /* opening/closing quotes */ ) {
353+ // Emit the opening quote to next line
354+ output .append ('\n' ).append (baseIndent ).append (indent ).append (quote );
355+
356+ // Extract value segment again with updated remaining length
357+ remain = maxColumn - baseIndentWidth - indentWidth - 1 /* opening quote */ ;
358+ s = extractText (value , idx , remain - 1 /* space for closing quotes */ , brkItr );
359+ idx += s .length ();
360+
361+ output .append (s ).append (quote );
362+ remain = maxColumn - baseIndentWidth - indentWidth - s .length () - 2 ;
363+ } else {
364+ idx += s .length ();
365+ // Emit the value to the same line
366+ output .append (quote ).append (s ).append (quote );
367+ remain -= (s .length () + 2 );
340368 }
341369
342- // process the key first
343- // splitting it into multiple lines if necessary
344- brkItr .setText (value );
345- start = 0 ;
346- end = brkItr .first ();
347- prevEnd = end ;
348- firstLine = true ;
349- while (end != BreakIterator .DONE ) {
350- prevEnd = end ;
351- end = brkItr .next ();
352- if (end - start > available ) {
353- output .append ('"' ).append (value .substring (start , prevEnd )).append ('"' ).append ('\n' ).append (tabStr )
354- .append ("+ " );
355- start = prevEnd ;
356-
357- // after first line, indent subsequent lines with 4 additional
358- // spaces
359- if (firstLine ) {
360- available = maxLineLen - tabStrSize - 5 ;
361- firstLine = false ;
362- }
363- } else if (end == valueLen ) {
364- output .append ('"' ).append (value .substring (start , end )).append ('"' );
370+ if (idx < value .length ()) {
371+ // Process continuation lines if required
372+ while (idx < value .length ()) {
373+ output .append ('\n' ).append (baseIndent ).append (indent ).append ("+ " ).append (quote );
374+ remain = maxColumn - baseIndentWidth - indentWidth - 3 /* +<sp><quote> */ ;
375+ s = extractText (value , idx , remain - 1 , brkItr );
376+ idx += s .length ();
377+ output .append (s ).append (quote );
378+ remain -= (s .length () + 1 );
365379 }
366380 }
367381
368382 return output .toString ();
369383 }
370384
385+ /**
386+ * Extracts a substring that fits within the specified maximum length. When the very
387+ * first segment of the given text staring with the index exceeds the specified maximum
388+ * length, this method still returns the segment. So this method always returns a
389+ * non-emptry string.
390+ *
391+ * @param text The base text
392+ * @param startIdx The start index within the text to be processed
393+ * @param maxLen The maximum length of substring. Note: this restriction is not
394+ * enforced for the very first text segment.
395+ * @param brkItr The break iterator to be used for segmenting text.
396+ * @return A substring
397+ */
398+ static String extractText (String text , int startIdx , int maxLen , BreakIterator brkItr ) {
399+ String s = text .substring (startIdx );
400+ brkItr .setText (s );
401+ int idx = brkItr .next ();
402+ while (true ) {
403+ int tmp = brkItr .next ();
404+ if (tmp == BreakIterator .DONE || tmp >= maxLen ) {
405+ break ;
406+ }
407+ idx = tmp ;
408+ }
409+ return s .substring (0 , idx );
410+ }
411+
371412 /**
372413 * This method looks at the provided string to determine if a tab char or
373414 * spaces are being used for tabbing.
@@ -396,15 +437,17 @@ private static String extractTabPrefix(String s) {
396437 return s .substring (0 , i );
397438 }
398439
440+
441+ private static final int TAB_WIDTH = 4 ;
399442 /**
400- * Gets the number of spaces the whitespace string is using. Tab chars are
401- * equal to 4 chars . i.e. a tab is considered to be of size 4 .
443+ * Gets the display width of the whitespace string is using. Tab chars are
444+ * equal to TAB_WIDTH . i.e. a tab is considered to be of 4 in width .
402445 */
403- static int getSpacesSize (String whitespace ) {
446+ static int getSpacesWidth (String whitespace ) {
404447 int size = 0 ;
405448 for (int i = 0 ; i < whitespace .length (); i ++) {
406449 if (whitespace .charAt (i ) == '\t' ) {
407- size += 4 ;
450+ size += TAB_WIDTH ;
408451 } else if (whitespace .charAt (i ) == ' ' ) {
409452 size ++;
410453 }
0 commit comments