package org.jgroups.util;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeMap;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
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;
import org.jgroups.annotations.Experimental;
import org.jgroups.annotations.Property;
import org.jgroups.annotations.Unsupported;
import org.jgroups.stack.Configurator;
import org.jgroups.stack.Protocol;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* Iterates over all concrete Protocol classes and creates an XML file per
* Protocol class.
*
* <p>
* Each XML file in turn contains all protocol properties along with property
* description. The output XML snipet is conforming docbook format so that
* property descriptions can be easily added to master docbook JGroups
* documentation.
*
* @author Vladimir Blagojevic
* @version $Id: PropertiesToXML.java,v 1.5 2008/11/03 15:14:57 vlada Exp $
*
*/
public class PropertiesToXML {
public static void main(String[] args) {
try {
File f = new File("doc/manual/en/modules/protocols.xml");
String s = fileToString(f);
Set<Class<?>> classes=getClasses("org.jgroups.protocols", Protocol.class);
classes.addAll(getClasses("org.jgroups.protocols.pbcast", Protocol.class));
Properties props = new Properties();
for(Class<?> clazz:classes) {
classToXML(props,clazz);
}
String result = Util.replaceProperties(s, props);
FileWriter fw = new FileWriter(f,false);
fw.write(result);
fw.flush();
fw.close();
}
catch(Exception e) {
e.printStackTrace();
}
}
private static Set<Class<?>> getClasses(String packageName, Class<?> assignableFrom) throws IOException,
ClassNotFoundException {
ClassLoader loader=Thread.currentThread().getContextClassLoader();
Set<Class<?>> classes=new HashSet<Class<?>>();
String path=packageName.replace('.', '/');
URL resource=loader.getResource(path);
if(resource != null) {
String filePath=resource.getFile();
if(filePath != null && new File(filePath).isDirectory()) {
for(String file:new File(filePath).list()) {
if(file.endsWith(".class")) {
String name=packageName + '.' + file.substring(0, file.indexOf(".class"));
Class<?> clazz=Class.forName(name);
if(assignableFrom.isAssignableFrom(clazz))
classes.add(clazz);
}
}
}
}
return classes;
}
private static void classToXML(Properties props, Class<?> clazz) throws ParserConfigurationException,
TransformerException {
boolean isConcreteClass=(clazz.getModifiers() & Modifier.ABSTRACT) == 0;
boolean isExperimental=clazz.isAnnotationPresent(Experimental.class);
boolean isUnsupported=clazz.isAnnotationPresent(Unsupported.class);
if(isConcreteClass && !isExperimental && !isUnsupported) {
Class<?> protocol=clazz;
Document xmldoc=null;
DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
DocumentBuilder builder=factory.newDocumentBuilder();
DOMImplementation impl=builder.getDOMImplementation();
xmldoc=impl.createDocument(null, "table", null);
Element tbody=createXMLTree(xmldoc);
Map<String,String> nameToDescription = new TreeMap<String,String>();
//iterate fields
for(Class clazzInLoop=clazz;clazzInLoop != null;clazzInLoop=clazzInLoop.getSuperclass()) {
Field[] fields=clazzInLoop.getDeclaredFields();
for(Field field:fields) {
if(field.isAnnotationPresent(Property.class)) {
String property=field.getName();
Property annotation=field.getAnnotation(Property.class);
String desc=annotation.description();
nameToDescription.put(property, desc);
}
}
}
//iterate methods
Method[] methods=clazz.getMethods();
for(Method method:methods) {
if(method.isAnnotationPresent(Property.class) && method.getName()
.startsWith("set")) {
Property annotation=method.getAnnotation(Property.class);
String desc=annotation.description();
if(desc.length() > 0) {
String name=annotation.name();
if(name.length() < 1) {
name=Configurator.renameFromJavaCodingConvention(method.getName()
.substring(3));
}
nameToDescription.put(name, desc);
}
}
}
//and write them out
for(Map.Entry<String,String> e:nameToDescription.entrySet()){
Element row=xmldoc.createElement("row");
Element entry=xmldoc.createElement("entry");
entry.setTextContent(e.getKey());
row.appendChild(entry);
entry=xmldoc.createElement("entry");
entry.setTextContent(e.getValue());
row.appendChild(entry);
tbody.appendChild(row);
}
//do we have more than one property (superclass Protocol has only one property (stats))
if(nameToDescription.size()>1) {
DOMSource domSource=new DOMSource(xmldoc);
StringWriter sw = new StringWriter();
StreamResult streamResult=new StreamResult(sw);
TransformerFactory tf=TransformerFactory.newInstance();
Transformer serializer=tf.newTransformer();
serializer.setOutputProperty(OutputKeys.ENCODING, "ISO-8859-1");
serializer.setOutputProperty(OutputKeys.INDENT, "yes");
serializer.transform(domSource, streamResult);
StringBuffer buffer=sw.getBuffer();
buffer.delete(0, buffer.indexOf("table")-1);
props.put(protocol.getSimpleName(), buffer.toString());
}
}
}
private static String fileToString(File f) throws Exception {
StringWriter output=new StringWriter();
FileReader input = new FileReader(f);
char[] buffer=new char[8 * 1024];
int count=0;
int n=0;
while(-1 != (n=input.read(buffer))) {
output.write(buffer, 0, n);
count+=n;
}
return output.toString();
}
public static int copy(Reader input, Writer output) throws IOException {
char[] buffer=new char[8 * 1024];
int count=0;
int n=0;
while(-1 != (n=input.read(buffer))) {
output.write(buffer, 0, n);
count+=n;
}
return count;
}
private static Element createXMLTree(Document xmldoc) throws ParserConfigurationException {
Element root=xmldoc.getDocumentElement();
Element title=xmldoc.createElement("title");
title.setTextContent("Properties");
root.appendChild(title);
Element tgroup=xmldoc.createElement("tgroup");
tgroup.setAttribute("cols", "2");
root.appendChild(tgroup);
Element colspec=xmldoc.createElement("colspec");
colspec.setAttribute("align", "left");
tgroup.appendChild(colspec);
Element thead=xmldoc.createElement("thead");
tgroup.appendChild(thead);
Element row=xmldoc.createElement("row");
thead.appendChild(row);
Element entry=xmldoc.createElement("entry");
entry.setAttribute("align", "center");
entry.setTextContent("Name");
row.appendChild(entry);
entry=xmldoc.createElement("entry");
entry.setAttribute("align", "center");
entry.setTextContent("Description");
row.appendChild(entry);
Element tbody=xmldoc.createElement("tbody");
tgroup.appendChild(tbody);
return tbody;
}
}