blob: 3272b94e82fc047c81f642d1c92c165d3ad5cc44 [file] [log] [blame]
/*
*******************************************************************************
* Copyright (C) 2004, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*
* Created on Jul 28, 2004
*
* TODO To change the template for this generated file go to
* Window - Preferences - Java - Code Generation - Code and Comments
*/
package com.ibm.icu.dev.tool.cldr;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.File;
import java.io.FileReader;
import java.io.BufferedReader;
import java.io.*;
// DOM imports
import org.apache.xpath.XPathAPI;
import org.apache.xalan.serialize.DOMSerializer;
import org.apache.xalan.serialize.Serializer;
import org.apache.xalan.serialize.SerializerFactory;
import org.apache.xalan.templates.OutputProperties;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
// Needed JAXP classes
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
// SAX2 imports
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
/**
* @author ram
*
* TODO To change the template for this generated type comment go to
* Window - Preferences - Java - Code Generation - Code and Comments
*/
public class LDMLUtilities {
/**
* Creates a fully resolved locale starting with root and
* @param sourceDir
* @param locale
* @return
*/
public static Document getFullyResolvedLDML(String sourceDir, String locale,
boolean ignoreRoot, boolean ignoreUnavailable,
boolean ignoreIfNoneAvailable){
Document full =null;
try{
full = parse(sourceDir+File.separator+ "root.xml", ignoreRoot);
if(full!=null){
full = resolveAliases(full, sourceDir, "root");
}
/*
* Debugging
*
Node[] list = getNodeArray(full, LDMLConstants.ALIAS);
if(list.length>0){
System.err.println("Aliases not resolved!. list.getLength() returned "+ list.length);
}*/
}catch(RuntimeException ex){
if(!ignoreRoot){
throw ex;
}
}
int index = locale.indexOf(".xml");
if(index > -1){
locale = locale.substring(0,index);
}
if(locale.equals("root")){
return full;
}
String[] constituents = locale.split("_");
String loc=null;
boolean isAvailable = false;
//String lastLoc = "root";
for(int i=0; i<constituents.length; i++){
if(loc==null){
loc = constituents[i];
}else{
loc = loc +"_"+ constituents[i];
}
Document doc = null;
// Try cache
//doc = readMergeCache(sourceDir, lastLoc, loc);
//if(doc == null) { ..
String fileName = sourceDir+File.separator+loc+".xml";
File file = new File(fileName);
if(file.exists()){
isAvailable = true;
doc = parse(fileName, ignoreUnavailable);
doc = resolveAliases(doc, sourceDir, loc);
/*
* Debugging
*
Node[] list = getNodeArray(doc, LDMLConstants.ALIAS);
if(list.length>0){
System.err.println("Aliases not resolved!. list.getLength() returned "+ list.length);
}
*/
if(full==null){
full = doc;
}else{
StringBuffer xpath = new StringBuffer();
mergeLDMLDocuments(full, doc, xpath, loc, sourceDir);
}
/*
* debugging
*
Node ec = getNode(full, "//ldml/characters/exemplarCharacters");
if(ec==null){
System.err.println("Could not find exemplarCharacters");
}else{
System.out.println("The chars are: "+ getNodeValue(ec));
}
*/
//writeMergeCache(sourceDir, lastLoc, loc, full);
//lastLoc = loc;
}else{
if(!ignoreUnavailable){
throw new RuntimeException("Could not find: " +fileName);
}
}
// TODO: investigate if we really need to revalidate the DOM tree!
// full = revalidate(full, locale);
}
if(ignoreIfNoneAvailable==true && isAvailable==false){
return null ;
}
try {
OutputStreamWriter writer = new OutputStreamWriter(
new FileOutputStream("./" + File.separator + locale
+ "_debug.xml"), "UTF-8");
LDMLUtilities.printDOMTree(full, new PrintWriter(writer));
writer.flush();
} catch (IOException e) {
//throw the exceptionaway .. this is for debugging
}
return full;
}
public static Document revalidate(Document doc, String fileName){
// what a waste!!
// to revalidate an in-memory DOM tree we need to first
// serialize it to byte array and read it back again.
// in DOM level 3 implementation there is API to validate
// in-memory DOM trees but the latest implementation of Xerces
// can only validate against schemas not DTDs!!!
try{
// revalidate the document
Serializer serializer = SerializerFactory.getSerializer(OutputProperties.getDefaultMethodProperties("xml"));
ByteArrayOutputStream os = new ByteArrayOutputStream();
serializer.setOutputStream(os);
DOMSerializer ds = serializer.asDOMSerializer();
ds.serialize(doc);
os.flush();
ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
doc = parse(new InputSource(is),"Fully resolved: "+fileName, false);
return doc;
}catch(IOException ex){
throw new RuntimeException(ex.getMessage());
}
}
public static String convertXPath2ICU(Node alias, Node namespaceNode, StringBuffer fullPath)
throws TransformerException{
Node context = alias.getParentNode();
StringBuffer icu = new StringBuffer();
String source = getAttributeValue(alias, LDMLConstants.SOURCE);
String xpath = getAttributeValue(alias, LDMLConstants.PATH);
// make sure that the xpaths are valid
if(namespaceNode==null){
XPathAPI.eval(context, fullPath.toString());
if(xpath!=null){
XPathAPI.eval(context,xpath);
}
}else{
XPathAPI.eval(context, fullPath.toString(), namespaceNode);
if(xpath!=null){
XPathAPI.eval(context, xpath, namespaceNode);
}
}
icu.append(source);
if(xpath!=null){
StringBuffer resolved = XPathTokenizer.relativeToAbsolute(xpath, fullPath);
// make sure that fullPath is not corrupted!
XPathAPI.eval(context, fullPath.toString());
//TODO .. do the conversion
XPathTokenizer tokenizer = new XPathTokenizer(resolved);
String token = tokenizer.nextToken();
while(token!=null){
if(!token.equals("ldml")){
String equiv = getICUEquivalent(token);
if(equiv==null){
throw new IllegalArgumentException("Could not find ICU equivalent for token: " +token);
}
if(equiv.length()>0){
icu.append("/");
icu.append(equiv);
}
}
token = tokenizer.nextToken();
}
}
return icu.toString();
}
public static String getDayIndexAsString(String type){
if(type.equals("sun")){
return "0";
}else if(type.equals("mon")){
return "1";
}else if(type.equals("tue")){
return "2";
}else if(type.equals("wed")){
return "3";
}else if(type.equals("thu")){
return "4";
}else if(type.equals("fri")){
return "5";
}else if(type.equals("sat")){
return "6";
}else{
throw new IllegalArgumentException("Unknown type: "+type);
}
}
public static String getMonthIndexAsString(String type){
return Integer.toString(Integer.parseInt(type)-1);
}
private static String getICUEquivalent(String token){
int index = 0;
if(token.indexOf(LDMLConstants.LDN) > -1){
return "";
}else if(token.indexOf(LDMLConstants.LANGUAGES) > -1){
return "Languages";
}else if(token.indexOf(LDMLConstants.LANGUAGE) > -1){
return getAttributeValue(token, LDMLConstants.TYPE);
}else if(token.indexOf(LDMLConstants.TERRITORIES) > -1){
return "Countries";
}else if(token.indexOf(LDMLConstants.TERRITORY) > -1){
return getAttributeValue(token, LDMLConstants.TYPE);
}else if(token.indexOf(LDMLConstants.SCRIPTS) > -1){
return "Scripts";
}else if(token.indexOf(LDMLConstants.SCRIPT) > -1){
return getAttributeValue(token, LDMLConstants.TYPE);
}else if(token.indexOf(LDMLConstants.VARIANTS) > -1){
return "Variants";
}else if(token.indexOf(LDMLConstants.VARIANT) > -1){
return getAttributeValue(token, LDMLConstants.TYPE);
}else if(token.indexOf(LDMLConstants.KEYS) > -1){
return "Keys";
}else if(token.indexOf(LDMLConstants.KEY) > -1){
return getAttributeValue(token, LDMLConstants.TYPE);
}else if(token.indexOf(LDMLConstants.TYPES) > -1){
return "Types";
}else if((index=token.indexOf(LDMLConstants.TYPE)) > -1 && token.charAt(index-1)!='@'){
String type = getAttributeValue(token, LDMLConstants.TYPE);
String key = getAttributeValue(token, LDMLConstants.KEY);
return type+"/"+key;
}else if(token.indexOf(LDMLConstants.LAYOUT) > -1){
return "Layout";
}else if(token.indexOf(LDMLConstants.ORIENTATION) > -1){
//TODO fix this
}else if(token.indexOf(LDMLConstants.CHARACTERS) > -1){
return "";
}else if(token.indexOf(LDMLConstants.EXEMPLAR_CHARACTERS) > -1){
return "ExemplarCharacters";
}else if(token.indexOf(LDMLConstants.MEASUREMENT) > -1){
return "";
}else if(token.indexOf(LDMLConstants.MS) > -1){
return "MeasurementSystem";
}else if(token.indexOf(LDMLConstants.PAPER_SIZE) > -1){
return "PaperSize";
}else if(token.indexOf(LDMLConstants.HEIGHT) > -1){
return "0";
}else if(token.indexOf(LDMLConstants.WIDTH) > -1){
return "1";
}else if(token.indexOf(LDMLConstants.DATES) > -1){
return "";
}else if(token.indexOf(LDMLConstants.LPC) > -1){
return "localPatternCharacters";
}else if(token.indexOf(LDMLConstants.CALENDARS) > -1){
return "calendar";
}else if(token.indexOf(LDMLConstants.DEFAULT) > -1){
return "default";
}else if(token.indexOf(LDMLConstants.CALENDAR) > -1){
return getAttributeValue(token, LDMLConstants.TYPE);
}else if(token.indexOf(LDMLConstants.ERAS) > -1){
return "eras";
}else if(token.indexOf(LDMLConstants.ERAABBR) > -1){
return "abbreviated";
}else if(token.indexOf(LDMLConstants.ERA) > -1){
return getAttributeValue(token, LDMLConstants.TYPE);
}else if(token.indexOf(LDMLConstants.DATE_FORMATS) > -1){
// TODO fix this
}else if(token.indexOf(LDMLConstants.DFL) > -1){
// TODO fix this
}else if(token.indexOf(LDMLConstants.DATE_FORMAT) > -1){
// TODO fix this
}else if(token.indexOf(LDMLConstants.TIME_FORMATS) > -1){
// TODO fix this
}else if(token.indexOf(LDMLConstants.TFL) > -1){
// TODO fix this
}else if(token.indexOf(LDMLConstants.TIME_FORMAT) > -1){
// TODO fix this
}else if(token.indexOf(LDMLConstants.DATE_TIME_FORMATS) > -1){
// TODO fix this
}else if(token.indexOf(LDMLConstants.DTFL) > -1){
// TODO fix this
}else if(token.indexOf(LDMLConstants.DATE_TIME_FORMAT) > -1){
// TODO fix this
}else if(token.indexOf(LDMLConstants.MONTHS) > -1){
return "monthNames";
}else if(token.indexOf(LDMLConstants.MONTH_CONTEXT) > -1){
return getAttributeValue(token, LDMLConstants.TYPE);
}else if(token.indexOf(LDMLConstants.MONTH_WIDTH) > -1){
return getAttributeValue(token, LDMLConstants.TYPE);
}else if(token.indexOf(LDMLConstants.MONTH) > -1){
String valStr = getAttributeValue(token, LDMLConstants.TYPE);
return getMonthIndexAsString(valStr);
}else if(token.indexOf(LDMLConstants.DAYS) > -1){
return "dayNames";
}else if(token.indexOf(LDMLConstants.DAY_CONTEXT) > -1){
return getAttributeValue(token, LDMLConstants.TYPE);
}else if(token.indexOf(LDMLConstants.DAY_WIDTH) > -1){
return getAttributeValue(token, LDMLConstants.TYPE);
}else if(token.indexOf(LDMLConstants.DAY) > -1){
String dayName = getAttributeValue(token, LDMLConstants.TYPE);
return getDayIndexAsString(dayName);
}else if(token.indexOf(LDMLConstants.COLLATIONS) > -1){
return "collations";
}else if(token.indexOf(LDMLConstants.COLLATION) > -1){
return getAttributeValue(token, LDMLConstants.TYPE);
}
// TODO: this method is not finished yet
// the conversion of Xpath to ICU alias path
// is not as straight forward as I thought
// need to cater to idiosynchracies of each
// element node :(
throw new IllegalArgumentException("Unknown Xpath fragment: " + token);
}
/**
*
* @param token XPath token fragment
* @param attrib attribute whose value must be fetched
* @return
*/
private static String getAttributeValue(String token, String attrib){
int attribStart = token.indexOf(attrib);
int valStart = token.indexOf('=', attribStart)+1/*skip past the separtor*/;
int valEnd = token.indexOf('@', valStart);
if(valEnd <0){
valEnd = valStart + (token.length()-valStart-1);
}else{
valEnd = token.length() - 1 /*valEnd should be index*/;
}
String value = token.substring(valStart, valEnd);
int s = value.indexOf('\'');
if(s>-1){
s++;
int e = value.lastIndexOf('\'');
return value.substring(s,e);
}
return value;
}
/**
* Resolved Data File
* <p>To produce fully resolved locale data file from CLDR for a locale ID L, you start with root, and
* replace/add items from the child locales until you get down to L. More formally, this can be
* expressed as the following procedure.</p>
* <ol>
* <li>Let Result be an empty LDML file.</li>
*
* <li>For each Li in the locale chain for L<ol>
* <li>For each element pair P in the LDML file for Li:<ol>
* <li>If Result has an element pair Q with an equivalent element chain, remove Q.</li>
* <li>Add P to Result.</li>
* </ol>
* </li>
* </ol>
*
* </li>
* </ol>
* <p>Note: when adding an element pair to a result, it has to go in the right order for it to be valid
* according to the DTD.</p>
*
* @param source
* @param override
* @return the merged document
*/
private static Node mergeLDMLDocuments(Document source, Node override, StringBuffer xpath,
String thisName, String sourceDir){
if(source==null){
return override;
}
if(xpath.length()==0){
xpath.append("/");
}
// boolean gotcha = false;
// String oldx = new String(xpath);
// if(override.getNodeName().equals("week")) {
// gotcha = true;
// System.out.println("SRC: " + getNode(source, xpath.toString()).toString());
// System.out.println("OVR: " + override.toString());
// }
// we know that every child xml file either adds or
// overrides the elements in parent
// so we traverse the child, at every node check if
// if the node is present in the source,
// if (present)
// recurse to replace any nodes that need to be overridded
// else
// import the node into source
Node child = override.getFirstChild();
while( child!=null){
// we are only concerned with element nodes
if(child.getNodeType()!=Node.ELEMENT_NODE){
child = child.getNextSibling();
continue;
}
String childName = child.getNodeName();
int savedLength=xpath.length();
xpath.append("/");
xpath.append(childName);
appendXPathAttribute(child,xpath);
Node nodeInSource = null;
if(childName.indexOf(":")>-1){
nodeInSource = getNode(source, xpath.toString(), child);
}else{
nodeInSource = getNode(source, xpath.toString());
}
Node parentNodeInSource = null;
if(nodeInSource==null){
// the child xml has a new node
// that should be added to parent
String parentXpath = xpath.substring(0, savedLength);
if(childName.indexOf(":")>-1){
parentNodeInSource = getNode(source, parentXpath, child);
}else{
parentNodeInSource = getNode(source,parentXpath);
}
if(parentNodeInSource==null){
throw new RuntimeException("Internal Error");
}
Node childToImport = source.importNode(child,true);
parentNodeInSource.appendChild(childToImport);
}else if( childName.equals(LDMLConstants.IDENTITY) ||
childName.equals(LDMLConstants.COLLATION)){
// replace the source doc
// none of the elements under collations are inherited
// only the node as a whole!!
parentNodeInSource = nodeInSource.getParentNode();
Node childToImport = source.importNode(child,true);
parentNodeInSource.replaceChild(childToImport, nodeInSource);
}else{
boolean childElementNodes = areChildrenElementNodes(child);
boolean sourceElementNodes = areChildrenElementNodes(nodeInSource);
// System.out.println(childName + ":" + childElementNodes + "/" + sourceElementNodes);
if(childElementNodes && sourceElementNodes){
//recurse to pickup any children!
mergeLDMLDocuments(source, child, xpath, thisName, sourceDir);
}else{
// we have reached a leaf node now get the
// replace to the source doc
parentNodeInSource = nodeInSource.getParentNode();
Node childToImport = source.importNode(child,true);
parentNodeInSource.replaceChild(childToImport, nodeInSource);
}
}
xpath.delete(savedLength,xpath.length());
child= child.getNextSibling();
}
// if(gotcha==true) {
// System.out.println("Final: " + getNode(source, oldx).toString());
// }
return source;
}
private static Node[] getNodeArray(Document doc, String tagName){
NodeList list = doc.getElementsByTagName(tagName);
// node list is dynamic .. if a node is deleted, then
// list is immidiately updated.
// so first cache the nodes returned and do stuff
Node[] array = new Node[list.getLength()];
for(int i=0; i<list.getLength(); i++){
array[i] = list.item(i);
}
return array;
}
/**
* Utility to create abosolute Xpath from 1.1 style alias element
* @param node
* @param type
* @return
*/
public static String getAbsoluteXPath(Node node, String type){
StringBuffer xpath = new StringBuffer();
StringBuffer xpathFragment = new StringBuffer();
node = node.getParentNode(); // the node is alias node .. get its parent
if(node==null){
throw new IllegalArgumentException("Alias node's parent is null!");
}
xpath.append(node.getNodeName());
if(type!=null){
xpath.append("[@type='"+type+"']");
}
Node parent = node;
while((parent = parent.getParentNode())!=null){
xpathFragment.setLength(0);
xpathFragment.append(parent.getNodeName());
if(parent.getNodeType()!= Node.DOCUMENT_NODE){
appendXPathAttribute(parent, xpathFragment);
xpath.insert(0,"/");
xpath.insert(0, xpathFragment);
}
}
xpath.insert(0, "//");
return xpath.toString();
}
/**
*
* @param doc
* @param sourceDir
* @param thisLocale
*/
// TODO guard against circular aliases
public static Document resolveAliases(Document doc, String sourceDir, String thisLocale){
Node[] array = getNodeArray(doc, LDMLConstants.ALIAS);
// resolve all the aliases by iterating over
// the list of nodes
Node[] replacementList = null;
Node parent = null;
String source = null;
String path = null;
String type = null;
for(int i=0; i < array.length ; i++){
Node node = array[i];
if(node==null){
System.err.println("list.item("+i+") returned null!. The list reports it's length as: "+array.length);
//System.exit(-1);
continue;
}
source = getAttributeValue(node, LDMLConstants.SOURCE);
path = getAttributeValue(node, LDMLConstants.PATH);
type = getAttributeValue(node, LDMLConstants.TYPE);
parent = node.getParentNode();
if(source!=null && path==null){
//this LDML 1.1 style alias parse it
path = getAbsoluteXPath(node, type);
}
if(source!=null && !source.equals(thisLocale)){
// if source is defined then path should not be
// relative
if(path.indexOf("..")>0){
throw new IllegalArgumentException("Cannot parse relative xpath: " + path +
" in locale: "+ source +
" from source locale: "+thisLocale);
}
// this is a is an absolute XPath
Document newDoc = parse(sourceDir + File.separator + source + ".xml", false);
replacementList = getNodeListAsArray(newDoc, path);
}else{
// path attribute is referencing another node in this DOM tree
replacementList = getNodeListAsArray(parent, path);
}
if(replacementList != null){
parent.removeChild(node);
int listLen = replacementList.length;
if(listLen > 1){
// check if the whole locale is aliased
// if yes then remove the identity from
// the current document!
if(path!=null && path.equals("//ldml/*")){
Node[] identity = getNodeArray(doc, LDMLConstants.IDENTICAL);
for(int j=0; j<identity.length; j++){
parent.removeChild(node);
}
}
for(int j=0; j<listLen; j++){
// found an element node in the aliased resource
// add to the source
Node child = replacementList[j];
Node childToImport = doc.importNode(child,true);
parent.appendChild(childToImport);
}
}else{
Node replacement = replacementList[0];
for(Node child = replacement.getFirstChild(); child!=null; child=child.getNextSibling()){
// found an element node in the aliased resource
// add to the source
Node childToImport = doc.importNode(child,true);
parent.appendChild(childToImport);
}
}
}else{
throw new IllegalArgumentException("Could not find node for xpath: " + path +
" in locale: "+ source +
" from source locale: "+thisLocale);
}
}
return doc;
}
//TODO add funtions for fetching legitimate children
// for ICU
public boolean isParentDraft(Document fullyResolved, String xpath){
Node node = getNode(fullyResolved, xpath);
Node parentNode ;
while((parentNode = node.getParentNode())!=null){
String draft = getAttributeValue(parentNode, LDMLConstants.DRAFT);
if(draft!=null ){
if(draft.equals("true")){
return true;
}else{
return false;
}
}
}
// the default value is false if none specified
return false;
}
public static boolean isNodeDraft(Node node){
String draft = getAttributeValue(node, LDMLConstants.DRAFT);
if(draft!=null ){
if(draft.equals("true")){
return true;
}else{
return false;
}
}
return false;
}
public static boolean isDraft(Node fullyResolved, StringBuffer xpath){
Node current = getNode(fullyResolved, xpath.toString());
String draft = null;
while(current!=null && current.getNodeType()== Node.ELEMENT_NODE){
draft = getAttributeValue(current, LDMLConstants.DRAFT);
if(draft!=null){
if(draft.equals("true")){
return true;
}else{
return false;
}
}
current = current.getParentNode();
}
return false;
}
public static boolean isSiblingDraft(Node root){
Node current = root;
String draft = null;
while(current!=null && current.getNodeType()== Node.ELEMENT_NODE){
draft = getAttributeValue(current, LDMLConstants.DRAFT);
if(draft!=null){
if(draft.equals("true")){
return true;
}else{
return false;
}
}
current = current.getNextSibling();
}
return false;
}
public static void appendAllAttributes(Node node, StringBuffer xpath){
NamedNodeMap attr = node.getAttributes();
int len = attr.getLength();
if(len>0){
xpath.append("[");
for(int i=0; i<len; i++){
if(i>0){
xpath.append(" and ");
}
Node item = attr.item(i);
xpath.append("@");
xpath.append(item.getNodeName());
xpath.append("='");
xpath.append(item.getNodeValue());
xpath.append("'");
}
xpath.append("]");
}
}
/**
* Appends the attribute values that make differentiate 2 siblings
* in LDML
* @param node
* @param xpath
*/
public static final void appendXPathAttribute(Node node, StringBuffer xpath){
appendXPathAttribute(node,xpath,false);
}
public static void appendXPathAttribute(Node node, StringBuffer xpath,boolean ignoreAlt){
boolean terminate = false;
String val = getAttributeValue(node, LDMLConstants.TYPE);
String and = "and";
boolean isStart = true;
String name = node.getNodeName();
if(val!=null && !name.equals(LDMLConstants.DEFAULT)&& !name.equals(LDMLConstants.MS)){
if(isStart){
xpath.append("[");
isStart=false;
}
xpath.append("@type='");
xpath.append(val);
xpath.append("'");
terminate = true;
}
if(!ignoreAlt) {
val = getAttributeValue(node, LDMLConstants.ALT);
if(val!=null){
if(isStart){
xpath.append("[");
isStart=false;
}else{
xpath.append(and);
}
xpath.append("@alt='");
xpath.append(val);
xpath.append("'");
terminate = true;
}
}
val = getAttributeValue(node, LDMLConstants.KEY);
if(val!=null){
if(isStart){
xpath.append("[");
isStart=false;
}else{
xpath.append(and);
}
xpath.append("@key='");
xpath.append(val);
xpath.append("'");
terminate = true;
}
val = getAttributeValue(node, LDMLConstants.REGISTRY);
if(val!=null){
if(isStart){
xpath.append("[");
isStart=false;
}else{
xpath.append(and);
}
xpath.append("@registry='");
xpath.append(val);
xpath.append("'");
terminate = true;
}
if(terminate){
xpath.append("]");
}
}
/**
* Ascertains if the children of the given node are element
* nodes.
* @param node
* @return
*/
public static boolean areChildrenElementNodes(Node node){
NodeList list = node.getChildNodes();
for(int i=0;i<list.getLength();i++){
if(list.item(i).getNodeType()==Node.ELEMENT_NODE){
return true;
}
}
return false;
}
public static Node[] getNodeListAsArray( Node doc, String xpath){
try{
NodeList list = XPathAPI.selectNodeList(doc, xpath);
int length = list.getLength();
if(length>0){
Node[] array = new Node[length];
for(int i=0; i<length; i++){
array[i] = list.item(i);
}
return array;
}
return null;
}catch(TransformerException ex){
throw new RuntimeException(ex.getMessage());
}
}
public static Node[] getElementsByTagName(Document doc, String tagName){
try{
NodeList list = doc.getElementsByTagName(tagName);
int length = list.getLength();
if(length>0){
Node[] array = new Node[length];
for(int i=0; i<length; i++){
array[i] = list.item(i);
}
return array;
}
return null;
}catch(Exception ex){
throw new RuntimeException(ex.getMessage());
}
}
/**
* Fetches the list of nodes that match the given xpath
* @param doc
* @param xpath
* @return
*/
public static NodeList getNodeList( Document doc, String xpath){
try{
return XPathAPI.selectNodeList(doc, xpath);
}catch(TransformerException ex){
throw new RuntimeException(ex.getMessage());
}
}
public static final boolean isAlternate(Node node){
NamedNodeMap attributes = node.getAttributes();
Node attr = attributes.getNamedItem(LDMLConstants.ALT);
if(attr!=null){
return true;
}
return false;
}
private static final Node getNonAltNode(NodeList list /*, StringBuffer xpath*/){
// A nonalt node is one which .. does not have alternate
// attribute set
Node node =null;
for(int i =0; i<list.getLength(); i++){
node = list.item(i);
if(/*!isDraft(node, xpath)&& */!isAlternate(node)){
return node;
}
}
return null;
}
public static Node getNonAltNodeLike(Node parent, Node child){
StringBuffer childXpath = new StringBuffer(child.getNodeName());
appendXPathAttribute(child,childXpath,true);
String childXPathString = childXpath.toString();
for(Node other=parent.getFirstChild(); other!=null; other=other.getNextSibling() ){
if((other.getNodeType()!=Node.ELEMENT_NODE) || (other==child)) {
continue;
}
StringBuffer otherXpath = new StringBuffer(other.getNodeName());
appendXPathAttribute(other,otherXpath);
// System.out.println("Compare: " + childXpath + " to " + otherXpath);
if(childXPathString.equals(otherXpath.toString())) {
// System.out.println("Match!");
return other;
}
}
return null;
}
/**
* Fetches the node from the document that matches the given xpath.
* The context namespace node is required if the xpath contains
* namespace elments
* @param doc
* @param xpath
* @param namespaceNode
* @return
*/
public static Node getNode(Document doc, String xpath, Node namespaceNode){
try{
NodeList nl = XPathAPI.selectNodeList(doc, xpath, namespaceNode);
int len = nl.getLength();
//TODO watch for attribute "alt"
if(len>1){
throw new IllegalArgumentException("The XPATH returned more than 1 node!. Check XPATH: "+xpath);
}
if(len==0){
return null;
}
return nl.item(0);
}catch(TransformerException ex){
throw new RuntimeException(ex.getMessage());
}
}
public static Node getNode(Node context, String resToFetch, Node namespaceNode){
try{
NodeList nl = XPathAPI.selectNodeList(context, "./"+resToFetch, namespaceNode);
int len = nl.getLength();
//TODO watch for attribute "alt"
if(len>1){
throw new IllegalArgumentException("The XPATH returned more than 1 node!. Check XPATH: "+resToFetch);
}
if(len==0){
return null;
}
return nl.item(0);
}catch(TransformerException ex){
throw new RuntimeException(ex.getMessage());
}
}
/**
* Fetches the node from the document which matches the xpath
* @param node
* @param xpath
* @return
*/
public static Node getNode(Node node, String xpath){
try{
NodeList nl = XPathAPI.selectNodeList(node, xpath);
int len = nl.getLength();
//TODO watch for attribute "alt"
if(len>1){
Node best = getNonAltNode(nl);
if(best != null) {
//System.err.println("Chose best node from " + xpath);
return best;
}
/* else complain */
String all = "";
int i;
for(i=0;i<len;i++) {
all = all + ", " + nl.item(i);
}
throw new IllegalArgumentException("The XPATH returned more than 1 node!. Check XPATH: "+xpath + " = " + all);
}
if(len==0){
return null;
}
return nl.item(0);
}catch(TransformerException ex){
throw new RuntimeException(ex.getMessage());
}
}
/**
*
* @param context
* @param resToFetch
* @param fullyResolved
* @param xpath
* @return
*/
public static Node getNode(Node context, String resToFetch, Document fullyResolved, String xpath){
String ctx = "./"+ resToFetch;
Node node = getNode(context, ctx);
if(node == null && fullyResolved!=null){
// try from fully resolved
String path = xpath+"/"+resToFetch;
node = getNode(fullyResolved, path);
}
return node;
}
/**
*
* @param context
* @param resToFetch
* @param fullyResolved
* @param xpath
* @return
*/
public static NodeList getChildNodes(Node context, String resToFetch){
String ctx = "./"+ resToFetch;
NodeList list = getNodeList(context, ctx);
return list;
}
/**
* Fetches the node from the document that matches the given xpath.
* The context namespace node is required if the xpath contains
* namespace elments
* @param doc
* @param xpath
* @param namespaceNode
* @return
*/
public static NodeList getNodeList(Document doc, String xpath, Node namespaceNode){
try{
NodeList nl = XPathAPI.selectNodeList(doc, xpath, namespaceNode);
if(nl.getLength()==0){
return null;
}
return nl;
}catch(TransformerException ex){
throw new RuntimeException(ex.getMessage());
}
}
/**
* Fetches the node from the document which matches the xpath
* @param node
* @param xpath
* @return
*/
public static NodeList getNodeList(Node node, String xpath){
try{
NodeList nl = XPathAPI.selectNodeList(node, xpath);
int len = nl.getLength();
if(len==0){
return null;
}
return nl;
}catch(TransformerException ex){
throw new RuntimeException(ex.getMessage());
}
}
/**
* Fetches node list from the children of the context node.
* @param context
* @param resToFetch
* @param fullyResolved
* @param xpath
* @return
*/
public static NodeList getNodeList(Node context, String resToFetch, Document fullyResolved, String xpath){
String ctx = "./"+ resToFetch;
NodeList list = getNodeList(context, ctx);
if(list == null && fullyResolved!=null){
// try from fully resolved
String path = xpath+"/"+resToFetch;
list = getNodeList(fullyResolved, path);
}
return list;
}
/**
* Decide if the node is text, and so must be handled specially
* @param n
* @return
*/
private static boolean isTextNode(Node n) {
if (n == null)
return false;
short nodeType = n.getNodeType();
return nodeType == Node.CDATA_SECTION_NODE || nodeType == Node.TEXT_NODE;
}
/**
* Utility method to fetch the attribute value from the given
* element node
* @param sNode
* @param attribName
* @return
*/
public static String getAttributeValue(Node sNode, String attribName){
String value=null;
Node attr = sNode.getAttributes().getNamedItem(attribName);
if(attr!=null){
value = attr.getNodeValue();
}
return value;
}
/**
* Utility method to set the attribute value on the given
* element node
* @param sNode
* @param attribName
* @param val
* @return
*/
public static void setAttributeValue(Node sNode, String attribName, String val){
String value=null;
Node attr = sNode.getAttributes().getNamedItem(attribName);
if(attr!=null){
attr.setNodeValue(val);
} else {
attr = sNode.getOwnerDocument().createAttribute(attribName);
attr.setNodeValue(val);
sNode.getAttributes().setNamedItem(attr);
}
}
/**
* Utility method to fetch the value of the element node
* @param node
* @return
*/
public static String getNodeValue(Node node){
for(Node child=node.getFirstChild(); child!=null; child=child.getNextSibling() ){
if(child.getNodeType()==Node.TEXT_NODE){
return child.getNodeValue();
}
}
return null;
}
/**
* Simple worker method to parse filename to a Document.
*
* Attempts XML parse, then HTML parse (when parser available),
* then just parses as text and sticks into a text node.
*
* @param filename to parse as a local path
*
* @return Document object with contents of the file;
* otherwise throws an unchecked RuntimeException if there
* is any fatal problem
*/
public static Document parse(String filename, boolean ignoreError)throws RuntimeException {
// Force filerefs to be URI's if needed: note this is independent of any other files
String docURI = filenameToURL(filename);
return parse(new InputSource(docURI),filename,ignoreError);
}
public static Document parseAndResolveAliases(String locale, String sourceDir, boolean ignoreError){
try{
Document full = parse(sourceDir+File.separator+ locale, ignoreError);
if(full!=null){
full = resolveAliases(full, sourceDir, locale );
}
/*
* Debugging
*
Node[] list = getNodeArray(full, LDMLConstants.ALIAS);
if(list.length>0){
System.err.println("Aliases not resolved!. list.getLength() returned "+ list.length);
}*/
return full;
}catch(Exception ex){
if(!ignoreError){
throw new RuntimeException(ex);
}
}
return null;
}
private static ErrorHandler getNullErrorHandler(final String filename2){
// Local class: cheap non-printing ErrorHandler
// This is used to suppress validation warnings
ErrorHandler nullHandler = new ErrorHandler() {
public void warning(SAXParseException e) throws SAXException { int col = e.getColumnNumber();
System.err.println(filename2 + ":" + e.getLineNumber() + (col>=0?":" + col:"") + ": WARNING: " + e.getMessage());}
public void error(SAXParseException e) throws SAXException { int col = e.getColumnNumber();
System.err.println(filename2 + ":" + e.getLineNumber() + (col>=0?":" + col:"") + ": ERROR: " + e.getMessage());}
public void fatalError(SAXParseException e) throws SAXException
{
throw e;
}
};
return nullHandler;
}
public static Document newDocument() {
return newDocumentBuilder(false).newDocument();
}
private static DocumentBuilder newDocumentBuilder(boolean validating) {
try
{
DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
// Always set namespaces on
dfactory.setNamespaceAware(true);
dfactory.setValidating(validating);
dfactory.setIgnoringComments(false);
// Set other attributes here as needed
//applyAttributes(dfactory, attributes);
DocumentBuilder docBuilder = dfactory.newDocumentBuilder();
docBuilder.setEntityResolver(new CachingEntityResolver());
return docBuilder;
} catch(Throwable se) {
System.err.println(": ERROR : trying to create documentBuilder: " + se.getMessage());
se.printStackTrace();
throw new RuntimeException(se);
}
}
public static Document parse(InputSource docSrc, String filename, boolean ignoreError){
Document doc = null;
try
{
// First, attempt to parse as XML (preferred)...
DocumentBuilder docBuilder = newDocumentBuilder(true);
docBuilder.setErrorHandler(getNullErrorHandler(filename));
doc = docBuilder.parse(docSrc);
}
catch (Throwable se)
{
// ... if we couldn't parse as XML, attempt parse as HTML...
System.err.println(filename + ": ERROR :" + se.getMessage());
if(!ignoreError){
throw new RuntimeException(se);
}
}
return doc;
} // end of parse()
/*
* Utility method to translate a String filename to URL.
*
* Note: This method is not necessarily proven to get the
* correct URL for every possible kind of filename; it should
* be improved. It handles the most common cases that we've
* encountered when running Conformance tests on Xalan.
* Also note, this method does not handle other non-file:
* flavors of URLs at all.
*
* If the name is null, return null.
* If the name starts with a common URI scheme (namely the ones
* found in the examples of RFC2396), then simply return the
* name as-is (the assumption is that it's already a URL)
* Otherwise we attempt (cheaply) to convert to a file:/// URL.
*/
public static String filenameToURL(String filename){
// null begets null - something like the commutative property
if (null == filename){
return null;
}
// Don't translate a string that already looks like a URL
if (filename.startsWith("file:")
|| filename.startsWith("http:")
|| filename.startsWith("ftp:")
|| filename.startsWith("gopher:")
|| filename.startsWith("mailto:")
|| filename.startsWith("news:")
|| filename.startsWith("telnet:")
){
return filename;
}
File f = new File(filename);
String tmp = null;
try{
// This normally gives a better path
tmp = f.getCanonicalPath();
}catch (IOException ioe){
// But this can be used as a backup, for cases
// where the file does not exist, etc.
tmp = f.getAbsolutePath();
}
// URLs must explicitly use only forward slashes
if (File.separatorChar == '\\') {
tmp = tmp.replace('\\', '/');
}
// Note the presumption that it's a file reference
// Ensure we have the correct number of slashes at the
// start: we always want 3 /// if it's absolute
// (which we should have forced above)
if (tmp.startsWith("/")){
return "file://" + tmp;
}
else{
return "file:///" + tmp;
}
}
/**
* Debugging method for printing out the DOM Tree
* Prints the specified node, recursively.
* @param node
* @param out
* @throws IOException
*/
public static void printDOMTree(Node node, PrintWriter out) throws IOException
{
try {
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, "http://oss.software.ibm.com/cvs/icu/~checkout~/locale/ldml.dtd");
//transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, XLIFF_PUBLIC_NAME);
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.transform(new DOMSource(node), new StreamResult(out));
}
catch (TransformerException te) {
throw new IOException(te.getMessage());
}
//out.close();
} // printDOMTree(Node, PrintWriter)
private static String escapeForXML(String string){
int len = string.length();
StringBuffer ret = new StringBuffer(len);
for(int i =0 ; i<len; i++){
char ch = string.charAt(i);
switch(ch){
case '<':
ret.append("&lt;");
break;
case '>':
ret.append("&gt;");
break;
case '&':
ret.append("&amp;");
break;
default :
ret.append(ch);
}
}
return ret.toString();
}
// Utility functions, HTML and such.
public static final String getCVSLink(String locale)
{
return "<a href=\"http://oss.software.ibm.com/cvs/icu/locale/common/main/" + locale + ".xml\">";
}
public static final String getCVSLink(String locale, String version)
{
return "<a href=\"http://oss.software.ibm.com/cvs/icu/locale/common/main/" + locale + ".xml?rev=" +
version + "&content-type=text/x-cvsweb-markup\">";
}
static public String getCVSVersion(String fileName)
{
int index = fileName.lastIndexOf(File.separatorChar);
if(index==-1) {
return null;
}
String sourceDir = fileName.substring(0, index);
return getCVSVersion(sourceDir, new File(fileName).getName());
}
static public String getCVSVersion(String sourceDir, String fileName) {
String aVersion = null;
File entriesFile = new File(sourceDir + File.separatorChar + "CVS","Entries");
if(!entriesFile.exists() || !entriesFile.canRead()) {
System.out.println("Can't read, won't try to get CVS " + entriesFile.toString());
return null;
}
try{
BufferedReader r = new BufferedReader(new FileReader(entriesFile.getPath()));
String s;
while((s=r.readLine())!=null) {
String lookFor = "/"+fileName+"/";
if(s.startsWith(lookFor)) {
String ver = s.substring(lookFor.length());
ver = ver.substring(0,ver.indexOf('/'));
aVersion = ver;
}
}
r.close();
} catch ( Throwable th ) {
System.err.println(th.toString() + " trying to read CVS Entries file " + entriesFile.getPath());
return null;
}
return aVersion;
}
// // Caching Resolution
// private static File getCacheName(String sourceDir, String last, String loc)
// {
// //File xCacheDir = new File((CachingEntityResolver.getCacheDir()!=null)?CachingEntityResolver.getCacheDir():"/tmp/cldrres");
// return new File(sourceDir).getName() + "_" + last + "." + loc;
// }
// Document readMergeCache(String sourceDir, String last, String loc)
// {
// File cacheName = getCacheName(String sourceDir, last, loc);
// System.out.println(" M: " + cacheName);
// File cacheFile = new File(xCacheDir, cacheName + ".xml");
// if(cacheFile.exists()) { // && is newer than last, loc
// doc = parse(cacheFile.getPath(),ignoreUnavailable);
// }
// if(doc!=null) {
// System.out.println("Cache hit for " + cacheName);
// }
// return doc;
// }
// void writeMergeCache(String sourceDir, String last, String loc, Document full)
// {
// try {
// OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(cacheFile),"UTF-8");
// PrintWriter pw = new PrintWriter(writer);
// printDOMTree(full,pw);
// long stop = System.currentTimeMillis();
// long total = (stop-start);
// double s = total/1000.;
// System.out.println(" " + cacheName + " parse time: " + s + "s");
// pw.println("<!-- " + cacheName + " parse time: " + s + "s -->");
// writer.close();
// } catch (Throwable t) {
// System.err.println(t.toString() + " while trying to write cache file " + cacheName);
// }
// }
static public String getFullPath(boolean fileType, String fName, String sourceDir, String destDir){
String str;
int lastIndex1 = fName.lastIndexOf(File.separator, fName.length()) + 1/* add 1 to skip past the separator */;
int lastIndex2 = fName.lastIndexOf('.', fName.length());
if (fileType == true) {
if(lastIndex2 == -1){
fName = fName.trim() + ".txt";
}else{
if(!fName.substring(lastIndex2).equalsIgnoreCase(".txt")){
fName = fName.substring(lastIndex1,lastIndex2) + ".txt";
}
}
if (destDir != null && fName != null) {
str = destDir + "/" + fName.trim();
} else {
str = System.getProperty("user.dir") + "/" + fName.trim();
}
} else {
if(lastIndex2 == -1){
fName = fName.trim() + ".xml";
}else{
if(!fName.substring(lastIndex2).equalsIgnoreCase(".xml") && fName.substring(lastIndex2).equalsIgnoreCase(".xlf")){
fName = fName.substring(lastIndex1,lastIndex2) + ".xml";
}
}
if(sourceDir != null && fName != null) {
str = sourceDir + "/" + fName;
} else if (lastIndex1 > 0) {
str = fName;
} else {
str = System.getProperty("user.dir") + "/" + fName;
}
}
return str;
}
}