1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.tika.parser.asm;
18
19 import java.io.IOException;
20 import java.io.InputStream;
21
22 import org.apache.tika.exception.TikaException;
23 import org.apache.tika.metadata.Metadata;
24 import org.apache.tika.sax.XHTMLContentHandler;
25 import org.objectweb.asm.AnnotationVisitor;
26 import org.objectweb.asm.Attribute;
27 import org.objectweb.asm.ClassReader;
28 import org.objectweb.asm.ClassVisitor;
29 import org.objectweb.asm.FieldVisitor;
30 import org.objectweb.asm.MethodVisitor;
31 import org.objectweb.asm.Opcodes;
32 import org.objectweb.asm.Type;
33 import org.xml.sax.ContentHandler;
34 import org.xml.sax.SAXException;
35
36
37
38
39
40 class XHTMLClassVisitor implements ClassVisitor {
41
42 private final XHTMLContentHandler xhtml;
43
44 private final Metadata metadata;
45
46 private Type type;
47
48 private String packageName;
49
50 public XHTMLClassVisitor(ContentHandler handler, Metadata metadata) {
51 this.xhtml = new XHTMLContentHandler(handler, metadata);
52 this.metadata = metadata;
53 }
54
55 public void parse(InputStream stream)
56 throws TikaException, SAXException, IOException {
57 try {
58 ClassReader reader = new ClassReader(stream);
59 reader.accept(this, ClassReader.SKIP_FRAMES | ClassReader.SKIP_CODE);
60 } catch (RuntimeException e) {
61 if (e.getCause() instanceof SAXException) {
62 throw (SAXException) e.getCause();
63 } else {
64 throw new TikaException("Failed to parse a Java class", e);
65 }
66 }
67 }
68
69 public void visit(
70 int version, int access, String name, String signature,
71 String superName, String[] interfaces) {
72 type = Type.getObjectType(name);
73
74 String className = type.getClassName();
75 int dot = className.lastIndexOf('.');
76 if (dot != -1) {
77 packageName = className.substring(0, dot);
78 className = className.substring(dot + 1);
79 }
80
81 metadata.set(Metadata.TITLE, className);
82 metadata.set(Metadata.RESOURCE_NAME_KEY, className + ".class");
83
84 try {
85 xhtml.startDocument();
86 xhtml.startElement("pre");
87
88 if (packageName != null) {
89 writeKeyword("package");
90 xhtml.characters(" " + packageName + ";\n");
91 }
92
93 writeAccess(access);
94 if (isSet(access, Opcodes.ACC_INTERFACE)) {
95 writeKeyword("interface");
96 writeSpace();
97 writeType(type);
98 writeSpace();
99 writeInterfaces("extends", interfaces);
100 } else if (isSet(access, Opcodes.ACC_ENUM)) {
101 writeKeyword("enum");
102 writeSpace();
103 writeType(type);
104 writeSpace();
105 } else {
106 writeKeyword("class");
107 writeSpace();
108 writeType(type);
109 writeSpace();
110 if (superName != null) {
111 Type superType = Type.getObjectType(superName);
112 if (!superType.getClassName().equals("java.lang.Object")) {
113 writeKeyword("extends");
114 writeSpace();
115 writeType(superType);
116 writeSpace();
117 }
118 }
119 writeInterfaces("implements", interfaces);
120 }
121 xhtml.characters("{\n");
122 } catch (SAXException e) {
123 throw new RuntimeException(e);
124 }
125 }
126
127 private void writeInterfaces(String keyword, String[] interfaces)
128 throws SAXException {
129 if (interfaces != null && interfaces.length > 0) {
130 writeKeyword(keyword);
131 String separator = " ";
132 for (String iface : interfaces) {
133 xhtml.characters(separator);
134 writeType(Type.getObjectType(iface));
135 separator = ", ";
136 }
137 writeSpace();
138 }
139 }
140
141 public void visitEnd() {
142 try {
143 xhtml.characters("}\n");
144 xhtml.endElement("pre");
145 xhtml.endDocument();
146 } catch (SAXException e) {
147 throw new RuntimeException(e);
148 }
149 }
150
151
152
153
154 public void visitOuterClass(String owner, String name, String desc) {
155 }
156
157
158
159
160 public void visitSource(String source, String debug) {
161 }
162
163
164
165
166
167 public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
168 return null;
169 }
170
171
172
173
174 public void visitAttribute(Attribute attr) {
175 }
176
177
178
179
180 public void visitInnerClass(
181 String name, String outerName, String innerName, int access) {
182 }
183
184
185
186
187 public FieldVisitor visitField(
188 int access, String name, String desc, String signature,
189 Object value) {
190 if (!isSet(access, Opcodes.ACC_SYNTHETIC)) {
191 try {
192 xhtml.characters(" ");
193 writeAccess(access);
194 writeType(Type.getType(desc));
195 writeSpace();
196 writeIdentifier(name);
197
198 if (isSet(access, Opcodes.ACC_STATIC) && value != null) {
199 xhtml.characters(" = ");
200 xhtml.characters(value.toString());
201 }
202
203 writeSemicolon();
204 writeNewline();
205 } catch (SAXException e) {
206 throw new RuntimeException(e);
207 }
208 }
209
210 return null;
211 }
212
213
214
215
216 public MethodVisitor visitMethod(
217 int access, String name, String desc, String signature,
218 String[] exceptions) {
219 if (!isSet(access, Opcodes.ACC_SYNTHETIC)) {
220 try {
221 xhtml.characters(" ");
222 writeAccess(access);
223 writeType(Type.getReturnType(desc));
224 writeSpace();
225 if ("<init>".equals(name)) {
226 writeType(type);
227 } else {
228 writeIdentifier(name);
229 }
230
231 xhtml.characters("(");
232 String separator = "";
233 for (Type arg : Type.getArgumentTypes(desc)) {
234 xhtml.characters(separator);
235 writeType(arg);
236 separator = ", ";
237 }
238 xhtml.characters(")");
239
240 if (exceptions != null && exceptions.length > 0) {
241 writeSpace();
242 writeKeyword("throws");
243 separator = " ";
244 for (String exception : exceptions) {
245 xhtml.characters(separator);
246 writeType(Type.getObjectType(exception));
247 separator = ", ";
248 }
249 }
250
251 writeSemicolon();
252 writeNewline();
253 } catch (SAXException e) {
254 throw new RuntimeException(e);
255 }
256 }
257
258 return null;
259 }
260
261 private void writeIdentifier(String identifier) throws SAXException {
262 xhtml.startElement("span", "class", "java-identifier");
263 xhtml.characters(identifier);
264 xhtml.endElement("span");
265 }
266
267 private void writeKeyword(String keyword) throws SAXException {
268 xhtml.startElement("span", "class", "java-keyword");
269 xhtml.characters(keyword);
270 xhtml.endElement("span");
271 }
272
273 private void writeSemicolon() throws SAXException {
274 xhtml.characters(";");
275 }
276
277 private void writeSpace() throws SAXException {
278 xhtml.characters(" ");
279 }
280
281 private void writeNewline() throws SAXException {
282 xhtml.characters("\n");
283 }
284
285 private void writeAccess(int access) throws SAXException {
286 writeAccess(access, Opcodes.ACC_PRIVATE, "private");
287 writeAccess(access, Opcodes.ACC_PROTECTED, "protected");
288 writeAccess(access, Opcodes.ACC_PUBLIC, "public");
289 writeAccess(access, Opcodes.ACC_STATIC, "static");
290 writeAccess(access, Opcodes.ACC_FINAL, "final");
291 writeAccess(access, Opcodes.ACC_ABSTRACT, "abstract");
292 writeAccess(access, Opcodes.ACC_SYNCHRONIZED, "synchronized");
293 writeAccess(access, Opcodes.ACC_TRANSIENT, "transient");
294 writeAccess(access, Opcodes.ACC_VOLATILE, "volatile");
295 writeAccess(access, Opcodes.ACC_NATIVE, "native");
296 }
297
298 private void writeAccess(int access, int code, String keyword)
299 throws SAXException {
300 if (isSet(access, code)) {
301 writeKeyword(keyword);
302 xhtml.characters(" ");
303 }
304 }
305
306 private void writeType(Type type) throws SAXException {
307 String name = type.getClassName();
308 if (name.startsWith(packageName + ".")) {
309 xhtml.characters(name.substring(packageName.length() + 1));
310 } else if (name.startsWith("java.lang.")) {
311 xhtml.characters(name.substring("java.lang.".length()));
312 } else {
313 xhtml.characters(name);
314 }
315 }
316
317 private static boolean isSet(int value, int flag) {
318 return (value & flag) != 0;
319 }
320
321 }