blob: f7667f12d38646cfa2636f2fdab0d857c8a8fcb6 [file] [log] [blame]
/*
**********************************************************************
* Copyright (c) 2006, International Business Machines
* Corporation and others. All Rights Reserved.
**********************************************************************
* Created on 2006-7-24
*/
package com.ibm.icu.dev.tools.docs;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.lang.reflect.Field;
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Result;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.crimson.jaxp.DocumentBuilderFactoryImpl;
import org.apache.xerces.parsers.DOMParser;
import org.apache.xpath.XPathAPI;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
/**
A utility to report the status change between two ICU releases
To use the utility
1. Generate the XML files
(put the two ICU releases on your machine ^_^ )
(generate 'Doxygen' file on Windows platform with Cygwin's help)
Edit the generated 'Doxygen' file under ICU4C source directory
a) GENERATE_XML = YES
b) Sync the ALIASES definiation
(For example, copy the ALIASES defination from ICU 3.6
Doxygen file to ICU 3.4 Doxygen file.)
c) gerenate the XML files
2. Build the tool
Download Apache Xerces Java Parser
Build this file with the library
3. Edit the api-report-config.xml file & Change the file according your real configuration
4. Run the tool to generate the report.
* @author Raymond Yang
*/
public class StableAPI {
private String leftVer;
private String leftDir;
// private String leftStatus;
private String rightVer;
private String rightDir;
// private String rightStatus;
private String dumpCppXslt;
private String dumpCXslt;
private String reportXsl;
private String resultFile;
final private static String nul = "None";
public static void main(String[] args) throws FileNotFoundException, TransformerException, ParserConfigurationException {
StableAPI t = new StableAPI();
t.parseArgs(args);
Set full = new HashSet();
Set setCpp = t.getFullList(t.dumpCppXslt);
full.addAll(setCpp);
Set setC = t.getFullList(t.dumpCXslt);
full.addAll(setC);
Node fullList = t.setToNode(full);
// t.dumpNode(fullList,"");
t.reportSelectedFun(fullList);
System.out.println("Done. Please check " + t.resultFile);
}
private void parseArgs(String[] args){
for (int i = 0; i < args.length; i++) {
String arg = args[i];
if (arg == null || arg.length() == 0) {
continue;
}
if (arg.equals("--help") ) {
printUsage();
} else if (arg.equals("--oldver") ) {
leftVer = args[++i];
} else if (arg.equals("--olddir") ) {
leftDir = args[++i];
} else if (arg.equals("--newver")) {
rightVer = args[++i];
} else if (arg.equals("--newdir")) {
rightDir = args[++i];
} else if (arg.equals("--cxslt") ) {
dumpCXslt = args[++i];
} else if (arg.equals("--cppxslt") ) {
dumpCppXslt = args[++i];
} else if (arg.equals("--reportxslt") ) {
reportXsl = args[++i];
} else if (arg.equals("--resultfile")) {
resultFile = args[++i];
} else {
System.out.println("Unknown option: "+arg);
printUsage();
}
}
}
private static void printUsage(){
System.out.println("Usage: StableAPI option* target*");
System.out.println();
System.out.println("Options:");
System.out.println(" --help Print this text");
System.out.println(" --oldver Version of old version of ICU");
System.out.println(" --olddir Directory that contains xml docs of old version");
System.out.println(" --newver Version of new version of ICU");
System.out.println(" --newdir Directory that contains xml docs of new version");
System.out.println(" --cxslt XSLT file for C docs");
System.out.println(" --cppxslt XSLT file for C++ docs");
System.out.println(" --reportxslt XSLT file for report docs");
System.out.println(" --resultfile Output file");
System.exit(-1);
}
static String getAttr(Node node, String attrName){
return node.getAttributes().getNamedItem(attrName).getNodeValue();
}
static String getAttr(NamedNodeMap attrList, String attrName){
return attrList.getNamedItem(attrName).getNodeValue();
}
static class Fun {
public String prototype;
public String id;
public String status;
public String file;
public boolean equals(Fun right){
return this.prototype.equals(right.prototype);
}
static Fun fromXml(Node n){
Fun f = new Fun();
f.prototype = getAttr(n, "prototype");
f.id = getAttr(n, "id");
f.status = getAttr(n, "status");
f.file = getAttr(n, "file");
f.purifyPrototype();
f.purifyFile();
return f;
}
private void purifyFile(){
int i = file.lastIndexOf("/");
file = i == -1 ? file : file.substring(i+1);
}
/**
* Special cases:
*
* Remove the status attribute embedded in the C prototype
*
* Remove the virtual keyword in Cpp prototype
*/
private void purifyPrototype(){
//refer to 'umachine.h'
String statusList[] = {"U_CAPI", "U_STABLE", "U_DRAFT", "U_DEPRECATED", "U_OBSOLETE", "U_INTERNAL", "virtual"};
for (int i = 0; i < statusList.length; i++) {
String s = statusList[i];
prototype = prototype.replaceAll(s,"");
prototype = prototype.trim();
}
prototype = prototype.trim();
}
// private Element toXml(Document doc){
// Element ele = doc.createElement("func");
// ele.setAttribute("prototype", prototype);
// ele.setAttribute("id", id);
// ele.setAttribute("status", status);
// return ele;
// }
}
static class JoinedFun {
public String prototype;
public String leftRefId;
public String leftStatus;
public String leftFile;
public String rightRefId;
public String rightStatus;
public String rightFile;
static JoinedFun fromLeftFun(Fun left){
JoinedFun u = new JoinedFun();
u.prototype = left.prototype;
u.leftRefId = left.id;
u.leftStatus = left.status;
u.leftFile = left.file;
u.rightRefId = nul;
u.rightStatus = nul;
u.rightFile = nul;
return u;
}
static JoinedFun fromRightFun(Fun right){
JoinedFun u = new JoinedFun();
u.prototype = right.prototype;
u.leftRefId = nul;
u.leftStatus = nul;
u.leftFile = nul;
u.rightRefId = right.id;
u.rightStatus = right.status;
u.rightFile = right.file;
return u;
}
static JoinedFun fromTwoFun(Fun left, Fun right){
if (!left.equals(right)) throw new Error();
JoinedFun u = new JoinedFun();
u.prototype = left.prototype;
u.leftRefId = left.id;
u.leftStatus = left.status;
u.leftFile = left.file;
u.rightRefId = right.id;
u.rightStatus = right.status;
u.rightFile = right.file;
return u;
}
Element toXml(Document doc){
Element ele = doc.createElement("func");
ele.setAttribute("prototype", prototype);
// ele.setAttribute("leftRefId", leftRefId);
ele.setAttribute("leftStatus", leftStatus);
// ele.setAttribute("rightRefId", rightRefId);
ele.setAttribute("rightStatus", rightStatus);
// String f = rightRefId.equals(nul) ? leftRefId : rightRefId;
// int tail = f.indexOf("_");
// f = tail != -1 ? f.substring(0, tail) : f;
// f = f.startsWith("class") ? f.replaceFirst("class","") : f;
String f = rightFile.equals(nul) ? leftFile : rightFile;
ele.setAttribute("file", f);
return ele;
}
}
TransformerFactory transFac = TransformerFactory.newInstance();
private void reportSelectedFun(Node joinedNode) throws FileNotFoundException, TransformerException{
Transformer report = transFac.newTransformer(new DOMSource(getDocument(reportXsl)));
// report.setParameter("leftStatus", leftStatus);
report.setParameter("leftVer", leftVer);
// report.setParameter("rightStatus", rightStatus);
report.setParameter("rightVer", rightVer);
report.setParameter("dateTime", new GregorianCalendar().getTime());
report.setParameter("nul", nul);
DOMSource src = new DOMSource(joinedNode);
Result res = new StreamResult(new File(resultFile));
// DOMResult res = new DOMResult();
report.transform(src, res);
// dumpNode(res.getNode(),"");
}
private Set getFullList(String dumpXsltFile) throws FileNotFoundException, TransformerException, ParserConfigurationException{
// prepare transformer
Transformer transformer = transFac.newTransformer(new DOMSource(getDocument(dumpXsltFile)));
// Node joinedNode = null;
DOMSource leftIndex = new DOMSource(getDocument(leftDir + "index.xml"));
DOMResult leftResult = new DOMResult();
transformer.setParameter("docFolder", leftDir);
transformer.transform(leftIndex, leftResult);
Node leftList = XPathAPI.selectSingleNode(leftResult.getNode(),"/list");
// dumpNode(leftList,"");
DOMSource rightIndex = new DOMSource(getDocument(rightDir + "index.xml"));
DOMResult rightResutl = new DOMResult();
transformer.setParameter("docFolder", rightDir);
transformer.transform(rightIndex, rightResutl);
Node rightList = XPathAPI.selectSingleNode(rightResutl.getNode(),"/list");
// dumpNode(rightList,"");
Set leftSet = nodeToSet(leftList);
Set rightSet = nodeToSet(rightList);
Set joined = fullJoin(leftSet, rightSet);
return joined;
// joinedNode = setToNode(joined);
// dumpNode(joinedNode,"");
// return joinedNode;
}
/**
* @param node
* @return Set<Fun>
*/
private Set nodeToSet(Node node){
Set s = new HashSet();
NodeList list = node.getChildNodes();
for (int i = 0; i < list.getLength(); i++) {
Node n = list.item(i);
s.add(Fun.fromXml(n));
}
return s;
}
/**
* @param set Set<JoinedFun>
* @return
* @throws ParserConfigurationException
*/
private Node setToNode(Set set) throws ParserConfigurationException{
DocumentBuilderFactory dbf = DocumentBuilderFactoryImpl.newInstance();
Document doc = dbf.newDocumentBuilder().newDocument();
Element root = doc.createElement("list");
doc.appendChild(root);
for (Iterator iter = set.iterator(); iter.hasNext();) {
JoinedFun fun = (JoinedFun) iter.next();
root.appendChild(fun.toXml(doc));
}
return doc;
}
/**
* full-join two Set on 'prototype'
*
* @param left Set<Fun>
* @param right Set<Fun>
* @return Set<JoinedFun>
*/
private static Set fullJoin(Set left, Set right){
Set joined = new HashSet(); //Set<JoinedFun>
Set common = new HashSet(); //Set<Fun>
for (Iterator iter1 = left.iterator(); iter1.hasNext();) {
Fun f1 = (Fun) iter1.next();
// if (f1.prototype.matches(".*Transliterator::.*")){
// System.err.println("left: " + f1.prototype);
// System.err.println("left: " + f1.status);
// }
for (Iterator iter2 = right.iterator(); iter2.hasNext();) {
Fun f2 = (Fun) iter2.next();
// if ( f1.prototype.matches(".*filteredTransliterate.*")
// && f2.prototype.matches(".*filteredTransliterate.*")){
// System.err.println("right: " + f2.prototype);
// System.err.println("right: " + f2.status);
// System.err.println(f1.prototype.equals(f2.prototype));
// System.err.println(f1.prototype.getBytes()[0]);
// System.err.println(f2.prototype.getBytes()[0]);
// }
if (f1.equals(f2)) {
// should add left item to common set
// since we will remove common items with left set later
common.add(f1);
joined.add(JoinedFun.fromTwoFun(f1, f2));
right.remove(f2);
break;
}
}
}
for (Iterator iter = common.iterator(); iter.hasNext();) {
Fun f = (Fun) iter.next();
left.remove(f);
}
for (Iterator iter = left.iterator(); iter.hasNext();) {
Fun f = (Fun) iter.next();
joined.add(JoinedFun.fromLeftFun(f));
}
for (Iterator iter = right.iterator(); iter.hasNext();) {
Fun f = (Fun) iter.next();
joined.add(JoinedFun.fromRightFun(f));
}
return joined;
}
private static void dumpNode(Node n, String pre){
pre += " ";
System.out.println(pre + "<" + n.getNodeName() + ">");
//dump attribute
NamedNodeMap attr = n.getAttributes();
if (attr!=null){
for (int i = 0; i < attr.getLength(); i++) {
System.out.println(attr.item(i));
}
}
// dump value
String v = pre + n.getNodeValue();
// if (n.getNodeType() == Node.TEXT_NODE)
System.out.println(v);
// dump sub nodes
NodeList nList = n.getChildNodes();
for (int i = 0; i < nList.getLength(); i++) {
Node ln = nList.item(i);
dumpNode(ln, pre + " ");
}
System.out.println(pre + "</" + n.getNodeName() + ">");
}
private static Document getDocument(String name) throws FileNotFoundException{
FileInputStream fis = new FileInputStream(name);
InputSource inputSource = new InputSource(fis);
DOMParser parser = new DOMParser();
//convert it into DOM
try {
parser.parse(inputSource);
// fis.close();
} catch (Exception e) {
e.printStackTrace();
}
Document doc = parser.getDocument();
return doc;
}
}