/*
* Copyright (C) 2008 Júlio Vilmar Gesser.
*
* This file is part of Java 1.5 parser and Abstract Syntax Tree.
*
* Java 1.5 parser and Abstract Syntax Tree is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Java 1.5 parser and Abstract Syntax Tree 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Java 1.5 parser and Abstract Syntax Tree. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Created on 05/10/2006
*/
package japa.parser;
import static japa.parser.PositionUtils.areInOrder;
import static japa.parser.PositionUtils.sortByBeginPosition;
import japa.parser.ast.CompilationUnit;
import japa.parser.ast.ImportDeclaration;
import japa.parser.ast.Node;
import japa.parser.ast.body.AnnotableNode;
import japa.parser.ast.body.BodyDeclaration;
import japa.parser.ast.comments.Comment;
import japa.parser.ast.comments.CommentsCollection;
import japa.parser.ast.comments.CommentsParser;
import japa.parser.ast.comments.LineComment;
import japa.parser.ast.expr.AnnotationExpr;
import japa.parser.ast.expr.Expression;
import japa.parser.ast.stmt.BlockStmt;
import japa.parser.ast.stmt.Statement;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
// FIXME this file does not seem to be generated by javacc. Is the doc wrong, or the javacc config?
/**
* <p>
* This class was generated automatically by javacc, do not edit.
* </p>
* <p>
* Parse Java 1.5 source code and creates Abstract Syntax Tree classes.
* </p>
*
* @author Júlio Vilmar Gesser
*/
public final class JavaParser {
private JavaParser() {
// hide the constructor
}
private static boolean _doNotAssignCommentsPreceedingEmptyLines = true;
private static boolean _doNotConsiderAnnotationsAsNodeStartForCodeAttribution = false;
public static boolean getDoNotConsiderAnnotationsAsNodeStartForCodeAttribution()
{
return _doNotConsiderAnnotationsAsNodeStartForCodeAttribution;
}
public static void setDoNotConsiderAnnotationsAsNodeStartForCodeAttribution(boolean doNotConsiderAnnotationsAsNodeStartForCodeAttribution) {
_doNotConsiderAnnotationsAsNodeStartForCodeAttribution = doNotConsiderAnnotationsAsNodeStartForCodeAttribution;
}
public static boolean getDoNotAssignCommentsPreceedingEmptyLines()
{
return _doNotAssignCommentsPreceedingEmptyLines;
}
public static void setDoNotAssignCommentsPreceedingEmptyLines(boolean doNotAssignCommentsPreceedingEmptyLines)
{
_doNotAssignCommentsPreceedingEmptyLines = doNotAssignCommentsPreceedingEmptyLines;
}
public static CompilationUnit parse(final InputStream in,
final String encoding) throws ParseException {
return parse(in,encoding,true);
}
/**
* Parses the Java code contained in the {@link InputStream} and returns a
* {@link CompilationUnit} that represents it.
*
* @param in
* {@link InputStream} containing Java source code
* @param encoding
* encoding of the source code
* @return CompilationUnit representing the Java source code
* @throws ParseException
* if the source code has parser errors
*/
public static CompilationUnit parse(final InputStream in,
final String encoding, boolean considerComments) throws ParseException {
try {
String code = SourcesHelper.streamToString(in, encoding);
InputStream in1 = SourcesHelper.stringToStream(code);
CompilationUnit cu = new ASTParser(in1, encoding).CompilationUnit();
if (considerComments){
insertComments(cu,code);
}
return cu;
} catch (IOException ioe){
throw new ParseException(ioe.getMessage());
}
}
/**
* Parses the Java code contained in the {@link InputStream} and returns a
* {@link CompilationUnit} that represents it.
*
* @param in
* {@link InputStream} containing Java source code
* @return CompilationUnit representing the Java source code
* @throws ParseException
* if the source code has parser errors
*/
public static CompilationUnit parse(final InputStream in)
throws ParseException {
return parse(in, null,true);
}
public static CompilationUnit parse(final File file, final String encoding)
throws ParseException, IOException {
return parse(file,encoding,true);
}
/**
* Parses the Java code contained in a {@link File} and returns a
* {@link CompilationUnit} that represents it.
*
* @param file
* {@link File} containing Java source code
* @param encoding
* encoding of the source code
* @return CompilationUnit representing the Java source code
* @throws ParseException
* if the source code has parser errors
* @throws IOException
*/
public static CompilationUnit parse(final File file, final String encoding, boolean considerComments)
throws ParseException, IOException {
final FileInputStream in = new FileInputStream(file);
try {
return parse(in, encoding, considerComments);
} finally {
in.close();
}
}
/**
* Parses the Java code contained in a {@link File} and returns a
* {@link CompilationUnit} that represents it.
*
* @param file
* {@link File} containing Java source code
* @return CompilationUnit representing the Java source code
* @throws ParseException
* if the source code has parser errors
* @throws IOException
*/
public static CompilationUnit parse(final File file) throws ParseException,
IOException {
return parse(file, null,true);
}
public static CompilationUnit parse(final Reader reader, boolean considerComments)
throws ParseException {
try {
String code = SourcesHelper.readerToString(reader);
Reader reader1 = SourcesHelper.stringToReader(code);
CompilationUnit cu = new ASTParser(reader1).CompilationUnit();
if (considerComments){
insertComments(cu,code);
}
return cu;
} catch (IOException ioe){
throw new ParseException(ioe.getMessage());
}
}
/**
* Parses the Java block contained in a {@link String} and returns a
* {@link BlockStmt} that represents it.
*
* @param blockStatement
* {@link String} containing Java block code
* @return BlockStmt representing the Java block
* @throws ParseException
* if the source code has parser errors
* @throws IOException
*/
public static BlockStmt parseBlock(final String blockStatement)
throws ParseException {
StringReader sr = new StringReader(blockStatement);
BlockStmt result = new ASTParser(sr).Block();
sr.close();
return result;
}
/**
* Parses the Java statement contained in a {@link String} and returns a
* {@link Statement} that represents it.
*
* @param statement
* {@link String} containing Java statement code
* @return Statement representing the Java statement
* @throws ParseException
* if the source code has parser errors
* @throws IOException
*/
public static Statement parseStatement(final String statement)
throws ParseException {
StringReader sr = new StringReader(statement);
Statement stmt = new ASTParser(sr).Statement();
sr.close();
return stmt;
}
/**
* Parses the Java import contained in a {@link String} and returns a
* {@link ImportDeclaration} that represents it.
*
* @param importDeclaration
* {@link String} containing Java import code
* @return ImportDeclaration representing the Java import declaration
* @throws ParseException
* if the source code has parser errors
* @throws IOException
*/
public static ImportDeclaration parseImport(final String importDeclaration)
throws ParseException {
StringReader sr = new StringReader(importDeclaration);
ImportDeclaration id = new ASTParser(sr).ImportDeclaration();
sr.close();
return id;
}
/**
* Parses the Java expression contained in a {@link String} and returns a
* {@link Expression} that represents it.
*
* @param expression
* {@link String} containing Java expression
* @return Expression representing the Java expression
* @throws ParseException
* if the source code has parser errors
* @throws IOException
*/
public static Expression parseExpression(final String expression)
throws ParseException {
StringReader sr = new StringReader(expression);
Expression e = new ASTParser(sr).Expression();
sr.close();
return e;
}
/**
* Parses the Java annotation contained in a {@link String} and returns a
* {@link AnnotationExpr} that represents it.
*
* @param annotation
* {@link String} containing Java annotation
* @return AnnotationExpr representing the Java annotation
* @throws ParseException
* if the source code has parser errors
* @throws IOException
*/
public static AnnotationExpr parseAnnotation(final String annotation)
throws ParseException {
StringReader sr = new StringReader(annotation);
AnnotationExpr ae = new ASTParser(sr).Annotation();
sr.close();
return ae;
}
/**
* Parses the Java body declaration(e.g fields or methods) contained in a
* {@link String} and returns a {@link BodyDeclaration} that represents it.
*
* @param body
* {@link String} containing Java body declaration
* @return BodyDeclaration representing the Java annotation
* @throws ParseException
* if the source code has parser errors
* @throws IOException
*/
public static BodyDeclaration parseBodyDeclaration(final String body)
throws ParseException {
StringReader sr = new StringReader(body);
BodyDeclaration bd = new ASTParser(sr).AnnotationBodyDeclaration();
sr.close();
return bd;
}
/**
* Comments are attributed to the thing the comment and are removed from
* allComments.
*/
private static void insertCommentsInCu(CompilationUnit cu, CommentsCollection commentsCollection){
if (commentsCollection.size()==0) return;
// I should sort all the direct children and the comments, if a comment is the first thing then it
// a comment to the CompilationUnit
// FIXME if there is no package it could be also a comment to the following class...
// so I could use some heuristics in these cases to distinguish the two cases
List<Comment> comments = commentsCollection.getAll();
sortByBeginPosition(comments);
List<Node> children = cu.getChildrenNodes();
sortByBeginPosition(children);
if (cu.getPackage()!=null && (children.size()==0 || areInOrder(comments.get(0), children.get(0)))){
cu.setComment(comments.get(0));
comments.remove(0);
}
insertCommentsInNode(cu,comments);
}
private static boolean attributeLineCommentToNodeOrChild(Node node, LineComment lineComment)
{
// The node start and end at the same line as the comment,
// let's give to it the comment
if (node.getBeginLine()==lineComment.getBeginLine() && !node.hasComment())
{
node.setComment(lineComment);
return true;
} else {
// try with all the children, sorted by reverse position (so the
// first one is the nearest to the comment
List<Node> children = new LinkedList<Node>();
children.addAll(node.getChildrenNodes());
PositionUtils.sortByBeginPosition(children);
Collections.reverse(children);
for (Node child : children)
{
if (attributeLineCommentToNodeOrChild(child, lineComment))
{
return true;
}
}
return false;
}
}
/**
* This method try to attributes the nodes received to child of the node.
* It returns the node that were not attributed.
*/
private static void insertCommentsInNode(Node node, List<Comment> commentsToAttribute){
if (commentsToAttribute.size()==0) return;
// the comments can:
// 1) Inside one of the child, then it is the child that have to associate them
// 2) If they are not inside a child they could be preceeding nothing, a comment or a child
// if they preceed a child they are assigned to it, otherweise they remain "orphans"
List<Node> children = node.getChildrenNodes();
sortByBeginPosition(children);
for (Node child : children){
List<Comment> commentsInsideChild = new LinkedList<Comment>();
for (Comment c : commentsToAttribute){
if (PositionUtils.nodeContains(child, c, _doNotConsiderAnnotationsAsNodeStartForCodeAttribution)){
commentsInsideChild.add(c);
}
}
commentsToAttribute.removeAll(commentsInsideChild);
insertCommentsInNode(child,commentsInsideChild);
}
// I can attribute in line comments to elements preceeding them, if there
// is something contained in their line
List<Comment> attributedComments = new LinkedList<Comment>();
for (Comment comment : commentsToAttribute)
{
if (comment.isLineComment())
{
for (Node child : children)
{
if (child.getEndLine()==comment.getBeginLine())
{
if (attributeLineCommentToNodeOrChild(child, comment.asLineComment()))
{
attributedComments.add(comment);
}
}
}
}
}
// at this point I create an ordered list of all remaining comments and children
Comment previousComment = null;
attributedComments = new LinkedList<Comment>();
List<Node> childrenAndComments = new LinkedList<Node>();
childrenAndComments.addAll(children);
childrenAndComments.addAll(commentsToAttribute);
sortByBeginPosition(childrenAndComments, _doNotConsiderAnnotationsAsNodeStartForCodeAttribution);
for (Node thing : childrenAndComments){
if (thing instanceof Comment){
previousComment = (Comment)thing;
if (!previousComment.isOrphan())
{
previousComment = null;
}
} else {
if (previousComment != null && !thing.hasComment()){
if (!_doNotAssignCommentsPreceedingEmptyLines || !thereAreLinesBetween(previousComment, thing)) {
thing.setComment(previousComment);
attributedComments.add(previousComment);
previousComment = null;
}
}
}
}
commentsToAttribute.removeAll(attributedComments);
// all the remaining are orphan nodes
for (Comment c : commentsToAttribute){
if (c.isOrphan()) {
node.addOrphanComment(c);
}
}
}
private static boolean thereAreLinesBetween(Node a, Node b)
{
if (!PositionUtils.areInOrder(a, b))
{
return thereAreLinesBetween(b, a);
}
int endOfA = a.getEndLine();
return b.getBeginLine()>(a.getEndLine()+1);
}
private static void insertComments(CompilationUnit cu, String code) throws IOException {
CommentsParser commentsParser = new CommentsParser();
CommentsCollection allComments = commentsParser.parse(code);
insertCommentsInCu(cu,allComments);
}
}