001: /*
002: *******************************************************************************
003: * Copyright (C) 2002-2004, International Business Machines Corporation and *
004: * others. All Rights Reserved. *
005: *******************************************************************************
006: */
007: package com.ibm.icu.dev.tool.localeconverter;
008:
009: import java.io.*;
010: import java.util.*;
011:
012: import com.ibm.icu.impl.UCharacterProperty;
013:
014: /**
015: * A LocaleWriter takes locale data in standard form and
016: * writes it to standard output in a form suitable for
017: * loading programatically.
018: */
019: public abstract class LocaleWriter {
020: private static final String INDENT_CHARS = " "
021: + " "
022: + " ";
023: private static final char EOL_CHARS[] = { '\r', '\n', '\u2028',
024: '\u2029' };
025:
026: private static final int INDENT_SIZE = 4;
027: private static final int MAX_LINE_LENGTH = 80;
028: private int indentLevel;
029: private String indentString;
030: private boolean needsIndent;
031: protected StringBuffer lineBuffer = new StringBuffer();
032: private int lineLength;
033: protected PrintStream out;
034: protected PrintStream err;
035:
036: //final File outFile = new File( "cnvLoc.txt");
037: //FileOutputStream outFileStream;
038: //BufferedWriter outBufWrite;
039: //PrintWriter myOut;
040:
041: public LocaleWriter(PrintStream out) {
042: this .out = out;
043: this .err = out;
044: /* try{
045: outFile.canWrite();
046: outFileStream = new FileOutputStream(outFile);
047: outBufWrite = new BufferedWriter(new OutputStreamWriter(outFileStream,"UTF8"));
048: }
049: catch(java.io.IOException e){
050: System.out.println("Encoding unsupported");
051: return;
052: }
053: */
054: }
055:
056: public LocaleWriter(PrintStream out, PrintStream err) {
057: this .out = out;
058: this .err = err;
059: /*
060: try{
061: outFile.canWrite();
062: outFileStream = new FileOutputStream(outFile);
063: outBufWrite = new BufferedWriter(new OutputStreamWriter(outFileStream,"UTF8"));
064: }
065: catch(java.io.IOException e){
066: System.out.println("Encoding unsupported");
067: return;
068: }
069: */
070: }
071:
072: public void write(Locale locale, Hashtable localeData) {
073: open(locale);
074: //sort the key so the tags are in order in the resource file
075: SortedVector order = new SortedVector(localeData.keys(),
076: new SortedVector.StringComparator());
077: Enumeration e = order.elements();
078: while (e.hasMoreElements()) {
079: final String key = (String) e.nextElement();
080: final Object data = localeData.get(key);
081: if (isDuplicateOfInheritedValue(locale, key, data)) {
082: println("///* Discarding duplicate data for tag: "
083: + key + " */");
084: } else {
085: write(key, data);
086: }
087: }
088:
089: close();
090: }
091:
092: /*
093: public void closeFileHandle(){
094: try{
095: outBufWrite.close();
096: }
097: catch(java.io.IOException excp){
098: out.println("could not close the output file");
099: }
100: }
101: */
102: protected void write(String tag, Object o) {
103: if (o instanceof String) {
104: write(tag, (String) o);
105: } else if (o instanceof String[]) {
106: write(tag, (String[]) o);
107: } else if (o instanceof String[][]) {
108: write(tag, (String[][]) o);
109: } else if (o instanceof Object[]) {
110: Object[] data = (Object[]) o;
111: String[] temp = new String[data.length];
112: for (int i = 0; i < data.length; i++) {
113: temp[i] = data[i].toString();
114: }
115: write(tag, temp);
116: } else if (o instanceof Object[][]) {
117: Object[][] data = (Object[][]) o;
118: String[][] temp = new String[data.length][];
119: for (int i = 0; i < data.length; i++) {
120: temp[i] = new String[data[i].length];
121: for (int j = 0; j < temp[i].length; j++) {
122: temp[i][j] = data[i][j].toString();
123: }
124: }
125: write(tag, temp);
126: } else {
127: write(tag, o.toString());
128: }
129: }
130:
131: protected final void write(String tag, String[][] value) {
132: if (value.length > 0) {
133: if (value[0].length > 2) {
134: write2D(tag, value);
135: } else {
136: writeTagged(tag, value);
137: }
138: } else {
139: writeTagged(tag, value);
140: }
141: }
142:
143: protected abstract void open(Locale locale);
144:
145: protected abstract void write(String tag, String value);
146:
147: protected abstract void write(String tag, String[] value);
148:
149: protected abstract void write2D(String tag, String[][] value);
150:
151: protected abstract void writeTagged(String tag, String[][] value);
152:
153: protected abstract void close();
154:
155: protected abstract String getStringJoiningCharacter();
156:
157: protected void tabTo(int pos) {
158: if (pos > lineLength) {
159: for (int i = lineLength; i < pos; i++) {
160: print(" ");
161: }
162: }
163: }
164:
165: /*
166: protected void writeToFile(String str){
167:
168: try{
169: outBufWrite.write(prependEsc(str));
170: }
171: catch(java.io.IOException e){
172: out.println("Could not write to file");
173: }
174: }
175: */
176: protected void print(String val) {
177: if (needsIndent) {
178: out.print(indentString);
179: //writeToFile(indentString);
180: lineLength += indentString.length();
181: needsIndent = false;
182: }
183: String tval = PosixCollationBuilder.unescape(val);
184: if (tval.length() < val.length()) {
185: tval = prependEsc(tval);
186: }
187: if (tval != null) {
188: out.print(prependEsc(tval));
189: //writeToFile(tval);
190: int len = 0;
191: for (int i = 0; i < EOL_CHARS.length; i++) {
192: len = Math.max(len, tval.lastIndexOf(EOL_CHARS[i]));
193: }
194: if (len == 0) {
195: lineLength += tval.length();
196: } else {
197: lineLength = tval.length() - len;
198: }
199: }
200: }
201:
202: protected String prependEsc(String str) {
203: StringBuffer myStr = new StringBuffer();
204: for (int i = 0; i < str.length(); i++) {
205: char ch = str.charAt(i);
206: if (ch > 0x007f || ch < 0x0020) {
207: if (ch != 0x0009) {
208: myStr.append("\\u");
209: myStr.append(toHexString(ch, 16, 4));
210: } else {
211: myStr.append(ch);
212: }
213: } else {
214: myStr.append(ch);
215: }
216: }
217: return myStr.toString();
218: }
219:
220: protected String toHexString(char ch, int radix, int pad) {
221: final int MAX_DIGITS = 10;
222: int length = 0;
223: char buffer[] = new char[10];
224: int num = 0;
225: int digit;
226: int j;
227: char temp;
228: int i = (int) ch;
229: do {
230: digit = (int) (i % radix);
231: buffer[length++] = (char) (digit <= 9 ? (0x0030 + digit)
232: : (0x0030 + digit + 7));
233: i = (i / radix);
234: } while (i > 0);
235:
236: while (length < pad) {
237: buffer[length++] = 0x0030;/*zero padding */
238: }
239: /* null terminate the buffer */
240: if (length < MAX_DIGITS) {
241: buffer[length] = 0x0000;
242: }
243: num = (pad >= length) ? pad : length;
244:
245: /* Reverses the string */
246: for (j = 0; j < (num / 2); j++) {
247: temp = buffer[(length - 1) - j];
248: buffer[(length - 1) - j] = buffer[j];
249: buffer[j] = temp;
250: }
251: return new String(buffer, 0, length);
252: }
253:
254: protected void println(String val) {
255: print(val);
256: out.println();
257: //writeToFile("\n");
258: lineLength = 0;
259: needsIndent = true;
260: }
261:
262: protected void printString(String val) {
263: if (val != null) {
264: indent();
265: lineBuffer.setLength(0);
266: lineBuffer.append("\"");
267: final int size = val.length();
268: for (int i = 0; i < size; i++) {
269: append(val.charAt(i));
270: /*if (!append(val.charAt(i))) {
271: lineBuffer.append("\"");
272: lineBuffer.append(getStringJoiningCharacter());
273: println(lineBuffer.toString());
274: lineBuffer.setLength(0);
275: lineBuffer.append("\"");
276: }*/
277: }
278:
279: lineBuffer.append("\"");
280: print(lineBuffer.toString());
281: outdent();
282: } else {
283: print("\"\"");
284: }
285: }
286:
287: private boolean isSpecialChar(char ch) {
288:
289: if (((((ch) <= 0x002F) && ((ch) >= 0x0020))
290: || (((ch) <= 0x003F) && ((ch) >= 0x003A))
291: || (((ch) <= 0x0060) && ((ch) >= 0x005B))
292: || (((ch) <= 0x007E) && ((ch) >= 0x007D)) || (ch) == 0x007B)) {
293: return true;
294: }
295: return false;
296: }
297:
298: protected void printRuleString(String src) {
299: String val = PosixCollationBuilder.unescape(src);
300: if (val != null) {
301: indent();
302: lineBuffer.setLength(0);
303: lineBuffer.append("\"");
304: final int size = val.length();
305: for (int i = 0; i < size; i++) {
306: char ch = val.charAt(i);
307: if (ch == '\\') { //escape char
308: if (((i + 1) < val.length())) {
309: char c2 = val.charAt(i + 1);
310: if (isSpecialChar(c2)) {
311: // escape the escape and escape the
312: // special char
313: append('\\');
314: append('\\');
315: append('\\');
316: append(c2);
317: } else {
318: // write the sequence
319: append('\\');
320: append(c2);
321: }
322: } else {
323: // double escape the escape sequence
324: append('\\');
325: append('\\');
326: }
327: i++;
328: } else {
329: if (UCharacterProperty.isRuleWhiteSpace(ch)) {
330: append('\\');
331: }
332:
333: append(ch);
334: }
335:
336: }
337:
338: lineBuffer.append("\"");
339: print(lineBuffer.toString());
340: outdent();
341: } else {
342: print("\"\"");
343: }
344: }
345:
346: protected void printUnquotedString(String val) {
347: if (val != null) {
348: indent();
349: lineBuffer.setLength(0);
350: //lineBuffer.append("\"");
351: final int size = val.length();
352: for (int i = 0; i < size; i++) {
353: append(val.charAt(i));
354: /*if (!append(val.charAt(i))) {
355: lineBuffer.append("\"");
356: lineBuffer.append(getStringJoiningCharacter());
357: println(lineBuffer.toString());
358: lineBuffer.setLength(0);
359: lineBuffer.append("\"");
360: }*/
361: }
362:
363: //lineBuffer.append("\"");
364: print(lineBuffer.toString());
365: outdent();
366: } else {
367: print("");
368: }
369: }
370:
371: protected boolean append(final char c) {
372: boolean escape = isEscapeChar(c);
373: if (escape) {
374: appendEscapedChar(c, lineBuffer);
375: } else {
376: lineBuffer.append(c);
377: }
378: return (lineLength + lineBuffer.length() < MAX_LINE_LENGTH);
379: }
380:
381: protected boolean isEscapeChar(final char c) {
382: switch (c) {
383: case '"':
384: case '\\':
385: case '\n':
386: case '\r':
387: case '\u2028':
388: case '\u2029':
389: return true;
390: default:
391: return (c < ' ') || (c > 0x07F);
392: }
393: }
394:
395: protected void appendEscapedChar(char c, StringBuffer buffer) {
396: if (c >= 0x20 && c < 0x7f) {
397: buffer.append(c);
398: } else {
399: buffer.append(getEscapeChar());
400: int value = ((int) c) & 0xFFFF;
401: buffer.append(HEX_DIGIT[(value & 0xF000) >> 12]);
402: buffer.append(HEX_DIGIT[(value & 0x0F00) >> 8]);
403: buffer.append(HEX_DIGIT[(value & 0x00F0) >> 4]);
404: buffer.append(HEX_DIGIT[(value & 0x000F)]);
405: }
406: }
407:
408: protected String getEscapeChar() {
409: return "\\u";
410: }
411:
412: protected final void indent() {
413: indent(1);
414: }
415:
416: protected void indent(int amount) {
417: indentLevel += amount;
418: indentString = INDENT_CHARS.substring(0, indentLevel
419: * INDENT_SIZE);
420: }
421:
422: protected final void outdent() {
423: outdent(1);
424: }
425:
426: protected void outdent(int amount) {
427: indentLevel -= amount;
428: indentString = INDENT_CHARS.substring(0, indentLevel
429: * INDENT_SIZE);
430: }
431:
432: static final char[] HEX_DIGIT = { '0', '1', '2', '3', '4', '5',
433: '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
434:
435: /** Return true if the value for the specified tag is the same
436: * as the value inherited from the parent for that tag */
437: private boolean isDuplicateOfInheritedValue(final Locale loc,
438: String tag, Object value) {
439: if (value == null)
440: return true;
441: try {
442: final ResourceBundle parentBundle = getParentBundle(loc);
443: if (parentBundle == null)
444: return false;
445: Object parentValue = parentBundle.getObject(tag);
446: if (!objectsAreEqual(value, parentValue)) {
447: return false;
448: } else {
449: return true;
450: }
451: } catch (java.util.MissingResourceException e) {
452: return false;
453: }
454: }
455:
456: private boolean objectsAreEqual(final Object item,
457: final Object parentItem) {
458: if (item instanceof Object[] && parentItem instanceof Object[]) {
459: return arraysAreEqual((Object[]) item,
460: (Object[]) parentItem);
461: } else {
462: return item.equals(parentItem);
463: }
464: }
465:
466: private boolean arraysAreEqual(final Object[] item,
467: final Object[] parentItem) {
468: boolean matches = item.length == parentItem.length;
469: for (int i = 0; i < item.length && matches; i++) {
470: matches = objectsAreEqual(item[i], parentItem[i]);
471: }
472: return matches;
473: }
474:
475: private ResourceBundle getParentBundle(final Locale loc) {
476: try {
477: final String x = loc.toString();
478: final int ndx = x.lastIndexOf('_');
479: if (ndx < 0) {
480: return null;
481: } else {
482: final String parentLocName = x.substring(0, ndx);
483: final Locale parentLoc = localeFromString(parentLocName);
484: return ResourceBundle
485: .getBundle(
486: "com.ibm.icu.dev.tool.localeconverter.myLocaleElements",
487: parentLoc);
488: }
489: } catch (MissingResourceException e) {
490: return null;
491: }
492: }
493:
494: private String replace(String source, String target,
495: String replacement) {
496: if (target.equals(replacement)) {
497: return source;
498: } else {
499: StringBuffer result = new StringBuffer();
500: int lastNdx = 0;
501: int ndx = source.indexOf(target);
502: while (ndx >= 0) {
503: result.append(source.substring(lastNdx, ndx));
504: result.append(replacement);
505: ndx += target.length();
506: lastNdx = ndx;
507: ndx = source.indexOf(target, ndx);
508: }
509: result.append(source.substring(lastNdx));
510: return result.toString();
511: }
512: }
513:
514: public Locale localeFromString(final String localeName) {
515: String language = localeName;
516: String country = "";
517: String variant = "";
518:
519: int ndx = language.indexOf('_');
520: if (ndx >= 0) {
521: country = language.substring(ndx + 1);
522: language = language.substring(0, ndx);
523: }
524: ndx = country.indexOf('_');
525: if (ndx >= 0) {
526: variant = country.substring(ndx);
527: country = country.substring(0, ndx);
528: }
529: return new Locale(language, country, variant);
530: }
531: }
|