View Javadoc

1   /*
2    * Jour - bytecode instrumentation library
3    *
4    * Copyright (C) 2007 Vlad Skarzhevskyy
5    *
6    * This library is free software; you can redistribute it and/or
7    * modify it under the terms of the GNU Library General Public
8    * License as published by the Free Software Foundation; either
9    * version 2 of the License, or (at your option) any later version.
10   *
11   * This library is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   * Library General Public License for more details.
15   *
16   * You should have received a copy of the GNU Library General Public
17   * License along with this library; if not, write to the
18   * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19   * Boston, MA  02111-1307, USA.
20   */
21  package net.sf.jour.signature;
22  
23  import java.io.FileNotFoundException;
24  import java.io.FileOutputStream;
25  import java.io.OutputStream;
26  import java.util.Iterator;
27  import java.util.List;
28  
29  import javassist.CtBehavior;
30  import javassist.CtClass;
31  import javassist.CtConstructor;
32  import javassist.CtField;
33  import javassist.CtMethod;
34  import javassist.Modifier;
35  import javassist.NotFoundException;
36  
37  import javax.xml.parsers.DocumentBuilder;
38  import javax.xml.parsers.DocumentBuilderFactory;
39  import javax.xml.parsers.FactoryConfigurationError;
40  import javax.xml.parsers.ParserConfigurationException;
41  import javax.xml.transform.OutputKeys;
42  import javax.xml.transform.Transformer;
43  import javax.xml.transform.TransformerException;
44  import javax.xml.transform.TransformerFactory;
45  import javax.xml.transform.dom.DOMSource;
46  import javax.xml.transform.stream.StreamResult;
47  
48  import net.sf.jour.util.FileUtil;
49  
50  import org.w3c.dom.Attr;
51  import org.w3c.dom.Document;
52  import org.w3c.dom.Element;
53  import org.w3c.dom.Node;
54  
55  /**
56   * @author vlads
57   *
58   */
59  public class ExportXML {
60  
61  	public static final String rootNodeName = "signature";
62  	
63  	private Document document; 
64  	
65  	private APIFilter filter;
66  		
67  	public static void export(String reportFile, List classes, APIFilter filter) {
68  
69  		DocumentBuilder builder;
70  		try {
71  			builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
72  		} catch (ParserConfigurationException e) {
73  			throw new RuntimeException(e);
74  		} catch (FactoryConfigurationError e) {
75  			throw new RuntimeException(e);
76  		}
77  		
78  		ExportXML instance = new ExportXML();
79  		instance.filter = filter;
80  		
81  		instance.document = builder.newDocument();
82  		
83  		Element root = instance.document.createElement(rootNodeName);
84  		instance.document.appendChild(root);
85  		
86  		for (Iterator iterator = classes.iterator(); iterator.hasNext();) {
87  			CtClass klass = (CtClass) iterator.next();
88  			if (filter.isAPIClass(klass)) {
89  				root.appendChild(instance.classNode(klass));
90  			}
91  		}
92  		instance.serializeXML(reportFile);
93  	}
94  	
95  	private void serializeXML(String reportFile) {
96  		OutputStream out = null;
97  		try {
98  			Transformer transformer = TransformerFactory.newInstance().newTransformer();
99  
100 			transformer.setOutputProperty(OutputKeys.INDENT, "yes");
101 			transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
102 			
103 			DOMSource source = new DOMSource(document);
104 			
105 			out = new FileOutputStream(reportFile);
106 
107 			StreamResult result = new StreamResult(out);
108 			transformer.transform(source, result);
109 
110 		} catch (TransformerException e) {
111 			throw new RuntimeException(e);
112 		} catch (FileNotFoundException e) {
113 			throw new RuntimeException(e);
114 		} finally {
115 			FileUtil.closeQuietly(out);
116 		}
117 	}
118 	
119 	private void addAttribute(Node node, final String attrName, final String attrValue) {
120 		Attr attr = document.createAttribute(attrName);
121 		attr.setValue(attrValue);
122 		node.getAttributes().setNamedItem(attr);
123 	}
124 	
125 	private void addModifiers(Node node, int mod) {
126 		mod = APIFilter.filterModifiers(mod);
127 		if (mod != 0) {
128 			addAttribute(node, "modifiers", Modifier.toString(mod));
129 		}
130 	}
131 	
132 	private Node classNode(CtClass klass) {
133 		String nodeName = klass.isInterface()?"interface":"class";
134 		Element element = document.createElement(nodeName);
135 		addAttribute(element, "name", klass.getName()); 
136 		
137 		addModifiers(element, klass.getModifiers());
138 		
139 		try {
140 			buildHierarchy(element, klass);
141 			buildConstructors(element, klass);
142 			buildMethods(element, klass);
143 			buildFields(element, klass);
144 		} catch (NotFoundException e) {
145 			throw new RuntimeException(klass.getName(), e);
146 		}
147 		
148 		return element;
149 	}
150 
151 	private void buildHierarchy(Element element, CtClass klass) throws NotFoundException {
152 			CtClass superClass = klass.getSuperclass();
153 			if ((superClass != null) && (!superClass.getName().equals(Object.class.getName()))) {
154 				addAttribute(element, "extends", superClass.getName());
155 			}
156 
157 			CtClass[] interfaces = klass.getInterfaces();
158 			if (interfaces.length != 0) {
159 				
160 				Element interfacesNode = document.createElement("implements");
161 				element.appendChild(interfacesNode);
162 				
163 				for (int i = 0; i < interfaces.length; i++) {
164 					Element intElement = document.createElement("interface");
165 					addAttribute(intElement, "name", interfaces[i].getName());
166 					interfacesNode.appendChild(intElement);
167 				}
168 			}
169 	}
170 	
171 	private void buildExceptions(Element element, CtBehavior method) throws NotFoundException {
172 		CtClass[] exceptions = method.getExceptionTypes();
173 		for (int k = 0; k < exceptions.length; k++) {
174 			Element exceptElement = document.createElement("exception");
175 			addAttribute(exceptElement, "name", exceptions[k].getName());
176 			element.appendChild(exceptElement);
177 		}
178 	}
179 	
180 	private void buildParameterTypes(Element element, CtBehavior method) throws NotFoundException {
181 		CtClass[] params = method.getParameterTypes();
182 		for (int k = 0; k < params.length; k++) {
183 			Element paramsElement = document.createElement("parameter");
184 			addAttribute(paramsElement, "type", params[k].getName());
185 			element.appendChild(paramsElement);
186 		}
187 	}
188 	
189 	private void buildConstructors(Element element, CtClass klass) throws NotFoundException {
190 		CtConstructor[] constructors = klass.getDeclaredConstructors();
191 		int exportedCount = 0;
192 		for (int i = 0; i < constructors.length; i++) {
193 			if (!filter.isAPIMember(constructors[i])) {
194 				continue;
195 			}
196 			buildConstructor(element, constructors[i]);
197 			exportedCount ++;
198 		}
199 		// Define some constructor
200 		APIFilter lrfilter = filter;
201 		while (exportedCount == 0) {
202 		    try {
203                 lrfilter = lrfilter.getLessRestrictiveFilter();
204             } catch (IllegalArgumentException e) {
205                 break;
206             }
207             for (int i = 0; i < constructors.length; i++) {
208                 if (!lrfilter.isAPIMember(constructors[i])) {
209                     continue;
210                 }
211                 buildConstructor(element, constructors[i]);
212                 exportedCount ++;
213             }
214 		}
215 	}
216 	
217 	private void buildConstructor(Element element, CtConstructor constructor) throws NotFoundException {
218 	    Element constructorElement = document.createElement("constructor");
219         addModifiers(constructorElement, constructor.getModifiers());
220         buildExceptions(constructorElement, constructor);
221         buildParameterTypes(constructorElement, constructor);
222         element.appendChild(constructorElement);
223 	}
224 	
225 	private void buildMethods(Element element, CtClass klass) throws NotFoundException {
226 		CtMethod[] methods = klass.getDeclaredMethods();
227 		for (int i = 0; i < methods.length; i++) {
228 			
229 			if (!filter.isAPIMember(methods[i])) {
230 				continue;
231 			}
232 			
233 			Element methElement = document.createElement("method");
234 			addAttribute(methElement, "name", methods[i].getName());
235 			addAttribute(methElement, "return", methods[i].getReturnType().getName());
236 			addModifiers(methElement, methods[i].getModifiers());
237 			buildExceptions(methElement, methods[i]);
238 			buildParameterTypes(methElement, methods[i]);		
239 			
240 			element.appendChild(methElement);
241 		}
242 	}
243 	
244 	private void buildFields(Element element, CtClass klass) throws NotFoundException {
245 		CtField[] fields = klass.getDeclaredFields();
246 		for (int i = 0; i < fields.length; i++) {
247 			
248 			if (!filter.isAPIMember(fields[i])) {
249 				continue;
250 			}
251 			
252 			Element fieldElement = document.createElement("field");
253 			addAttribute(fieldElement, "name", fields[i].getName());
254 			addAttribute(fieldElement, "type", fields[i].getType().getName());
255 			addModifiers(fieldElement, fields[i].getModifiers());
256 
257 			int mod = fields[i].getModifiers();
258 			
259 			if ((Modifier.isFinal(mod)) && (Modifier.isStatic(mod)) && APIFilter.isExportableConstantType(fields[i].getType())) {
260 				Object constValue = fields[i].getConstantValue();
261 				if (constValue != null) {
262 					addAttribute(fieldElement, "constant-value", constValue.toString());		
263 				}
264 			}
265 			
266 			element.appendChild(fieldElement);
267 		}
268 	}
269 			
270 	
271 }