0001: /*
0002: ******************************************************************************
0003: * Copyright (C) 2003-2004, International Business Machines Corporation and *
0004: * others. All Rights Reserved. *
0005: ******************************************************************************
0006: */
0007:
0008: package com.ibm.icu.dev.tool.localeconverter;
0009:
0010: import com.ibm.icu.dev.tool.UOption;
0011: import org.w3c.dom.Document;
0012: import org.w3c.dom.Node;
0013: import org.w3c.dom.NamedNodeMap;
0014: import org.w3c.dom.NodeList;
0015: import org.xml.sax.ErrorHandler;
0016: import org.xml.sax.InputSource;
0017: import org.xml.sax.SAXException;
0018: import org.xml.sax.SAXParseException;
0019: import javax.xml.parsers.DocumentBuilder;
0020: import javax.xml.parsers.DocumentBuilderFactory;
0021:
0022: import java.io.*;
0023: import java.util.*;
0024:
0025: public final class XLIFF2ICUConverter {
0026:
0027: /**
0028: * These must be kept in sync with getOptions().
0029: */
0030: private static final int HELP1 = 0;
0031: private static final int HELP2 = 1;
0032: private static final int SOURCEDIR = 2;
0033: private static final int DESTDIR = 3;
0034: private static final int TARGETONLY = 4;
0035: private static final int SOURCEONLY = 5;
0036: private static final int MAKE_SOURCE_ROOT = 6;
0037:
0038: private static final UOption[] options = new UOption[] {
0039: UOption.HELP_H(), UOption.HELP_QUESTION_MARK(),
0040: UOption.SOURCEDIR(), UOption.DESTDIR(),
0041: UOption.create("target-only", 't', UOption.OPTIONAL_ARG),
0042: UOption.create("source-only", 'c', UOption.OPTIONAL_ARG),
0043: UOption.create("make-source-root", 'r', UOption.NO_ARG) };
0044:
0045: private static final String ROOT = "root";
0046: private static final String RESTYPE = "restype";
0047: private static final String RESNAME = "resname";
0048: private static final String YES = "yes";
0049: private static final String NO = "no";
0050: private static final String TRANSLATE = "translate";
0051: private static final String BODY = "body";
0052: private static final String GROUPS = "group";
0053: private static final String FILES = "file";
0054: private static final String TRANSUNIT = "trans-unit";
0055: private static final String BINUNIT = "bin-unit";
0056: private static final String BINSOURCE = "bin-source";
0057: private static final String TS = "ts";
0058: private static final String ORIGINAL = "original";
0059: private static final String SOURCELANGUAGE = "source-language";
0060: private static final String TARGETLANGUAGE = "target-language";
0061: private static final String TARGET = "target";
0062: private static final String SOURCE = "source";
0063: private static final String NOTE = "note";
0064: private static final String XMLLANG = "xml:lang";
0065: private static final String FILE = "file";
0066: private static final String INTVECTOR = "intvector";
0067: private static final String ARRAYS = "array";
0068: private static final String STRINGS = "string";
0069: private static final String BIN = "bin";
0070: private static final String INTS = "int";
0071: private static final String TABLE = "table";
0072: private static final String IMPORT = "import";
0073: private static final String HREF = "href";
0074: private static final String EXTERNALFILE = "external-file";
0075: private static final String INTERNALFILE = "internal-file";
0076: private static final String ALTTRANS = "alt-trans";
0077: private static final String CRC = "crc";
0078: private static final String ALIAS = "alias";
0079: private static final String LINESEP = System
0080: .getProperty("line.separator");
0081: private static final String BOM = "\uFEFF";
0082: private static final String CHARSET = "UTF-8";
0083: private static final String OPENBRACE = "{";
0084: private static final String CLOSEBRACE = "}";
0085: private static final String COLON = ":";
0086: private static final String COMMA = ",";
0087: private static final String QUOTE = "\"";
0088: private static final String COMMENTSTART = "/**";
0089: private static final String COMMENTEND = " */";
0090: private static final String TAG = " * @";
0091: private static final String COMMENTMIDDLE = " * ";
0092: private static final String SPACE = " ";
0093: private static final String INDENT = " ";
0094: private static final String EMPTY = "";
0095: private static final String ID = "id";
0096:
0097: public static void main(String[] args) {
0098: XLIFF2ICUConverter cnv = new XLIFF2ICUConverter();
0099: cnv.processArgs(args);
0100: }
0101:
0102: private String sourceDir = null;
0103: private String fileName = null;
0104: private String destDir = null;
0105: private boolean targetOnly = false;
0106: private String targetFileName = null;
0107: private boolean makeSourceRoot = false;
0108: private String sourceFileName = null;
0109: private boolean sourceOnly = false;
0110:
0111: private void processArgs(String[] args) {
0112: int remainingArgc = 0;
0113: try {
0114: remainingArgc = UOption.parseArgs(args, options);
0115: } catch (Exception e) {
0116: System.err.println("ERROR: " + e.toString());
0117: usage();
0118: }
0119: if (args.length == 0 || options[HELP1].doesOccur
0120: || options[HELP2].doesOccur) {
0121: usage();
0122: }
0123: if (remainingArgc == 0) {
0124: System.err
0125: .println("ERROR: Either the file name to be processed is not "
0126: + "specified or the it is specified after the -t/-c \n"
0127: + "option which has an optional argument. Try rearranging "
0128: + "the options.");
0129: usage();
0130: }
0131: if (options[SOURCEDIR].doesOccur) {
0132: sourceDir = options[SOURCEDIR].value;
0133: }
0134: if (options[DESTDIR].doesOccur) {
0135: destDir = options[DESTDIR].value;
0136: }
0137: if (options[TARGETONLY].doesOccur) {
0138: targetOnly = true;
0139: targetFileName = options[TARGETONLY].value;
0140: }
0141: if (options[SOURCEONLY].doesOccur) {
0142: sourceOnly = true;
0143: sourceFileName = options[SOURCEONLY].value;
0144: }
0145:
0146: if (options[MAKE_SOURCE_ROOT].doesOccur) {
0147: makeSourceRoot = true;
0148: }
0149: if (destDir == null) {
0150: destDir = ".";
0151: }
0152: if (sourceOnly == true && targetOnly == true) {
0153: System.err
0154: .println("--source-only and --target-only are specified. Please check the arguments and try again.");
0155: usage();
0156: }
0157:
0158: for (int i = 0; i < remainingArgc; i++) {
0159: int lastIndex = args[i].lastIndexOf(File.separator, args[i]
0160: .length()) + 1; /* add 1 to skip past the separator */
0161: fileName = args[i].substring(lastIndex, args[i].length());
0162: String xmlfileName = getFullPath(false, args[i]);
0163: System.out.println("Processing file: " + xmlfileName);
0164: createRB(xmlfileName);
0165: }
0166: }
0167:
0168: private void usage() {
0169: System.out
0170: .println("\nUsage: XLIFF2ICUConverter [OPTIONS] [FILES]\n\n"
0171: + "This program is used to convert XLIFF files to ICU ResourceBundle TXT files.\n"
0172: + "Please refer to the following options. Options are not case sensitive.\n"
0173: + "Options:\n"
0174: + "-s or --sourcedir source directory for files followed by path, default is current directory.\n"
0175: + "-d or --destdir destination directory, followed by the path, default is current directory.\n"
0176: + "-h or -? or --help this usage text.\n"
0177: + "-t or --target-only only generate the target language txt file, followed by optional output file name.\n"
0178: + " Cannot be used in conjunction with --source-only.\n"
0179: + "-c or --source-only only generate the source language bundle followed by optional output file name.\n"
0180: + " Cannot be used in conjunction with --target-only.\n"
0181: + "-r or --make-source-root produce root bundle from source elements.\n"
0182: + "example: com.ibm.icu.dev.tool.localeconverter.XLIFF2ICUConverter -t <optional argument> -s xxx -d yyy myResources.xlf");
0183: System.exit(-1);
0184: }
0185:
0186: private String getFullPath(boolean fileType, String fName) {
0187: String str;
0188: int lastIndex1 = fName.lastIndexOf(File.separator, fName
0189: .length()) + 1; /*add 1 to skip past the separator*/
0190: int lastIndex2 = fName.lastIndexOf('.', fName.length());
0191: if (fileType == true) {
0192: if (lastIndex2 == -1) {
0193: fName = fName.trim() + ".txt";
0194: } else {
0195: if (!fName.substring(lastIndex2).equalsIgnoreCase(
0196: ".txt")) {
0197: fName = fName.substring(lastIndex1, lastIndex2)
0198: + ".txt";
0199: }
0200: }
0201: if (destDir != null && fName != null) {
0202: str = destDir + File.separator + fName.trim();
0203: } else {
0204: str = System.getProperty("user.dir") + File.separator
0205: + fName.trim();
0206: }
0207: } else {
0208: if (lastIndex2 == -1) {
0209: fName = fName.trim() + ".xlf";
0210: } else {
0211: if (!fName.substring(lastIndex2).equalsIgnoreCase(
0212: ".xml")
0213: && fName.substring(lastIndex2)
0214: .equalsIgnoreCase(".xlf")) {
0215: fName = fName.substring(lastIndex1, lastIndex2)
0216: + ".xlf";
0217: }
0218: }
0219: if (sourceDir != null && fName != null) {
0220: str = sourceDir + File.separator + fName;
0221: } else if (lastIndex1 > 0) {
0222: str = fName;
0223: } else {
0224: str = System.getProperty("user.dir") + File.separator
0225: + fName;
0226: }
0227: }
0228: return str;
0229: }
0230:
0231: /*
0232: * Utility method to translate a String filename to URL.
0233: *
0234: * Note: This method is not necessarily proven to get the
0235: * correct URL for every possible kind of filename; it should
0236: * be improved. It handles the most common cases that we've
0237: * encountered when running Conformance tests on Xalan.
0238: * Also note, this method does not handle other non-file:
0239: * flavors of URLs at all.
0240: *
0241: * If the name is null, return null.
0242: * If the name starts with a common URI scheme (namely the ones
0243: * found in the examples of RFC2396), then simply return the
0244: * name as-is (the assumption is that it's already a URL)
0245: * Otherwise we attempt (cheaply) to convert to a file:/// URL.
0246: */
0247: private static String filenameToURL(String filename) {
0248: // null begets null - something like the commutative property
0249: if (null == filename) {
0250: return null;
0251: }
0252:
0253: // Don't translate a string that already looks like a URL
0254: if (filename.startsWith("file:")
0255: || filename.startsWith("http:")
0256: || filename.startsWith("ftp:")
0257: || filename.startsWith("gopher:")
0258: || filename.startsWith("mailto:")
0259: || filename.startsWith("news:")
0260: || filename.startsWith("telnet:")) {
0261: return filename;
0262: }
0263:
0264: File f = new File(filename);
0265: String tmp = null;
0266: try {
0267: // This normally gives a better path
0268: tmp = f.getCanonicalPath();
0269: } catch (IOException ioe) {
0270: // But this can be used as a backup, for cases
0271: // where the file does not exist, etc.
0272: tmp = f.getAbsolutePath();
0273: }
0274:
0275: // URLs must explicitly use only forward slashes
0276: if (File.separatorChar == '\\') {
0277: tmp = tmp.replace('\\', '/');
0278: }
0279: // Note the presumption that it's a file reference
0280: // Ensure we have the correct number of slashes at the
0281: // start: we always want 3 /// if it's absolute
0282: // (which we should have forced above)
0283: if (tmp.startsWith("/")) {
0284: return "file://" + tmp;
0285: } else {
0286: return "file:///" + tmp;
0287: }
0288: }
0289:
0290: private boolean isXmlLang(String lang) {
0291:
0292: int suffix;
0293: char c;
0294:
0295: if (lang.length() < 2) {
0296: return false;
0297: }
0298:
0299: c = lang.charAt(1);
0300: if (c == '-') {
0301: c = lang.charAt(0);
0302: if (!(c == 'i' || c == 'I' || c == 'x' || c == 'X')) {
0303: return false;
0304: }
0305: suffix = 1;
0306: } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
0307: c = lang.charAt(0);
0308: if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))) {
0309: return false;
0310: }
0311: suffix = 2;
0312: } else {
0313: return false;
0314: }
0315: while (suffix < lang.length()) {
0316: c = lang.charAt(suffix);
0317: if (c != '-') {
0318: break;
0319: }
0320: while (++suffix < lang.length()) {
0321: c = lang.charAt(suffix);
0322: if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))) {
0323: break;
0324: }
0325: }
0326: }
0327: return ((lang.length() == suffix) && (c != '-'));
0328: }
0329:
0330: private void createRB(String xmlfileName) {
0331:
0332: String urls = filenameToURL(xmlfileName);
0333: DocumentBuilderFactory dfactory = DocumentBuilderFactory
0334: .newInstance();
0335: dfactory.setNamespaceAware(true);
0336: dfactory.setValidating(true);
0337: Document doc = null;
0338:
0339: ErrorHandler nullHandler = new ErrorHandler() {
0340: public void warning(SAXParseException e)
0341: throws SAXException {
0342:
0343: }
0344:
0345: public void error(SAXParseException e) throws SAXException {
0346: System.err
0347: .println("The XLIFF document is invalid, please check it first: ");
0348: System.err.println("Line " + e.getLineNumber()
0349: + ", Column " + e.getColumnNumber());
0350: System.err.println("Error: " + e.getMessage());
0351: System.exit(0);
0352: }
0353:
0354: public void fatalError(SAXParseException e)
0355: throws SAXException {
0356: throw e;
0357: }
0358: };
0359:
0360: try {
0361: DocumentBuilder docBuilder = dfactory.newDocumentBuilder();
0362: docBuilder.setErrorHandler(nullHandler);
0363: doc = docBuilder.parse(new InputSource(urls));
0364:
0365: NodeList nlist = doc.getElementsByTagName(FILES);
0366: if (nlist.getLength() > 1) {
0367: throw new RuntimeException(
0368: "Multiple <file> elements in the XLIFF file not supported.");
0369: }
0370: // get the value of source-language attribute
0371: String sourceLang = getLanguageName(doc, SOURCELANGUAGE);
0372: // get the value of target-language attribute
0373: String targetLang = getLanguageName(doc, TARGETLANGUAGE);
0374:
0375: // get the list of <source> elements
0376: NodeList sourceList = doc.getElementsByTagName(SOURCE);
0377: // get the list of target elements
0378: NodeList targetList = doc.getElementsByTagName(TARGET);
0379:
0380: // check if the xliff file has source elements in multiple languages
0381: // the source-language value should be the same as xml:lang values
0382: // of all the source elements.
0383: String xmlSrcLang = checkLangAttribute(sourceList,
0384: sourceLang);
0385:
0386: // check if the xliff file has target elements in multiple languages
0387: // the target-language value should be the same as xml:lang values
0388: // of all the target elements.
0389: String xmlTargetLang = checkLangAttribute(targetList,
0390: targetLang);
0391:
0392: // Create the Resource linked list which will hold the
0393: // source and target bundles after parsing
0394: Resource[] set = new Resource[2];
0395: set[0] = new ResourceTable();
0396: set[1] = new ResourceTable();
0397:
0398: // lenient extraction of source language
0399: if (makeSourceRoot == true) {
0400: set[0].name = ROOT;
0401: } else if (sourceLang != null) {
0402: set[0].name = sourceLang.replace('-', '_');
0403: } else {
0404: if (xmlSrcLang != null) {
0405: set[0].name = xmlSrcLang.replace('-', '_');
0406: } else {
0407: System.err
0408: .println("ERROR: Could not figure out the source language of the file. Please check the XLIFF file.");
0409: System.exit(-1);
0410: }
0411: }
0412:
0413: // lenient extraction of the target language
0414: if (targetLang != null) {
0415: set[1].name = targetLang.replace('-', '_');
0416: } else {
0417: if (xmlTargetLang != null) {
0418: set[1].name = xmlTargetLang.replace('-', '_');
0419: } else {
0420: System.err
0421: .println("WARNING: Could not figure out the target language of the file. Producing source bundle only.");
0422: }
0423: }
0424:
0425: // check if any <alt-trans> elements are present
0426: NodeList altTrans = doc.getElementsByTagName(ALTTRANS);
0427: if (altTrans.getLength() > 0) {
0428: System.err
0429: .println("WARNING: <alt-trans> elements in found. Ignoring all <alt-trans> elements.");
0430: }
0431:
0432: // get all the group elements
0433: NodeList list = doc.getElementsByTagName(GROUPS);
0434:
0435: // process the first group element. The first group element is
0436: // the base table that must be parsed recursively
0437: parseTable(list.item(0), set);
0438:
0439: // write out the bundle
0440: writeResource(set, xmlfileName);
0441: } catch (Throwable se) {
0442: System.err.println("ERROR: " + se.toString());
0443: System.exit(1);
0444: }
0445: }
0446:
0447: private void writeResource(Resource[] set, String xmlfileName) {
0448: if (targetOnly == false) {
0449: writeResource(set[0], xmlfileName, sourceFileName);
0450: }
0451: if (sourceOnly == false) {
0452: if (targetOnly == true && set[1].name == null) {
0453: throw new RuntimeException("The " + xmlfileName
0454: + " does not contain translation\n");
0455: }
0456: if (set[1].name != null) {
0457: writeResource(set[1], xmlfileName, targetFileName);
0458: }
0459: }
0460: }
0461:
0462: private void writeResource(Resource set, String sourceFileName,
0463: String targetFileName) {
0464: try {
0465: String outputFileName = null;
0466: if (targetFileName != null) {
0467: outputFileName = destDir + File.separator
0468: + targetFileName + ".txt";
0469: } else {
0470: outputFileName = destDir + File.separator + set.name
0471: + ".txt";
0472: }
0473: FileOutputStream file = new FileOutputStream(outputFileName);
0474: BufferedOutputStream writer = new BufferedOutputStream(file);
0475:
0476: writeHeader(writer, sourceFileName);
0477:
0478: //Now start writing the resource;
0479: Resource current = set;
0480: while (current != null) {
0481: current.write(writer, 0, false);
0482: current = current.next;
0483: }
0484: writer.flush();
0485: writer.close();
0486: } catch (Exception ie) {
0487: System.err.println("ERROR :" + ie.toString());
0488: return;
0489: }
0490: }
0491:
0492: private String getLanguageName(Document doc, String lang) {
0493: if (doc != null) {
0494: NodeList list = doc.getElementsByTagName(FILE);
0495: Node node = list.item(0);
0496: NamedNodeMap attr = node.getAttributes();
0497: Node orig = attr.getNamedItem(lang);
0498:
0499: if (orig != null) {
0500: String name = orig.getNodeValue();
0501: NodeList groupList = doc.getElementsByTagName(GROUPS);
0502: Node group = groupList.item(0);
0503: NamedNodeMap groupAtt = group.getAttributes();
0504: Node id = groupAtt.getNamedItem(ID);
0505: if (id != null) {
0506: String idVal = id.getNodeValue();
0507:
0508: if (!name.equals(idVal)) {
0509: System.out
0510: .println("WARNING: The id value != language name. "
0511: + "Please compare the output with the orignal "
0512: + "ICU ResourceBundle before proceeding.");
0513: }
0514: }
0515: if (!isXmlLang(name)) {
0516: System.err
0517: .println("The attribute "
0518: + lang
0519: + "=\""
0520: + name
0521: + "\" of <file> element does not satisfy RFC 1766 conditions.");
0522: System.exit(-1);
0523: }
0524: return name;
0525: }
0526: }
0527: return null;
0528: }
0529:
0530: // check if the xliff file is translated into multiple languages
0531: // The XLIFF specification allows for single <target> element
0532: // as the child of <trans-unit> but the attributes of the
0533: // <target> element may different across <trans-unit> elements
0534: // check for it. Similar is the case with <source> elements
0535: private String checkLangAttribute(NodeList list, String origName) {
0536: String oldLangName = origName;
0537: for (int i = 0; i < list.getLength(); i++) {
0538: Node node = list.item(i);
0539: NamedNodeMap attr = node.getAttributes();
0540: Node lang = attr.getNamedItem(XMLLANG);
0541: String langName = null;
0542: // the target element should always contain xml:lang attribute
0543: if (lang == null) {
0544: if (origName == null) {
0545: System.err
0546: .println("Encountered <target> element without xml:lang attribute. Please fix the below element in the XLIFF file.\n"
0547: + node.toString());
0548: System.exit(-1);
0549: } else {
0550: langName = origName;
0551: }
0552: } else {
0553: langName = lang.getNodeValue();
0554: }
0555:
0556: if (oldLangName != null && langName != null
0557: && !langName.equals(oldLangName)) {
0558: throw new RuntimeException(
0559: "The <trans-unit> elements must be bilingual, multilingual tranlations not supported. xml:lang = "
0560: + oldLangName
0561: + " and xml:lang = "
0562: + langName);
0563: }
0564: oldLangName = langName;
0565: }
0566: return oldLangName;
0567: }
0568:
0569: private class Resource {
0570: String[] note = new String[20];
0571: int noteLen = 0;
0572: String translate;
0573: String comment;
0574: String name;
0575: Resource next;
0576:
0577: public String escapeSyntaxChars(String val) {
0578: // escape the embedded quotes
0579: char[] str = val.toCharArray();
0580: StringBuffer result = new StringBuffer();
0581: for (int i = 0; i < str.length; i++) {
0582: switch (str[i]) {
0583: case '\u0022':
0584: result.append('\\'); //append backslash
0585: default:
0586: result.append(str[i]);
0587: }
0588: }
0589: return result.toString();
0590: }
0591:
0592: public void write(OutputStream writer, int numIndent,
0593: boolean bare) {
0594: while (next != null) {
0595: next.write(writer, numIndent + 1, false);
0596: }
0597: }
0598:
0599: public void writeIndent(OutputStream writer, int numIndent) {
0600: for (int i = 0; i < numIndent; i++) {
0601: write(writer, INDENT);
0602: }
0603: }
0604:
0605: public void write(OutputStream writer, String value) {
0606: try {
0607: byte[] bytes = value.getBytes(CHARSET);
0608: writer.write(bytes, 0, bytes.length);
0609: } catch (Exception e) {
0610: System.err.println(e);
0611: System.exit(1);
0612: }
0613: }
0614:
0615: public void writeComments(OutputStream writer, int numIndent) {
0616: if (comment != null || translate != null || noteLen > 0) {
0617: // print the start of the comment
0618: writeIndent(writer, numIndent);
0619: write(writer, COMMENTSTART + LINESEP);
0620:
0621: // print comment if any
0622: if (comment != null) {
0623: writeIndent(writer, numIndent);
0624: write(writer, COMMENTMIDDLE);
0625: write(writer, comment);
0626: write(writer, LINESEP);
0627: }
0628:
0629: // print the translate attribute if any
0630: if (translate != null) {
0631: writeIndent(writer, numIndent);
0632: write(writer, TAG + TRANSLATE + SPACE);
0633: write(writer, translate);
0634: write(writer, LINESEP);
0635: }
0636:
0637: // print note elements if any
0638: for (int i = 0; i < noteLen; i++) {
0639: if (note[i] != null) {
0640: writeIndent(writer, numIndent);
0641: write(writer, TAG + NOTE + SPACE + note[i]);
0642: write(writer, LINESEP);
0643: }
0644: }
0645:
0646: // terminate the comment
0647: writeIndent(writer, numIndent);
0648: write(writer, COMMENTEND + LINESEP);
0649: }
0650: }
0651: }
0652:
0653: private class ResourceString extends Resource {
0654: String val;
0655:
0656: public void write(OutputStream writer, int numIndent,
0657: boolean bare) {
0658: writeComments(writer, numIndent);
0659: writeIndent(writer, numIndent);
0660: if (bare == true) {
0661: if (name != null) {
0662: throw new RuntimeException(
0663: "Bare option is set to true but the resource has a name!");
0664: }
0665:
0666: write(writer, QUOTE + escapeSyntaxChars(val) + QUOTE);
0667: } else {
0668: write(writer, name + COLON + STRINGS + OPENBRACE
0669: + QUOTE + escapeSyntaxChars(val) + QUOTE
0670: + CLOSEBRACE + LINESEP);
0671: }
0672: }
0673: }
0674:
0675: private class ResourceAlias extends Resource {
0676: String val;
0677:
0678: public void write(OutputStream writer, int numIndent,
0679: boolean bare) {
0680: writeComments(writer, numIndent);
0681: writeIndent(writer, numIndent);
0682: String line = ((name == null) ? EMPTY : name) + COLON
0683: + ALIAS + OPENBRACE + QUOTE
0684: + escapeSyntaxChars(val) + QUOTE + CLOSEBRACE;
0685: if (bare == true) {
0686: if (name != null) {
0687: throw new RuntimeException(
0688: "Bare option is set to true but the resource has a name!");
0689: }
0690: write(writer, line);
0691: } else {
0692: write(writer, line + LINESEP);
0693: }
0694: }
0695: }
0696:
0697: private class ResourceInt extends Resource {
0698: String val;
0699:
0700: public void write(OutputStream writer, int numIndent,
0701: boolean bare) {
0702: writeComments(writer, numIndent);
0703: writeIndent(writer, numIndent);
0704: String line = ((name == null) ? EMPTY : name) + COLON
0705: + INTS + OPENBRACE + val + CLOSEBRACE;
0706: if (bare == true) {
0707: if (name != null) {
0708: throw new RuntimeException(
0709: "Bare option is set to true but the resource has a name!");
0710: }
0711: write(writer, line);
0712: } else {
0713: write(writer, line + LINESEP);
0714: }
0715: }
0716: }
0717:
0718: private class ResourceBinary extends Resource {
0719: String internal;
0720: String external;
0721:
0722: public void write(OutputStream writer, int numIndent,
0723: boolean bare) {
0724: writeComments(writer, numIndent);
0725: writeIndent(writer, numIndent);
0726: if (internal == null) {
0727: String line = ((name == null) ? EMPTY : name) + COLON
0728: + IMPORT + OPENBRACE + QUOTE + external + QUOTE
0729: + CLOSEBRACE
0730: + ((bare == true) ? EMPTY : LINESEP);
0731: write(writer, line);
0732: } else {
0733: String line = ((name == null) ? EMPTY : name) + COLON
0734: + BIN + OPENBRACE + internal + CLOSEBRACE
0735: + ((bare == true) ? EMPTY : LINESEP);
0736: write(writer, line);
0737: }
0738:
0739: }
0740: }
0741:
0742: private class ResourceIntVector extends Resource {
0743: ResourceInt first;
0744:
0745: public void write(OutputStream writer, int numIndent,
0746: boolean bare) {
0747: writeComments(writer, numIndent);
0748: writeIndent(writer, numIndent);
0749: write(writer, name + COLON + INTVECTOR + OPENBRACE
0750: + LINESEP);
0751: numIndent++;
0752: ResourceInt current = (ResourceInt) first;
0753: while (current != null) {
0754: //current.write(writer, numIndent, true);
0755: writeIndent(writer, numIndent);
0756: write(writer, current.val);
0757: write(writer, COMMA + LINESEP);
0758: current = (ResourceInt) current.next;
0759: }
0760: numIndent--;
0761: writeIndent(writer, numIndent);
0762: write(writer, CLOSEBRACE + LINESEP);
0763: }
0764: }
0765:
0766: private class ResourceTable extends Resource {
0767: Resource first;
0768:
0769: public void write(OutputStream writer, int numIndent,
0770: boolean bare) {
0771: writeComments(writer, numIndent);
0772: writeIndent(writer, numIndent);
0773: write(writer, name + COLON + TABLE + OPENBRACE + LINESEP);
0774: numIndent++;
0775: Resource current = first;
0776: while (current != null) {
0777: current.write(writer, numIndent, false);
0778: current = current.next;
0779: }
0780: numIndent--;
0781: writeIndent(writer, numIndent);
0782: write(writer, CLOSEBRACE + LINESEP);
0783: }
0784: }
0785:
0786: private class ResourceArray extends Resource {
0787: Resource first;
0788:
0789: public void write(OutputStream writer, int numIndent,
0790: boolean bare) {
0791: writeComments(writer, numIndent);
0792: writeIndent(writer, numIndent);
0793: write(writer, name + COLON + ARRAYS + OPENBRACE + LINESEP);
0794: numIndent++;
0795: Resource current = first;
0796: while (current != null) {
0797: current.write(writer, numIndent, true);
0798: write(writer, COMMA + LINESEP);
0799: current = current.next;
0800: }
0801: numIndent--;
0802: writeIndent(writer, numIndent);
0803: write(writer, CLOSEBRACE + LINESEP);
0804: }
0805: }
0806:
0807: private String getAttributeValue(Node sNode, String attribName) {
0808: String value = null;
0809: Node node = sNode;
0810:
0811: NamedNodeMap attributes = node.getAttributes();
0812: Node attr = attributes.getNamedItem(attribName);
0813: if (attr != null) {
0814: value = attr.getNodeValue();
0815: }
0816:
0817: return value;
0818: }
0819:
0820: private void parseResourceString(Node node, ResourceString[] set) {
0821: ResourceString currentSource;
0822: ResourceString currentTarget;
0823: currentSource = set[0];
0824: currentTarget = set[1];
0825: String resName = getAttributeValue(node, RESNAME);
0826: String translate = getAttributeValue(node, TRANSLATE);
0827:
0828: // loop to pickup <source>, <note> and <target> elements
0829: for (Node transUnit = node.getFirstChild(); transUnit != null; transUnit = transUnit
0830: .getNextSibling()) {
0831: short type = transUnit.getNodeType();
0832: String name = transUnit.getNodeName();
0833: if (type == Node.COMMENT_NODE) {
0834: // get the comment
0835: currentSource.comment = currentTarget.comment = transUnit
0836: .getNodeValue();
0837: } else if (type == Node.ELEMENT_NODE) {
0838: if (name.equals(SOURCE)) {
0839: // save the source and target values
0840: currentSource.name = currentTarget.name = resName;
0841: currentSource.val = currentTarget.val = transUnit
0842: .getFirstChild().getNodeValue();
0843: currentSource.translate = currentTarget.translate = translate;
0844: } else if (name.equals(NOTE)) {
0845: // save the note values
0846: currentSource.note[currentSource.noteLen++] = currentTarget.note[currentTarget.noteLen++] = transUnit
0847: .getFirstChild().getNodeValue();
0848: } else if (name.equals(TARGET)) {
0849: // if there is a target element replace it
0850: currentTarget.val = transUnit.getFirstChild()
0851: .getNodeValue();
0852: }
0853: }
0854:
0855: }
0856: }
0857:
0858: private void parseResourceInt(Node node, ResourceInt[] set) {
0859: ResourceInt currentSource;
0860: ResourceInt currentTarget;
0861: currentSource = set[0];
0862: currentTarget = set[1];
0863: String resName = getAttributeValue(node, RESNAME);
0864: String translate = getAttributeValue(node, TRANSLATE);
0865: // loop to pickup <source>, <note> and <target> elements
0866: for (Node transUnit = node.getFirstChild(); transUnit != null; transUnit = transUnit
0867: .getNextSibling()) {
0868: short type = transUnit.getNodeType();
0869: String name = transUnit.getNodeName();
0870: if (type == Node.COMMENT_NODE) {
0871: // get the comment
0872: currentSource.comment = currentTarget.comment = transUnit
0873: .getNodeValue();
0874: } else if (type == Node.ELEMENT_NODE) {
0875: if (name.equals(SOURCE)) {
0876: // save the source and target values
0877: currentSource.name = currentTarget.name = resName;
0878: currentSource.translate = currentTarget.translate = translate;
0879: currentSource.val = currentTarget.val = transUnit
0880: .getFirstChild().getNodeValue();
0881: } else if (name.equals(NOTE)) {
0882: // save the note values
0883: currentSource.note[currentSource.noteLen++] = currentTarget.note[currentTarget.noteLen++] = transUnit
0884: .getFirstChild().getNodeValue();
0885: } else if (name.equals(TARGET)) {
0886: // if there is a target element replace it
0887: currentTarget.val = transUnit.getFirstChild()
0888: .getNodeValue();
0889: }
0890: }
0891:
0892: }
0893: }
0894:
0895: private void parseResourceAlias(Node node, ResourceAlias[] set) {
0896: ResourceAlias currentSource;
0897: ResourceAlias currentTarget;
0898: currentSource = set[0];
0899: currentTarget = set[1];
0900: String resName = getAttributeValue(node, RESNAME);
0901: String translate = getAttributeValue(node, TRANSLATE);
0902: // loop to pickup <source>, <note> and <target> elements
0903: for (Node transUnit = node.getFirstChild(); transUnit != null; transUnit = transUnit
0904: .getNextSibling()) {
0905: short type = transUnit.getNodeType();
0906: String name = transUnit.getNodeName();
0907: if (type == Node.COMMENT_NODE) {
0908: // get the comment
0909: currentSource.comment = currentTarget.comment = transUnit
0910: .getNodeValue();
0911: } else if (type == Node.ELEMENT_NODE) {
0912: if (name.equals(SOURCE)) {
0913: // save the source and target values
0914: currentSource.name = currentTarget.name = resName;
0915: currentSource.translate = currentTarget.translate = translate;
0916: currentSource.val = currentTarget.val = transUnit
0917: .getFirstChild().getNodeValue();
0918: } else if (name.equals(NOTE)) {
0919: // save the note values
0920: currentSource.note[currentSource.noteLen++] = currentTarget.note[currentTarget.noteLen++] = transUnit
0921: .getFirstChild().getNodeValue();
0922: } else if (name.equals(TARGET)) {
0923: // if there is a target element replace it
0924: currentTarget.val = transUnit.getFirstChild()
0925: .getNodeValue();
0926: }
0927: }
0928:
0929: }
0930: }
0931:
0932: private void parseResourceBinary(Node node, ResourceBinary[] set) {
0933: ResourceBinary currentSource;
0934: ResourceBinary currentTarget;
0935: currentSource = set[0];
0936: currentTarget = set[1];
0937:
0938: // loop to pickup <source>, <note> and <target> elements
0939: for (Node transUnit = node.getFirstChild(); transUnit != null; transUnit = transUnit
0940: .getNextSibling()) {
0941: short type = transUnit.getNodeType();
0942: String name = transUnit.getNodeName();
0943: if (type == Node.COMMENT_NODE) {
0944: // get the comment
0945: currentSource.comment = currentTarget.comment = transUnit
0946: .getNodeValue();
0947: } else if (type == Node.ELEMENT_NODE) {
0948: if (name.equals(BINSOURCE)) {
0949: // loop to pickup internal-file/extenal-file element
0950: continue;
0951: } else if (name.equals(NOTE)) {
0952: // save the note values
0953: currentSource.note[currentSource.noteLen++] = currentTarget.note[currentTarget.noteLen++] = transUnit
0954: .getFirstChild().getNodeValue();
0955: } else if (name.equals(INTERNALFILE)) {
0956: // if there is a target element replace it
0957: String crc = getAttributeValue(transUnit, CRC);
0958: String value = transUnit.getFirstChild()
0959: .getNodeValue();
0960:
0961: //verify that the binary value conforms to the CRC
0962: if (Integer.parseInt(crc, 10) != CalculateCRC32
0963: .computeCRC32(value)) {
0964: System.err
0965: .println("ERROR: CRC value incorrect! Please check.");
0966: System.exit(1);
0967: }
0968:
0969: currentTarget.internal = currentSource.internal = value;
0970:
0971: } else if (name.equals(EXTERNALFILE)) {
0972: String fileName = getAttributeValue(transUnit, HREF);
0973: currentTarget.external = currentSource.external = fileName;
0974: }
0975: }
0976:
0977: }
0978: }
0979:
0980: private void parseTransUnit(Node node, Resource[] set) {
0981:
0982: String attrType = getAttributeValue(node, RESTYPE);
0983: String translate = getAttributeValue(node, TRANSLATE);
0984: if (attrType == null || attrType.equals(STRINGS)) {
0985: ResourceString[] strings = new ResourceString[2];
0986: strings[0] = new ResourceString();
0987: strings[1] = new ResourceString();
0988: parseResourceString(node, strings);
0989: strings[0].translate = strings[1].translate = translate;
0990: set[0] = strings[0];
0991: set[1] = strings[1];
0992: } else if (attrType.equals(INTS)) {
0993: ResourceInt[] ints = new ResourceInt[2];
0994: ints[0] = new ResourceInt();
0995: ints[1] = new ResourceInt();
0996: parseResourceInt(node, ints);
0997: ints[0].translate = ints[1].translate = translate;
0998: set[0] = ints[0];
0999: set[1] = ints[1];
1000: } else if (attrType.equals(ALIAS)) {
1001: ResourceAlias[] ints = new ResourceAlias[2];
1002: ints[0] = new ResourceAlias();
1003: ints[1] = new ResourceAlias();
1004: parseResourceAlias(node, ints);
1005: ints[0].translate = ints[1].translate = translate;
1006: set[0] = ints[0];
1007: set[1] = ints[1];
1008: }
1009: }
1010:
1011: private void parseBinUnit(Node node, Resource[] set) {
1012: ResourceBinary[] bins = new ResourceBinary[2];
1013: bins[0] = new ResourceBinary();
1014: bins[1] = new ResourceBinary();
1015: Resource currentSource = bins[0];
1016: Resource currentTarget = bins[1];
1017: String resName = getAttributeValue(node, RESNAME);
1018: String translate = getAttributeValue(node, TRANSLATE);
1019: currentTarget.name = currentSource.name = resName;
1020: currentSource.translate = currentTarget.translate = translate;
1021: for (Node child = node.getFirstChild(); child != null; child = child
1022: .getNextSibling()) {
1023: short type = child.getNodeType();
1024: String name = child.getNodeName();
1025: if (type == Node.COMMENT_NODE) {
1026: currentSource.comment = currentTarget.comment = child
1027: .getNodeValue();
1028: } else if (type == Node.ELEMENT_NODE) {
1029: if (name.equals(BINSOURCE)) {
1030: parseResourceBinary(child, bins);
1031: } else if (name.equals(NOTE)) {
1032: String note = child.getFirstChild().getNodeValue();
1033: currentSource.note[currentSource.noteLen++] = currentTarget.note[currentTarget.noteLen++] = note;
1034: }
1035: }
1036: }
1037: set[0] = bins[0];
1038: set[1] = bins[1];
1039: }
1040:
1041: private void parseArray(Node node, Resource[] set) {
1042: if (set[0] == null) {
1043: set[0] = new ResourceArray();
1044: set[1] = new ResourceArray();
1045: }
1046: Resource currentSource = set[0];
1047: Resource currentTarget = set[1];
1048: String resName = getAttributeValue(node, RESNAME);
1049: currentSource.name = currentTarget.name = resName;
1050: boolean isFirst = true;
1051:
1052: for (Node child = node.getFirstChild(); child != null; child = child
1053: .getNextSibling()) {
1054: short type = child.getNodeType();
1055: String name = child.getNodeName();
1056: if (type == Node.COMMENT_NODE) {
1057: currentSource.comment = currentTarget.comment = child
1058: .getNodeValue();
1059: } else if (type == Node.ELEMENT_NODE) {
1060: if (name.equals(TRANSUNIT)) {
1061: Resource[] next = new Resource[2];
1062: parseTransUnit(child, next);
1063: if (isFirst == true) {
1064: ((ResourceArray) currentSource).first = next[0];
1065: ((ResourceArray) currentTarget).first = next[1];
1066: currentSource = ((ResourceArray) currentSource).first;
1067: currentTarget = ((ResourceArray) currentTarget).first;
1068: isFirst = false;
1069: } else {
1070: currentSource.next = next[0];
1071: currentTarget.next = next[1];
1072: // set the next pointers
1073: currentSource = currentSource.next;
1074: currentTarget = currentTarget.next;
1075: }
1076: } else if (name.equals(NOTE)) {
1077: String note = child.getFirstChild().getNodeValue();
1078: currentSource.note[currentSource.noteLen++] = currentTarget.note[currentTarget.noteLen++] = note;
1079: } else if (name.equals(BINUNIT)) {
1080: Resource[] next = new Resource[2];
1081: parseBinUnit(child, next);
1082: if (isFirst == true) {
1083: ((ResourceArray) currentSource).first = next[0];
1084: ((ResourceArray) currentTarget).first = next[1];
1085: currentSource = ((ResourceArray) currentSource).first.next;
1086: currentTarget = ((ResourceArray) currentTarget).first.next;
1087: isFirst = false;
1088: } else {
1089: currentSource.next = next[0];
1090: currentTarget.next = next[1];
1091: // set the next pointers
1092: currentSource = currentSource.next;
1093: currentTarget = currentTarget.next;
1094: }
1095: }
1096: }
1097: }
1098: }
1099:
1100: private void parseIntVector(Node node, Resource[] set) {
1101: if (set[0] == null) {
1102: set[0] = new ResourceIntVector();
1103: set[1] = new ResourceIntVector();
1104: }
1105: Resource currentSource = set[0];
1106: Resource currentTarget = set[1];
1107: String resName = getAttributeValue(node, RESNAME);
1108: String translate = getAttributeValue(node, TRANSLATE);
1109: currentSource.name = currentTarget.name = resName;
1110: currentSource.translate = translate;
1111: boolean isFirst = true;
1112: for (Node child = node.getFirstChild(); child != null; child = child
1113: .getNextSibling()) {
1114: short type = child.getNodeType();
1115: String name = child.getNodeName();
1116: if (type == Node.COMMENT_NODE) {
1117: currentSource.comment = currentTarget.comment = child
1118: .getNodeValue();
1119: } else if (type == Node.ELEMENT_NODE) {
1120: if (name.equals(TRANSUNIT)) {
1121: Resource[] next = new Resource[2];
1122: parseTransUnit(child, next);
1123: if (isFirst == true) {
1124: // the down cast should be safe .. if not something is terribly wrong!!
1125: ((ResourceIntVector) currentSource).first = (ResourceInt) next[0];
1126: ((ResourceIntVector) currentTarget).first = (ResourceInt) next[1];
1127: currentSource = ((ResourceIntVector) currentSource).first;
1128: currentTarget = ((ResourceIntVector) currentTarget).first;
1129: isFirst = false;
1130: } else {
1131: currentSource.next = next[0];
1132: currentTarget.next = next[1];
1133: // set the next pointers
1134: currentSource = currentSource.next;
1135: currentTarget = currentTarget.next;
1136: }
1137: } else if (name.equals(NOTE)) {
1138: String note = child.getFirstChild().getNodeValue();
1139: currentSource.note[currentSource.noteLen++] = currentTarget.note[currentTarget.noteLen++] = note;
1140: }
1141: }
1142: }
1143: }
1144:
1145: private void parseTable(Node node, Resource[] set) {
1146: if (set[0] == null) {
1147: set[0] = new ResourceTable();
1148: set[1] = new ResourceTable();
1149: }
1150: Resource currentSource = set[0];
1151: Resource currentTarget = set[1];
1152:
1153: String resName = getAttributeValue(node, RESNAME);
1154: String translate = getAttributeValue(node, TRANSLATE);
1155: if (resName != null && currentSource.name == null
1156: && currentTarget.name == null) {
1157: currentSource.name = currentTarget.name = resName;
1158: }
1159: currentTarget.translate = currentSource.translate = translate;
1160:
1161: boolean isFirst = true;
1162: for (Node child = node.getFirstChild(); child != null; child = child
1163: .getNextSibling()) {
1164: short type = child.getNodeType();
1165: String name = child.getNodeName();
1166: if (type == Node.COMMENT_NODE) {
1167: currentSource.comment = currentTarget.comment = child
1168: .getNodeValue();
1169: } else if (type == Node.ELEMENT_NODE) {
1170: if (name.equals(GROUPS)) {
1171: Resource[] next = new Resource[2];
1172: parseGroup(child, next);
1173: if (isFirst == true) {
1174: // the down cast should be safe .. if not something is terribly wrong!!
1175: ((ResourceTable) currentSource).first = next[0];
1176: ((ResourceTable) currentTarget).first = next[1];
1177: currentSource = ((ResourceTable) currentSource).first;
1178: currentTarget = ((ResourceTable) currentTarget).first;
1179: isFirst = false;
1180: } else {
1181: currentSource.next = next[0];
1182: currentTarget.next = next[1];
1183: // set the next pointers
1184: currentSource = currentSource.next;
1185: currentTarget = currentTarget.next;
1186: }
1187: } else if (name.equals(TRANSUNIT)) {
1188: Resource[] next = new Resource[2];
1189: parseTransUnit(child, next);
1190: if (isFirst == true) {
1191: // the down cast should be safe .. if not something is terribly wrong!!
1192: ((ResourceTable) currentSource).first = next[0];
1193: ((ResourceTable) currentTarget).first = next[1];
1194: currentSource = ((ResourceTable) currentSource).first;
1195: currentTarget = ((ResourceTable) currentTarget).first;
1196: isFirst = false;
1197: } else {
1198: currentSource.next = next[0];
1199: currentTarget.next = next[1];
1200: // set the next pointers
1201: currentSource = currentSource.next;
1202: currentTarget = currentTarget.next;
1203: }
1204: } else if (name.equals(NOTE)) {
1205: String note = child.getFirstChild().getNodeValue();
1206: currentSource.note[currentSource.noteLen++] = currentTarget.note[currentTarget.noteLen++] = note;
1207: } else if (name.equals(BINUNIT)) {
1208: Resource[] next = new Resource[2];
1209: parseBinUnit(child, next);
1210: if (isFirst == true) {
1211: // the down cast should be safe .. if not something is terribly wrong!!
1212: ((ResourceTable) currentSource).first = next[0];
1213: ((ResourceTable) currentTarget).first = next[1];
1214: currentSource = ((ResourceTable) currentSource).first;
1215: currentTarget = ((ResourceTable) currentTarget).first;
1216: isFirst = false;
1217: } else {
1218: currentSource.next = next[0];
1219: currentTarget.next = next[1];
1220: // set the next pointers
1221: currentSource = currentSource.next;
1222: currentTarget = currentTarget.next;
1223: }
1224: }
1225: }
1226: }
1227: }
1228:
1229: private void parseGroup(Node node, Resource[] set) {
1230:
1231: // figure out what kind of group this is
1232: String resType = getAttributeValue(node, RESTYPE);
1233: if (resType.equals(ARRAYS)) {
1234: parseArray(node, set);
1235: } else if (resType.equals(TABLE)) {
1236: parseTable(node, set);
1237: } else if (resType.equals(INTVECTOR)) {
1238: parseIntVector(node, set);
1239: }
1240: }
1241:
1242: private void writeLine(OutputStream writer, String line) {
1243: try {
1244: byte[] bytes = line.getBytes(CHARSET);
1245: writer.write(bytes, 0, bytes.length);
1246: } catch (Exception e) {
1247: System.err.println(e);
1248: System.exit(1);
1249: }
1250: }
1251:
1252: private void writeHeader(OutputStream writer, String fileName) {
1253: writeBOM(writer);
1254: Calendar c = Calendar.getInstance();
1255: StringBuffer buffer = new StringBuffer();
1256: buffer
1257: .append("// ***************************************************************************"
1258: + LINESEP);
1259: buffer.append("// *" + LINESEP);
1260: buffer
1261: .append("// * Tool: com.ibm.icu.dev.tool.localeconverter.XLIFF2ICUConverter.java"
1262: + LINESEP);
1263: buffer.append("// * Date & Time: " + c.get(Calendar.YEAR) + "/"
1264: + (c.get(Calendar.MONTH) + 1) + "/"
1265: + c.get(Calendar.DAY_OF_MONTH) + " "
1266: + c.get(Calendar.HOUR_OF_DAY) + COLON
1267: + c.get(Calendar.MINUTE) + LINESEP);
1268: buffer.append("// * Source File: " + fileName + LINESEP);
1269: buffer.append("// *" + LINESEP);
1270: buffer
1271: .append("// ***************************************************************************"
1272: + LINESEP);
1273: writeLine(writer, buffer.toString());
1274:
1275: }
1276:
1277: private void writeBOM(OutputStream buffer) {
1278: try {
1279: byte[] bytes = BOM.getBytes(CHARSET);
1280: buffer.write(bytes, 0, bytes.length);
1281: } catch (Exception e) {
1282: System.err.println(e);
1283: System.exit(1);
1284: }
1285: }
1286: }
|