blob: d55528d8c0c4da90224b410021603b00fe12757f [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tika.parser.asm;
import java.io.IOException;
import java.io.InputStream;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.apache.tika.exception.RuntimeSAXException;
import org.apache.tika.exception.TikaException;
import org.apache.tika.exception.WriteLimitReachedException;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.metadata.TikaCoreProperties;
import org.apache.tika.sax.XHTMLContentHandler;
/**
* Class visitor that generates XHTML SAX events to describe the
* contents of the visited class.
*/
class XHTMLClassVisitor extends ClassVisitor {
private final XHTMLContentHandler xhtml;
private final Metadata metadata;
private Type type;
private String packageName;
public XHTMLClassVisitor(ContentHandler handler, Metadata metadata) {
super(Opcodes.ASM7);
this.xhtml = new XHTMLContentHandler(handler, metadata);
this.metadata = metadata;
}
private static boolean isSet(int value, int flag) {
return (value & flag) != 0;
}
public void parse(InputStream stream) throws TikaException, SAXException, IOException {
try {
ClassReader reader = new ClassReader(stream);
reader.accept(this, ClassReader.SKIP_FRAMES | ClassReader.SKIP_CODE);
} catch (RuntimeException e) {
WriteLimitReachedException.throwIfWriteLimitReached(e);
throw new TikaException("Failed to parse a Java class", e);
}
}
public void visit(int version, int access, String name, String signature, String superName,
String[] interfaces) {
type = Type.getObjectType(name);
String className = type.getClassName();
int dot = className.lastIndexOf('.');
if (dot != -1) {
packageName = className.substring(0, dot);
className = className.substring(dot + 1);
}
metadata.set(TikaCoreProperties.TITLE, className);
metadata.set(TikaCoreProperties.RESOURCE_NAME_KEY, className + ".class");
try {
xhtml.startDocument();
xhtml.startElement("pre");
if (packageName != null) {
writeKeyword("package");
xhtml.characters(" " + packageName + ";\n");
}
writeAccess(access);
if (isSet(access, Opcodes.ACC_INTERFACE)) {
writeKeyword("interface");
writeSpace();
writeType(type);
writeSpace();
writeInterfaces("extends", interfaces);
} else if (isSet(access, Opcodes.ACC_ENUM)) {
writeKeyword("enum");
writeSpace();
writeType(type);
writeSpace();
} else {
writeKeyword("class");
writeSpace();
writeType(type);
writeSpace();
if (superName != null) {
Type superType = Type.getObjectType(superName);
if (!superType.getClassName().equals("java.lang.Object")) {
writeKeyword("extends");
writeSpace();
writeType(superType);
writeSpace();
}
}
writeInterfaces("implements", interfaces);
}
xhtml.characters("{\n");
} catch (SAXException e) {
throw new RuntimeSAXException(e);
}
}
private void writeInterfaces(String keyword, String[] interfaces) throws SAXException {
if (interfaces != null && interfaces.length > 0) {
writeKeyword(keyword);
String separator = " ";
for (String iface : interfaces) {
xhtml.characters(separator);
writeType(Type.getObjectType(iface));
separator = ", ";
}
writeSpace();
}
}
public void visitEnd() {
try {
xhtml.characters("}\n");
xhtml.endElement("pre");
xhtml.endDocument();
} catch (SAXException e) {
throw new RuntimeSAXException(e);
}
}
/**
* Ignored.
*/
public void visitOuterClass(String owner, String name, String desc) {
}
/**
* Ignored.
*/
public void visitSource(String source, String debug) {
}
/**
* Ignored.
*/
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
return null;
}
/**
* Ignored.
*/
public void visitAttribute(Attribute attr) {
}
/**
* Ignored.
*/
public void visitInnerClass(String name, String outerName, String innerName, int access) {
}
/**
* Visits a field.
*/
public FieldVisitor visitField(int access, String name, String desc, String signature,
Object value) {
if (!isSet(access, Opcodes.ACC_SYNTHETIC)) {
try {
xhtml.characters(" ");
writeAccess(access);
writeType(Type.getType(desc));
writeSpace();
writeIdentifier(name);
if (isSet(access, Opcodes.ACC_STATIC) && value != null) {
xhtml.characters(" = ");
xhtml.characters(value.toString());
}
writeSemicolon();
writeNewline();
} catch (SAXException e) {
throw new RuntimeSAXException(e);
}
}
return null;
}
/**
* Visits a method.
*/
public MethodVisitor visitMethod(int access, String name, String desc, String signature,
String[] exceptions) {
if (!isSet(access, Opcodes.ACC_SYNTHETIC)) {
try {
xhtml.characters(" ");
writeAccess(access);
writeType(Type.getReturnType(desc));
writeSpace();
if ("<init>".equals(name)) {
writeType(type);
} else {
writeIdentifier(name);
}
xhtml.characters("(");
String separator = "";
for (Type arg : Type.getArgumentTypes(desc)) {
xhtml.characters(separator);
writeType(arg);
separator = ", ";
}
xhtml.characters(")");
if (exceptions != null && exceptions.length > 0) {
writeSpace();
writeKeyword("throws");
separator = " ";
for (String exception : exceptions) {
xhtml.characters(separator);
writeType(Type.getObjectType(exception));
separator = ", ";
}
}
writeSemicolon();
writeNewline();
} catch (SAXException e) {
throw new RuntimeSAXException(e);
}
}
return null;
}
private void writeIdentifier(String identifier) throws SAXException {
xhtml.startElement("span", "class", "java-identifier");
xhtml.characters(identifier);
xhtml.endElement("span");
}
private void writeKeyword(String keyword) throws SAXException {
xhtml.startElement("span", "class", "java-keyword");
xhtml.characters(keyword);
xhtml.endElement("span");
}
private void writeSemicolon() throws SAXException {
xhtml.characters(";");
}
private void writeSpace() throws SAXException {
xhtml.characters(" ");
}
private void writeNewline() throws SAXException {
xhtml.characters("\n");
}
private void writeAccess(int access) throws SAXException {
writeAccess(access, Opcodes.ACC_PRIVATE, "private");
writeAccess(access, Opcodes.ACC_PROTECTED, "protected");
writeAccess(access, Opcodes.ACC_PUBLIC, "public");
writeAccess(access, Opcodes.ACC_STATIC, "static");
writeAccess(access, Opcodes.ACC_FINAL, "final");
writeAccess(access, Opcodes.ACC_ABSTRACT, "abstract");
writeAccess(access, Opcodes.ACC_SYNCHRONIZED, "synchronized");
writeAccess(access, Opcodes.ACC_TRANSIENT, "transient");
writeAccess(access, Opcodes.ACC_VOLATILE, "volatile");
writeAccess(access, Opcodes.ACC_NATIVE, "native");
}
private void writeAccess(int access, int code, String keyword) throws SAXException {
if (isSet(access, code)) {
writeKeyword(keyword);
xhtml.characters(" ");
}
}
private void writeType(Type type) throws SAXException {
String name = type.getClassName();
if (name.startsWith(packageName + ".")) {
xhtml.characters(name.substring(packageName.length() + 1));
} else if (name.startsWith("java.lang.")) {
xhtml.characters(name.substring("java.lang.".length()));
} else {
xhtml.characters(name);
}
}
}