/*
* Copyright (C) 2011 4th Line GmbH, Switzerland
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.fourthline.lemma.reader.javacode;
import japa.parser.JavaParser;
import japa.parser.ParseException;
import japa.parser.ast.CompilationUnit;
import japa.parser.ast.body.BodyDeclaration;
import japa.parser.ast.body.ClassOrInterfaceDeclaration;
import japa.parser.ast.body.MethodDeclaration;
import japa.parser.ast.body.Parameter;
import japa.parser.ast.visitor.VoidVisitorAdapter;
import org.fourthline.lemma.reader.content.LineRange;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.logging.Logger;
/**
* Finds the "line range" of declarations in a Java source file.
* <p>
* We need this to demarcate raw source code for citation, where a method/nested class begins and where it ends.
* The challenge is matching the Javadoc metadata of a method to this parser's metadata of a method.
* Javadoc has knowledge of the type system (e.g. qualified vs. unqualified parameters of a method
* are detectable) while this parser only has the declared string as-is from the source file and there is
* no way we can qualify/unqualify a method parameter type. So calculating a method signature here
* might have a different result than calculating a method signature in the Javadoc reader.
* </p>
* <p>
* In other words, don't be surprised when the source lines of a method can't be found if you use
* qualified type names on method parameters in your source.
* </p>
* TODO: Revisit this and see if it helps: http://code.google.com/p/javaparser/issues/detail?id=9
*
* @author Christian Bauer
*/
public class LineRangeParser {
private Logger log = Logger.getLogger(LineRangeParser.class.getName());
protected final Map<String, LineRange> methodsLineRange = new HashMap();
protected final Map<String, LineRange> typesLineRange = new HashMap();
public LineRangeParser(final File file) throws IOException, ParseException {
log.fine("Parsing Java source of file: " + file);
CompilationUnit cu = JavaParser.parse(file);
new VoidVisitorAdapter() {
@Override
public void visit(ClassOrInterfaceDeclaration typeDeclaration, Object o) {
String signature = typeDeclaration.getName();
LineRange range = new LineRange(typeDeclaration.getBeginLine(), typeDeclaration.getEndLine());
log.fine("Parsed source of type '" + signature + "', lines: " + range);
LineRangeParser.this.typesLineRange.put(signature, range);
for (BodyDeclaration bodyDeclaration : typeDeclaration.getMembers()) {
bodyDeclaration.accept(this, null);
}
}
@Override
public void visit(MethodDeclaration methodDeclaration, Object arg) {
String signature = getSignature(methodDeclaration);
LineRange range = new LineRange(methodDeclaration.getBeginLine(), methodDeclaration.getEndLine());
log.fine("Parsed source of method '" + signature + "', lines: " + range);
LineRangeParser.this.methodsLineRange.put(signature, range);
}
}.visit(cu, null);
}
public Map<String, LineRange> getMethodsLineRange() {
return methodsLineRange;
}
public Map<String, LineRange> getTypesLineRange() {
return typesLineRange;
}
protected String getSignature(MethodDeclaration methodDeclaration) {
final StringBuilder signature = new StringBuilder();
signature.append(methodDeclaration.getName());
signature.append("(");
if (methodDeclaration.getParameters() != null) {
for (Iterator<Parameter> i = methodDeclaration.getParameters().iterator(); i.hasNext(); ) {
Parameter p = i.next();
signature.append(p.getType().toString());
if (i.hasNext()) {
signature.append(",");
}
}
}
signature.append(")");
return signature.toString();
}
}