View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
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   * Class visitor that generates XHTML SAX events to describe the
38   * contents of the visited class.
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      * Ignored.
153      */
154     public void visitOuterClass(String owner, String name, String desc) {
155     }
156 
157     /**
158      * Ignored.
159      */
160     public void visitSource(String source, String debug) {
161     }
162 
163 
164     /**
165      * Ignored.
166      */
167     public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
168         return null;
169     }
170 
171     /**
172      * Ignored.
173      */
174     public void visitAttribute(Attribute attr) {
175     }
176 
177     /**
178      * Ignored.
179      */
180     public void visitInnerClass(
181             String name, String outerName, String innerName, int access) {
182     }
183 
184     /**
185      * Visits a field.
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      * Visits a method.
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 }