/*
* Copyright 2012 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Eclipse Public License version 1.0, available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.jboss.forge.roaster.spi;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.compiler.CategorizedProblem;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.EnumDeclaration;
import org.eclipse.jdt.core.dom.PackageDeclaration;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
import org.eclipse.jdt.internal.core.util.CodeSnippetParsingUtil;
import org.eclipse.jface.text.Document;
import org.jboss.forge.roaster.ParserException;
import org.jboss.forge.roaster.Problem;
import org.jboss.forge.roaster.model.JavaType;
import org.jboss.forge.roaster.model.JavaUnit;
import org.jboss.forge.roaster.model.ast.TypeDeclarationFinderVisitor;
import org.jboss.forge.roaster.model.impl.JavaAnnotationImpl;
import org.jboss.forge.roaster.model.impl.JavaClassImpl;
import org.jboss.forge.roaster.model.impl.JavaEnumImpl;
import org.jboss.forge.roaster.model.impl.JavaInterfaceImpl;
import org.jboss.forge.roaster.model.impl.JavaPackageInfoImpl;
import org.jboss.forge.roaster.model.impl.JavaUnitImpl;
import org.jboss.forge.roaster.model.source.JavaAnnotationSource;
import org.jboss.forge.roaster.model.source.JavaClassSource;
import org.jboss.forge.roaster.model.source.JavaEnumSource;
import org.jboss.forge.roaster.model.source.JavaInterfaceSource;
import org.jboss.forge.roaster.model.source.JavaPackageInfoSource;
import org.jboss.forge.roaster.model.source.JavaSource;
/**
* @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a>
*/
public class JavaParserImpl implements JavaParser
{
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public JavaUnit parseUnit(final String data)
{
Document document = new Document(data);
ASTParser parser = ASTParser.newParser(AST.JLS8);
parser.setSource(document.get().toCharArray());
Map options = JavaCore.getOptions();
options.put(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_1_8);
options.put(JavaCore.CORE_ENCODING, "UTF-8");
parser.setCompilerOptions(options);
parser.setResolveBindings(true);
parser.setKind(ASTParser.K_COMPILATION_UNIT);
CompilationUnit unit = (CompilationUnit) parser.createAST(null);
unit.recordModifications();
TypeDeclarationFinderVisitor visitor = new TypeDeclarationFinderVisitor();
unit.accept(visitor);
List<AbstractTypeDeclaration> declarations = visitor.getTypeDeclarations();
List<JavaType<?>> types = new ArrayList<JavaType<?>>();
if (!declarations.isEmpty())
{
for (AbstractTypeDeclaration declaration : declarations)
{
if (declaration.isPackageMemberTypeDeclaration())
{
types.add(getJavaSource(null, document, unit, declaration));
}
}
return new JavaUnitImpl(types);
}
else if (visitor.getPackageDeclaration() != null)
{
types.add(getJavaSource(null, document, unit, visitor.getPackageDeclaration()));
return new JavaUnitImpl(types);
}
else
{
throw new ParserException("Could not find type declaration in Java source - is this actually code?");
}
}
/**
* Create a {@link JavaType} instance from the given {@link Document}, {@link CompilationUnit},
* {@link TypeDeclaration}, and enclosing {@link JavaType} type.
*/
public static JavaSource<?> getJavaSource(JavaSource<?> enclosingType, Document document, CompilationUnit unit,
ASTNode declaration)
{
if (declaration instanceof TypeDeclaration)
{
TypeDeclaration typeDeclaration = (TypeDeclaration) declaration;
if (typeDeclaration.isInterface())
{
return new JavaInterfaceImpl(enclosingType, document, unit, typeDeclaration);
}
else
{
return new JavaClassImpl(enclosingType, document, unit, typeDeclaration);
}
}
else if (declaration instanceof EnumDeclaration)
{
EnumDeclaration enumDeclaration = (EnumDeclaration) declaration;
return new JavaEnumImpl(enclosingType, document, unit, enumDeclaration);
}
else if (declaration instanceof AnnotationTypeDeclaration)
{
AnnotationTypeDeclaration annotationTypeDeclaration = (AnnotationTypeDeclaration) declaration;
return new JavaAnnotationImpl(enclosingType, document, unit, annotationTypeDeclaration);
}
else if (declaration instanceof PackageDeclaration)
{
PackageDeclaration packageDeclaration = (PackageDeclaration) declaration;
return new JavaPackageInfoImpl(enclosingType, document, unit, packageDeclaration);
}
else
{
throw new ParserException("Unknown Java source type [" + declaration + "]");
}
}
@Override
@SuppressWarnings("unchecked")
public <T extends JavaSource<?>> T create(final Class<T> type)
{
if (type != null)
{
if (type.isAssignableFrom(JavaClassSource.class))
return (T) parseUnit("public class JavaClass { }").getGoverningType();
if (type.isAssignableFrom(JavaEnumSource.class))
return (T) parseUnit("public enum JavaEnum { }").getGoverningType();
if (type.isAssignableFrom(JavaAnnotationSource.class))
return (T) parseUnit("public @interface JavaAnnotation { }").getGoverningType();
if (type.isAssignableFrom(JavaInterfaceSource.class))
return (T) parseUnit("public interface JavaInterface { }").getGoverningType();
if (type.isAssignableFrom(JavaPackageInfoSource.class))
return (T) parseUnit("package org.example;").getGoverningType();
}
return null;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public List<Problem> validateSnippet(String snippet)
{
Hashtable options = JavaCore.getOptions();
options.put(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_1_8);
options.put(JavaCore.CORE_ENCODING, "UTF-8");
CodeSnippetParsingUtil codeSnippetParsingUtil = new CodeSnippetParsingUtil(false);
ConstructorDeclaration constructorDeclaration = codeSnippetParsingUtil.parseStatements(snippet.toCharArray(), 0,
snippet.length(), options, true, false);
CompilationResult compilationResult = constructorDeclaration.compilationResult();
List<Problem> problems = new ArrayList<Problem>();
if (compilationResult.hasErrors())
{
for (CategorizedProblem problem : compilationResult.getErrors())
{
Problem p = new Problem(problem.getMessage(),
problem.getSourceStart(),
problem.getSourceEnd(),
problem.getSourceLineNumber());
problems.add(p);
}
}
return problems;
}
}