0N/A/*
0N/A * CDDL HEADER START
0N/A *
0N/A * The contents of this file are subject to the terms of the
407N/A * Common Development and Distribution License (the "License").
0N/A * You may not use this file except in compliance with the License.
0N/A *
0N/A * See LICENSE.txt included in this distribution for the specific
0N/A * language governing permissions and limitations under the License.
0N/A *
0N/A * When distributing Covered Code, include this CDDL HEADER in each
0N/A * file and include the License file at LICENSE.txt.
0N/A * If applicable, add the following below this CDDL HEADER, with the
0N/A * fields enclosed by brackets "[]" replaced with your own identifying
0N/A * information: Portions Copyright [yyyy] [name of copyright owner]
0N/A *
0N/A * CDDL HEADER END
0N/A */
0N/A
0N/A/*
1380N/A * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
0N/A */
0N/Apackage org.opensolaris.opengrok.analysis.executables;
0N/A
392N/Aimport java.io.IOException;
392N/Aimport java.io.InputStream;
392N/Aimport java.io.Reader;
392N/Aimport java.io.StringReader;
392N/Aimport java.io.StringWriter;
392N/Aimport java.io.Writer;
392N/Aimport java.util.ArrayList;
457N/Aimport java.util.List;
1470N/A
1470N/Aimport org.apache.bcel.Constants;
392N/Aimport org.apache.bcel.classfile.Attribute;
392N/Aimport org.apache.bcel.classfile.ClassFormatException;
392N/Aimport org.apache.bcel.classfile.ClassParser;
392N/Aimport org.apache.bcel.classfile.Code;
392N/Aimport org.apache.bcel.classfile.Constant;
392N/Aimport org.apache.bcel.classfile.ConstantCP;
392N/Aimport org.apache.bcel.classfile.ConstantClass;
392N/Aimport org.apache.bcel.classfile.ConstantDouble;
392N/Aimport org.apache.bcel.classfile.ConstantFloat;
392N/Aimport org.apache.bcel.classfile.ConstantInteger;
392N/Aimport org.apache.bcel.classfile.ConstantLong;
392N/Aimport org.apache.bcel.classfile.ConstantNameAndType;
392N/Aimport org.apache.bcel.classfile.ConstantPool;
392N/Aimport org.apache.bcel.classfile.ConstantString;
392N/Aimport org.apache.bcel.classfile.ConstantUtf8;
392N/Aimport org.apache.bcel.classfile.ExceptionTable;
392N/Aimport org.apache.bcel.classfile.JavaClass;
392N/Aimport org.apache.bcel.classfile.LocalVariable;
392N/Aimport org.apache.bcel.classfile.LocalVariableTable;
392N/Aimport org.apache.bcel.classfile.Utility;
392N/Aimport org.apache.lucene.analysis.TokenStream;
392N/Aimport org.apache.lucene.document.Document;
99N/Aimport org.apache.lucene.document.Field;
392N/Aimport org.opensolaris.opengrok.analysis.FileAnalyzer;
392N/Aimport org.opensolaris.opengrok.analysis.FileAnalyzerFactory;
392N/Aimport org.opensolaris.opengrok.analysis.List2TokenStream;
392N/Aimport org.opensolaris.opengrok.analysis.TagFilter;
1384N/Aimport org.opensolaris.opengrok.analysis.XrefWriter;
392N/Aimport org.opensolaris.opengrok.analysis.plain.PlainFullTokenizer;
30N/Aimport org.opensolaris.opengrok.configuration.RuntimeEnvironment;
1470N/Aimport org.opensolaris.opengrok.web.Util;
0N/A
0N/A/**
0N/A * Ananlyzes Java Class files
0N/A * Created on September 23, 2005
0N/A *
0N/A * @author Chandan
929N/A * @author Lubos Kosco , January 2010 , updated bcel, comment on thread safety
0N/A */
0N/Apublic class JavaClassAnalyzer extends FileAnalyzer {
527N/A
1470N/A private final String urlPrefix = RuntimeEnvironment.getConfig().getUrlPrefix();
527N/A
527N/A /** Creates a new instance of JavaClassAnalyzer
527N/A * @param factory The factory that creates JavaClassAnalyzers
527N/A */
202N/A protected JavaClassAnalyzer(FileAnalyzerFactory factory) {
202N/A super(factory);
0N/A }
457N/A private List<String> defs;
457N/A private List<String> refs;
457N/A private List<String> full;
0N/A private String xref;
0N/A private JavaClass c;
0N/A
1470N/A /**
1470N/A * {@inheritDoc}
1470N/A */
508N/A @Override
889N/A public void analyze(Document doc, InputStream in) throws IOException {
1266N/A defs = new ArrayList<String>();
1266N/A refs = new ArrayList<String>();
1266N/A full = new ArrayList<String>();
527N/A xref = null;
889N/A
889N/A ClassParser classparser = new ClassParser(in, doc.get("path"));
889N/A c = classparser.parse();
889N/A StringWriter out = new StringWriter();
889N/A getContent(out);
889N/A xref = out.toString();
1266N/A
1266N/A out.getBuffer().setLength(0); // clear the buffer
889N/A for (String fl : full) {
889N/A out.write(fl);
889N/A out.write('\n');
0N/A }
1266N/A String constants = out.toString();
889N/A
1266N/A doc.add(new Field("defs", new List2TokenStream(defs)));
1266N/A doc.add(new Field("refs", new List2TokenStream(refs)));
1266N/A doc.add(new Field("full", new StringReader(xref)));
1266N/A doc.add(new Field("full", new StringReader(constants)));
0N/A }
0N/A
1470N/A /**
1470N/A * Gt the generated cross file content.
1470N/A * @return {@code null} as long as no analyze has been run, the result of
1470N/A * the analayzing phase otherwise.
1470N/A */
1266N/A public String getXref() {
1266N/A return xref;
0N/A }
527N/A
527N/A private int[] v;
0N/A private ConstantPool cp;
527N/A
1470N/A /**
1470N/A * {@inheritDoc}
1470N/A */
527N/A @Override
1380N/A public TokenStream overridableTokenStream(String fieldName, Reader reader) {
1265N/A if ("full".equals(fieldName)) {
1265N/A return new PlainFullTokenizer(new TagFilter(reader));
0N/A }
1380N/A return super.overridableTokenStream(fieldName, reader);
0N/A }
527N/A
1470N/A /**
1470N/A * Get a HTML anchor for the given path.
1470N/A * @param path path in question.
1470N/A * @return a string like &lt;a href="$prefix?path=$path"&gt;$path&lt;/a&gt;
1470N/A */
527N/A protected String linkPath(String path) {
1470N/A return "<a href=\"" + urlPrefix + "path=" + Util.uriEncodeQueryValue(path)
1470N/A + "\">" + Util.htmlize(path) + "</a>";
0N/A }
527N/A
1470N/A /**
1470N/A * Get a HTML anchor for the given symbol.
1470N/A * @param def symbol in question.
1470N/A * @return a string like &lt;a href="$prefix?defs=$def"&gt;$def&lt;/a&gt;
1470N/A */
527N/A protected String linkDef(String def) {
1470N/A return "<a href=\"" + urlPrefix + "defs=" + Util.uriEncodeQueryValue(def)
1470N/A + "\">" + Util.htmlize(def) + "</a>";
0N/A }
527N/A
1470N/A /**
1470N/A * Get a HTML anchor for the given symbol.
1470N/A * @param def symbol in question.
1470N/A * @return a string like &lt;a class="d" name="$def"
1470N/A * href="$prefix?defs=$def"&gt;$def&lt;/a&gt;
1470N/A */
527N/A protected String tagDef(String def) {
1470N/A return "<a class=\"d\" name=\"" + Util.formQuoteEscape(def)
1470N/A + "\" href=\"" + urlPrefix + "defs=" + Util.uriEncodeQueryValue(def)
1470N/A + "\">" + Util.htmlize(def) + "</a>";
0N/A }
1190N/A
1470N/A // TODO this class needs to be thread safe to avoid bug 13364, which was
1470N/A // fixed by just updating bcel to 5.2
527N/A private void getContent(Writer out) throws IOException {
0N/A String t;
0N/A cp = c.getConstantPool();
527N/A v = new int[cp.getLength() + 1];
0N/A out.write(linkPath(t = c.getSourceFileName()));
527N/A defs.add(t);
527N/A refs.add(t);
0N/A out.write('\n');
527N/A
0N/A out.write("package ");
0N/A out.write(linkDef(t = c.getPackageName()));
527N/A defs.add(t);
527N/A refs.add(t);
527N/A
0N/A out.write('\n');
0N/A String aflg = null;
0N/A out.write(aflg = Utility.accessToString(c.getAccessFlags(), true));
527N/A if (aflg != null) {
392N/A out.write(' ');
392N/A }
527N/A
0N/A v[c.getClassNameIndex()] = 1;
0N/A out.write(tagDef(t = c.getClassName()));
527N/A defs.add(t);
527N/A refs.add(t);
0N/A out.write(" extends ");
527N/A
0N/A v[c.getSuperclassNameIndex()] = 1;
0N/A out.write(linkDef(t = c.getSuperclassName()));
0N/A refs.add(t);
527N/A for (int i : c.getInterfaceIndices()) {
527N/A v[i] = 1;
0N/A }
0N/A String ins[] = c.getInterfaceNames();
0N/A if (ins != null && ins.length > 0) {
0N/A out.write(" implements ");
527N/A for (String in : ins) {
0N/A out.write(linkDef(t = in));
0N/A refs.add(t);
0N/A out.write(' ');
0N/A }
0N/A }
0N/A out.write(" {\n");
527N/A
436N/A for (Attribute a : c.getAttributes()) {
1470N/A if (a.getTag() == Constants.ATTR_CODE) {
436N/A for (Attribute ca : ((Code) a).getAttributes()) {
1470N/A if (ca.getTag() == Constants.ATTR_LOCAL_VARIABLE_TABLE) {
1470N/A for (LocalVariable l : ((LocalVariableTable) ca)
1470N/A .getLocalVariableTable())
1470N/A {
436N/A printLocal(out, l);
0N/A }
0N/A }
436N/A }
1470N/A } else if (a.getTag() == Constants.ATTR_SOURCE_FILE) {
436N/A v[a.getNameIndex()] = 1;
436N/A break;
0N/A }
0N/A }
527N/A
527N/A for (org.apache.bcel.classfile.Field fld : c.getFields()) {
0N/A out.write('\t');
0N/A String aflgs;
0N/A out.write(aflgs = Utility.accessToString(fld.getAccessFlags()));
527N/A if (aflgs != null && aflgs.length() > 0) {
392N/A out.write(' ');
392N/A }
0N/A out.write(Utility.signatureToString(fld.getSignature()));
0N/A out.write(' ');
0N/A out.write(tagDef(t = fld.getName()));
527N/A defs.add(t);
527N/A refs.add(t);
0N/A out.write('\n');
527N/A // @TODO show Attributes
0N/A }
527N/A
527N/A for (org.apache.bcel.classfile.Method m : c.getMethods()) {
0N/A out.write('\t');
0N/A String aflgs;
0N/A out.write(aflgs = Utility.accessToString(m.getAccessFlags()));
527N/A if (aflgs != null && aflgs.length() > 0) {
392N/A out.write(' ');
392N/A }
0N/A String sig = m.getSignature();
0N/A out.write(Utility.methodSignatureReturnType(sig, false));
0N/A out.write(' ');
0N/A out.write(tagDef(t = m.getName()));
527N/A defs.add(t);
527N/A refs.add(t);
0N/A out.write(" (");
527N/A String[] args = Utility.methodSignatureArgumentTypes(sig, false);
527N/A for (int i = 0; i < args.length; i++) {
0N/A out.write(t = args[i]);
0N/A int spi = t.indexOf(' ');
527N/A if (spi > 0) {
0N/A refs.add(t.substring(0, spi));
527N/A defs.add(t.substring(spi + 1));
0N/A }
527N/A if (i < args.length - 1) {
0N/A out.write(", ");
392N/A }
0N/A }
0N/A out.write(") ");
0N/A ArrayList<LocalVariable[]> locals = new ArrayList<LocalVariable[]>();
527N/A for (Attribute a : m.getAttributes()) {
1470N/A if (a.getTag() == Constants.ATTR_EXCEPTIONS) {
527N/A for (int i : ((ExceptionTable) a).getExceptionIndexTable()) {
0N/A v[i] = 1;
0N/A }
527N/A String[] exs = ((ExceptionTable) a).getExceptionNames();
527N/A if (exs != null && exs.length > 0) {
0N/A out.write(" throws ");
527N/A for (String ex : exs) {
0N/A out.write(linkDef(ex));
0N/A refs.add(ex);
0N/A out.write(' ');
0N/A }
0N/A }
1470N/A } else if (a.getTag() == Constants.ATTR_CODE) {
527N/A for (Attribute ca : ((Code) a).getAttributes()) {
1470N/A if (ca.getTag() == Constants.ATTR_LOCAL_VARIABLE_TABLE) {
1470N/A locals.add(((LocalVariableTable) ca)
1470N/A .getLocalVariableTable());
0N/A }
0N/A }
0N/A }
0N/A }
0N/A out.write("\n");
456N/A if (!locals.isEmpty()) {
527N/A for (LocalVariable[] ls : locals) {
527N/A for (LocalVariable l : ls) {
0N/A printLocal(out, l);
0N/A }
0N/A }
0N/A }
0N/A }
0N/A out.write("}\n");
527N/A for (int i = 0; i < v.length - 1; i++) {
0N/A if (v[i] != 1) {
527N/A Constant constant = cp.getConstant(i);
527N/A if (constant != null) {
527N/A full.add(constantToString(constant));
0N/A }
0N/A }
0N/A }
1190N/A }
0N/A
0N/A /**
0N/A * Write a cross referenced HTML file.
0N/A * @param out Writer to write HTML cross-reference
0N/A */
527N/A @Override
1384N/A public void writeXref(XrefWriter out) throws IOException {
527N/A if (xref != null) {
392N/A out.write(xref);
392N/A }
0N/A }
527N/A
0N/A private void printLocal(Writer out, LocalVariable l) throws IOException {
0N/A v[l.getIndex()] = 1;
0N/A v[l.getNameIndex()] = 1;
0N/A v[l.getSignatureIndex()] = 1;
527N/A if (!"this".equals(l.getName())) {
0N/A out.write("\t\t");
0N/A out.write(Utility.signatureToString(l.getSignature()));
0N/A out.write(' ');
0N/A String t;
0N/A out.write(t = l.getName());
0N/A defs.add(t);
0N/A refs.add(t);
0N/A out.write('\n');
0N/A }
0N/A }
527N/A
1470N/A /**
1470N/A * Convert a BCEL constant to a proper string.
1470N/A * @param c constant to convert.
1470N/A * @return the converted string
1470N/A * @throws ClassFormatException
1470N/A */
527N/A public String constantToString(Constant c) throws ClassFormatException {
527N/A String str;
527N/A int i, j;
527N/A byte tag = c.getTag();
527N/A
527N/A switch (tag) {
1470N/A case Constants.CONSTANT_Class:
527N/A i = ((ConstantClass) c).getNameIndex();
527N/A v[i] = 1;
1470N/A Constant con = cp.getConstant(i, Constants.CONSTANT_Utf8);
439N/A str = Utility.compactClassName(((ConstantUtf8) con).getBytes(), false);
0N/A break;
527N/A
1470N/A case Constants.CONSTANT_String:
527N/A i = ((ConstantString) c).getStringIndex();
527N/A v[i] = 1;
1470N/A Constant con2 = cp.getConstant(i, Constants.CONSTANT_Utf8);
527N/A str = ((ConstantUtf8) con2).getBytes();
527N/A break;
527N/A
1470N/A case Constants.CONSTANT_Utf8:
527N/A str = ((ConstantUtf8) c).toString();
527N/A break;
1470N/A case Constants.CONSTANT_Double:
527N/A str = ((ConstantDouble) c).toString();
0N/A break;
1470N/A case Constants.CONSTANT_Float:
527N/A str = ((ConstantFloat) c).toString();
527N/A break;
1470N/A case Constants.CONSTANT_Long:
527N/A str = ((ConstantLong) c).toString();
527N/A break;
1470N/A case Constants.CONSTANT_Integer:
527N/A str = ((ConstantInteger) c).toString();
527N/A break;
527N/A
1470N/A case Constants.CONSTANT_NameAndType:
527N/A i = ((ConstantNameAndType) c).getNameIndex();
527N/A v[i] = 1;
527N/A j = ((ConstantNameAndType) c).getSignatureIndex();
527N/A v[j] = 1;
0N/A String sig = constantToString(cp.getConstant(j));
527N/A if (sig.charAt(0) == '(') {
1470N/A str = Utility.methodSignatureToString(sig,
1470N/A constantToString(cp.getConstant(i)), " ");
0N/A } else {
1470N/A str = Utility.signatureToString(sig) + ' '
1470N/A + constantToString(cp.getConstant(i));
0N/A }
0N/A //str = constantToString(cp.getConstant(i)) +' ' + sig;
527N/A
0N/A break;
527N/A
1470N/A case Constants.CONSTANT_InterfaceMethodref:
1470N/A case Constants.CONSTANT_Methodref:
1470N/A case Constants.CONSTANT_Fieldref:
527N/A i = ((ConstantCP) c).getClassIndex();
527N/A v[i] = 1;
527N/A j = ((ConstantCP) c).getNameAndTypeIndex();
527N/A v[j] = 1;
1470N/A str = (constantToString(cp.getConstant(i)) + " "
1470N/A + constantToString(cp.getConstant(j)));
0N/A break;
527N/A
0N/A default: // Never reached
439N/A throw new ClassFormatException("Unknown constant type " + tag);
0N/A }
0N/A return str;
0N/A }
0N/A}