Skip to content

Commit cae584d

Browse files
yoshito-umaokayumaoka
authored andcommitted
Fixed gp-res-filter AMDJS line wrapping problem
AMDJS filter failed to emit a value word segment when only 1 word is overflowed. Reimplemented the line wrapping code to fix the issue.
1 parent b08ac53 commit cae584d

3 files changed

Lines changed: 284 additions & 90 deletions

File tree

gp-res-filter/src/main/java/com/ibm/g11n/pipeline/resfilter/impl/AmdJsResource.java

Lines changed: 128 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,11 @@
5656
*/
5757
public 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

Comments
 (0)