/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* See LICENSE.txt included in this distribution for the specific
* language governing permissions and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at LICENSE.txt.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
*/
package org.opensolaris.opengrok.analysis.executables;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.bcel.classfile.Attribute;
import org.apache.bcel.classfile.ClassFormatException;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.Code;
import org.apache.bcel.classfile.Constant;
import org.apache.bcel.classfile.ConstantCP;
import org.apache.bcel.classfile.ConstantClass;
import org.apache.bcel.classfile.ConstantDouble;
import org.apache.bcel.classfile.ConstantFloat;
import org.apache.bcel.classfile.ConstantInteger;
import org.apache.bcel.classfile.ConstantLong;
import org.apache.bcel.classfile.ConstantNameAndType;
import org.apache.bcel.classfile.ConstantPool;
import org.apache.bcel.classfile.ConstantString;
import org.apache.bcel.classfile.ConstantUtf8;
import org.apache.bcel.classfile.ExceptionTable;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.LocalVariable;
import org.apache.bcel.classfile.LocalVariableTable;
import org.apache.bcel.classfile.Utility;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.document.TextField;
import org.opensolaris.opengrok.analysis.FileAnalyzer;
import org.opensolaris.opengrok.analysis.FileAnalyzerFactory;
import org.opensolaris.opengrok.analysis.IteratorReader;
import org.opensolaris.opengrok.analysis.StreamSource;
import org.opensolaris.opengrok.configuration.RuntimeEnvironment;
/**
* Analyzes Java Class files Created on September 23, 2005
*
* @author Chandan
* @author Lubos Kosco , January 2010 , updated bcel, comment on thread safety
*/
public class JavaClassAnalyzer extends FileAnalyzer {
private final String urlPrefix = RuntimeEnvironment.getInstance().getUrlPrefix();
/**
* Creates a new instance of JavaClassAnalyzer
*
* @param factory The factory that creates JavaClassAnalyzers
*/
protected JavaClassAnalyzer(FileAnalyzerFactory factory) {
super(factory);
}
@Override
public void analyze(Document doc, StreamSource src, Writer xrefOut) throws IOException {
try (InputStream in = src.getStream()) {
analyze(doc, in, xrefOut);
}
}
void analyze(Document doc, InputStream in, Writer xrefOut) throws IOException {
List<String> defs = new ArrayList<>();
List<String> refs = new ArrayList<>();
List<String> full = new ArrayList<>();
ClassParser classparser = new ClassParser(in, doc.get("path"));
StringWriter out = new StringWriter();
StringWriter fout = new StringWriter();
getContent(out, fout, classparser.parse(), defs, refs, full);
String fullt = fout.toString();
String xref = out.toString();
if (xrefOut != null) {
xrefOut.append(xref);
try {
xrefOut.flush();
} catch (IOException ex) {
Logger.getLogger(JavaClassAnalyzer.class.getName()).log(Level.WARNING, "Couldn't flush xref, will retry once added to doc", ex);
}
}
xref = null; //flush the xref
StringWriter cout=new StringWriter();
for (String fl : full) {
cout.write(fl);
cout.write('\n');
}
String constants = cout.toString();
StringReader fullout=new StringReader(fullt);
doc.add(new TextField("defs", new IteratorReader(defs)));
doc.add(new TextField("refs", new IteratorReader(refs)));
doc.add(new TextField("full", fullout));
doc.add(new TextField("full", constants, Store.NO));
}
private static final String AHREF="<a href=\"";
private static final String AHREFT_END="\">";
private static final String AHREFEND="</a>";
private static final String ADEFS="defs=";
private static final String APATH="path=";
private static final String AIHREF="\" href=\"";
private static final String ADHREF="<a class=\"d\" name=\"";
private final StringBuffer rstring=new StringBuffer(512);
protected String linkPath(String path) {
rstring.setLength(0);
return rstring.append(AHREF).append(urlPrefix).append(APATH).append(path).append(AHREFT_END).append(path).append(AHREFEND).toString();
}
protected String linkDef(String def) {
rstring.setLength(0);
return rstring.append(AHREF).append(urlPrefix).append(ADEFS).append(def).append(AHREFT_END).append(def).append(AHREFEND).toString();
}
protected String tagDef(String def) {
rstring.setLength(0);
return rstring.append(ADHREF).append(def).append(AIHREF).append(urlPrefix).append(ADEFS).append(def).append(AHREFT_END).append(def).append(AHREFEND).toString();
}
private static final String PACKAGE="package ";
private static final char EOL='\n';
private static final char TAB='\t';
private static final char SPACE=' ';
private static final String EXTENDS=" extends ";
private static final String IMPLEMENTS=" implements ";
private static final String THROWS=" throws ";
private static final String THIS="this";
private static final String LCBREOL=" {\n";
private static final String LBRA=" (";
private static final String COMMA=", ";
private static final String RBRA=") ";
private static final String RCBREOL="}\n";
//TODO this class needs to be thread safe to avoid bug 13364, which was fixed by just updating bcel to 5.2
private void getContent(Writer out, Writer fout, JavaClass c,
List<String> defs, List<String> refs, List<String> full)
throws IOException {
String t;
ConstantPool cp = c.getConstantPool();
int[] v = new int[cp.getLength() + 1];
out.write(linkPath(t = c.getSourceFileName()));
defs.add(t);
refs.add(t);
fout.write(t);
out.write(EOL);
fout.write(EOL);
out.write(PACKAGE);
fout.write(PACKAGE);
out.write(linkDef(t = c.getPackageName()));
defs.add(t);
refs.add(t);
fout.write(t);
out.write(EOL);
fout.write(EOL);
String aflg;
out.write(aflg = Utility.accessToString(c.getAccessFlags(), true));
if (aflg != null) {
out.write(SPACE);
fout.write(aflg);
fout.write(SPACE);
}
v[c.getClassNameIndex()] = 1;
out.write(tagDef(t = c.getClassName()));
defs.add(t);
refs.add(t);
fout.write(t);
out.write(EXTENDS);
fout.write(EXTENDS);
v[c.getSuperclassNameIndex()] = 1;
out.write(linkDef(t = c.getSuperclassName()));
refs.add(t);
fout.write(t);
for (int i : c.getInterfaceIndices()) {
v[i] = 1;
}
String ins[] = c.getInterfaceNames();
if (ins != null && ins.length > 0) {
out.write(IMPLEMENTS);
fout.write(IMPLEMENTS);
for (String in : ins) {
out.write(linkDef(t = in));
refs.add(t);
fout.write(t);
out.write(SPACE);
fout.write(SPACE);
}
}
out.write(LCBREOL);
fout.write(LCBREOL);
for (Attribute a : c.getAttributes()) {
if (a.getTag() == org.apache.bcel.Constants.ATTR_CODE) {
for (Attribute ca : ((Code) a).getAttributes()) {
if (ca.getTag() == org.apache.bcel.Constants.ATTR_LOCAL_VARIABLE_TABLE) {
for (LocalVariable l : ((LocalVariableTable) ca).getLocalVariableTable()) {
printLocal(out, fout, l, v, defs, refs);
}
}
}
} else if (a.getTag() == org.apache.bcel.Constants.ATTR_SOURCE_FILE) {
v[a.getNameIndex()] = 1;
break;
}
}
String aflgs;
String fldsig;
String tdef;
for (org.apache.bcel.classfile.Field fld : c.getFields()) {
out.write(TAB);
fout.write(TAB);
aflgs = Utility.accessToString(fld.getAccessFlags());
if (aflgs != null && aflgs.length() > 0) {
out.write(aflgs);
fout.write(aflgs);
fout.write(SPACE);
out.write(SPACE);
}
fldsig=Utility.signatureToString(fld.getSignature());
out.write(fldsig);
fout.write(fldsig);
out.write(SPACE);
fout.write(SPACE);
tdef=tagDef(t = fld.getName());
out.write(tdef);
fout.write(tdef);
defs.add(t);
refs.add(t);
out.write(EOL);
fout.write(EOL);
//TODO show Attributes
}
String sig;
String msig;
String ltdef;
for (org.apache.bcel.classfile.Method m : c.getMethods()) {
out.write(TAB);
fout.write(TAB);
aflgs = Utility.accessToString(m.getAccessFlags());
if (aflgs != null && aflgs.length() > 0) {
out.write(aflgs);
fout.write(aflgs);
out.write(SPACE);
fout.write(SPACE);
}
sig = m.getSignature();
msig=Utility.methodSignatureReturnType(sig, false);
out.write(msig);
fout.write(msig);
out.write(SPACE);
fout.write(SPACE);
ltdef=tagDef(t = m.getName());
out.write(ltdef);
fout.write(ltdef);
defs.add(t);
refs.add(t);
out.write(LBRA);
fout.write(LBRA);
String[] args = Utility.methodSignatureArgumentTypes(sig, false);
for (int i = 0; i < args.length; i++) {
t = args[i];
out.write(t);
fout.write(t);
int spi = t.indexOf(SPACE);
if (spi > 0) {
refs.add(t.substring(0, spi));
defs.add(t.substring(spi + 1));
}
if (i < args.length - 1) {
out.write(COMMA);
fout.write(COMMA);
}
}
out.write(RBRA);
fout.write(RBRA);
ArrayList<LocalVariable[]> locals = new ArrayList<>();
for (Attribute a : m.getAttributes()) {
if (a.getTag() == org.apache.bcel.Constants.ATTR_EXCEPTIONS) {
for (int i : ((ExceptionTable) a).getExceptionIndexTable()) {
v[i] = 1;
}
String[] exs = ((ExceptionTable) a).getExceptionNames();
if (exs != null && exs.length > 0) {
out.write(THROWS);
fout.write(THROWS);
for (String ex : exs) {
out.write(linkDef(ex));
fout.write(ex);
refs.add(ex);
out.write(SPACE);
fout.write(SPACE);
}
}
} else if (a.getTag() == org.apache.bcel.Constants.ATTR_CODE) {
for (Attribute ca : ((Code) a).getAttributes()) {
if (ca.getTag() == org.apache.bcel.Constants.ATTR_LOCAL_VARIABLE_TABLE) {
locals.add(((LocalVariableTable) ca).getLocalVariableTable());
}
}
}
}
out.write(EOL);
fout.write(EOL);
if (!locals.isEmpty()) {
for (LocalVariable[] ls : locals) {
for (LocalVariable l : ls) {
printLocal(out, fout, l, v, defs, refs);
}
}
}
}
out.write(RCBREOL);
fout.write(RCBREOL);
for (int i = 0; i < v.length - 1; i++) {
if (v[i] != 1) {
Constant constant = cp.getConstant(i);
if (constant != null) {
full.add(constantToString(constant, cp, v));
}
}
}
}
private void printLocal(Writer out, Writer fout, LocalVariable l,
int[] v, List<String> defs, List<String> refs) throws IOException {
v[l.getIndex()] = 1;
v[l.getNameIndex()] = 1;
v[l.getSignatureIndex()] = 1;
if (!THIS.equals(l.getName())) {
out.write(TAB);out.write(TAB);
fout.write(TAB);fout.write(TAB);
String sig=Utility.signatureToString(l.getSignature());
out.write(sig);
fout.write(sig);
out.write(SPACE);
fout.write(SPACE);
String t;
out.write(t = l.getName());
defs.add(t);
refs.add(t);
fout.write(t);
out.write(EOL);
fout.write(EOL);
}
}
public String constantToString(Constant c, ConstantPool cp, int[] v)
throws ClassFormatException {
String str;
int i, j;
byte tag = c.getTag();
switch (tag) {
case org.apache.bcel.Constants.CONSTANT_Class:
i = ((ConstantClass) c).getNameIndex();
v[i] = 1;
Constant con = cp.getConstant(i, org.apache.bcel.Constants.CONSTANT_Utf8);
str = Utility.compactClassName(((ConstantUtf8) con).getBytes(), false);
break;
case org.apache.bcel.Constants.CONSTANT_String:
i = ((ConstantString) c).getStringIndex();
v[i] = 1;
Constant con2 = cp.getConstant(i, org.apache.bcel.Constants.CONSTANT_Utf8);
str = ((ConstantUtf8) con2).getBytes();
break;
case org.apache.bcel.Constants.CONSTANT_Utf8:
str = ((ConstantUtf8) c).toString();
break;
case org.apache.bcel.Constants.CONSTANT_Double:
str = ((ConstantDouble) c).toString();
break;
case org.apache.bcel.Constants.CONSTANT_Float:
str = ((ConstantFloat) c).toString();
break;
case org.apache.bcel.Constants.CONSTANT_Long:
str = ((ConstantLong) c).toString();
break;
case org.apache.bcel.Constants.CONSTANT_Integer:
str = ((ConstantInteger) c).toString();
break;
case org.apache.bcel.Constants.CONSTANT_NameAndType:
i = ((ConstantNameAndType) c).getNameIndex();
v[i] = 1;
j = ((ConstantNameAndType) c).getSignatureIndex();
v[j] = 1;
String sig = constantToString(cp.getConstant(j), cp, v);
if (sig.charAt(0) == '(') {
str = Utility.methodSignatureToString(sig,
constantToString(cp.getConstant(i), cp, v), " ");
} else {
str = Utility.signatureToString(sig) + ' ' +
constantToString(cp.getConstant(i), cp, v);
}
//str = constantToString(cp.getConstant(i)) +' ' + sig;
break;
case org.apache.bcel.Constants.CONSTANT_InterfaceMethodref:
case org.apache.bcel.Constants.CONSTANT_Methodref:
case org.apache.bcel.Constants.CONSTANT_Fieldref:
i = ((ConstantCP) c).getClassIndex();
v[i] = 1;
j = ((ConstantCP) c).getNameAndTypeIndex();
v[j] = 1;
str = (constantToString(cp.getConstant(i), cp, v) + ' ' +
constantToString(cp.getConstant(j), cp, v));
break;
default: // Never reached
throw new ClassFormatException("Unknown constant type " + tag);
}
return str;
}
}