/*
* ModeShape (http://www.modeshape.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.modeshape.sequencer.javafile;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import javax.jcr.Node;
import javax.jcr.Value;
import javax.jcr.ValueFactory;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
import org.eclipse.jdt.core.dom.AnnotationTypeMemberDeclaration;
import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
import org.eclipse.jdt.core.dom.ArrayType;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.BodyDeclaration;
import org.eclipse.jdt.core.dom.BooleanLiteral;
import org.eclipse.jdt.core.dom.CharacterLiteral;
import org.eclipse.jdt.core.dom.Comment;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
import org.eclipse.jdt.core.dom.EnumDeclaration;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.IExtendedModifier;
import org.eclipse.jdt.core.dom.ImportDeclaration;
import org.eclipse.jdt.core.dom.Initializer;
import org.eclipse.jdt.core.dom.Javadoc;
import org.eclipse.jdt.core.dom.MemberValuePair;
import org.eclipse.jdt.core.dom.Message;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.PackageDeclaration;
import org.eclipse.jdt.core.dom.ParameterizedType;
import org.eclipse.jdt.core.dom.PrimitiveType;
import org.eclipse.jdt.core.dom.QualifiedType;
import org.eclipse.jdt.core.dom.SimpleType;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.Statement;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.TypeParameter;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.WildcardType;
import org.modeshape.common.logging.Logger;
import org.modeshape.common.util.StringUtil;
import org.modeshape.jcr.api.sequencer.Sequencer;
import org.modeshape.jcr.api.sequencer.Sequencer.Context;
import org.modeshape.sequencer.classfile.ClassFileSequencerLexicon;
import org.modeshape.sequencer.classfile.metadata.Visibility;
/**
* An Eclipse JDT DOM to JCR node recorder.
*
* <pre>
* CompilationUnit:
* [ PackageDeclaration ]
* { ImportDeclaration }
* { TypeDeclaration | EnumDeclaration | AnnotationTypeDeclaration | ; }
*
* PackageDeclaration:
* [ Javadoc ] { Annotation } package Name ;
*
* ImportDeclaration:
* import [ static ] Name [ . * ] ;
*
* TypeDeclaration:
* ClassDeclaration
* InterfaceDeclaration
*
* ClassDeclaration:
* [ Javadoc ] { ExtendedModifier } class Identifier
* [ < TypeParameter { , TypeParameter } > ]
* [ extends Type ]
* [ implements Type { , Type } ]
* { { ClassBodyDeclaration | ; } }
*
* InterfaceDeclaration:
* [ Javadoc ] { ExtendedModifier } interface Identifier
* [ < TypeParameter { , TypeParameter } > ]
* [ extends Type { , Type } ]
* { { InterfaceBodyDeclaration | ; } }
* </pre>
*/
public class JdtRecorder implements SourceFileRecorder {
private static final Logger LOGGER = Logger.getLogger(JdtRecorder.class);
private CompilationUnit compilationUnit;
private Sequencer.Context context;
private String sourceCode;
protected String getSourceCode( final int startPosition,
final int length ) {
if (StringUtil.isBlank(this.sourceCode)) {
return null;
}
return this.sourceCode.substring(startPosition, (startPosition + length));
}
protected String getTypeName( final Type type ) {
if (type.isPrimitiveType()) {
final PrimitiveType primitiveType = (PrimitiveType)type;
return primitiveType.getPrimitiveTypeCode().toString();
}
if (type.isSimpleType()) {
final SimpleType simpleType = (SimpleType)type;
return simpleType.getName().getFullyQualifiedName();
}
if (type.isQualifiedType()) {
final QualifiedType qualifiedType = (QualifiedType)type;
return qualifiedType.getName().getFullyQualifiedName();
}
if (type.isParameterizedType()) {
final ParameterizedType parameterizedType = (ParameterizedType)type;
final StringBuilder result = new StringBuilder(getTypeName(parameterizedType.getType()));
result.append('<');
if (!parameterizedType.typeArguments().isEmpty()) {
@SuppressWarnings( "unchecked" )
final List<ParameterizedType> paramTypes = parameterizedType.typeArguments();
boolean firstTime = true;
for (final Type paramType : paramTypes) {
if (firstTime) {
firstTime = false;
} else {
result.append(", ");
}
result.append(getTypeName(paramType));
}
}
result.append('>');
return result.toString();
}
if (type.isArrayType()) {
final ArrayType arrayType = (ArrayType)type;
final Type elementType = arrayType.getElementType(); // the element type is never an array type
if (elementType.isPrimitiveType()) {
return ((PrimitiveType)elementType).getPrimitiveTypeCode().toString();
}
// can't be an array type
if (elementType.isSimpleType()) {
return ((SimpleType)elementType).getName().getFullyQualifiedName();
}
}
if (type.isWildcardType()) {
return "?";
}
return null;
}
protected String getVisibility( final int modifiers ) {
if ((modifiers & Modifier.PUBLIC) != 0) {
return Visibility.PUBLIC.getDescription();
}
if ((modifiers & Modifier.PROTECTED) != 0) {
return Visibility.PROTECTED.getDescription();
}
if ((modifiers & Modifier.PRIVATE) != 0) {
return Visibility.PRIVATE.getDescription();
}
return Visibility.PACKAGE.getDescription();
}
/**
* <pre>
* Annotation:
* NormalAnnotation
* MarkerAnnotation
* SingleMemberAnnotation
*
* NormalAnnotation:
* \@ TypeName ( [ MemberValuePair { , MemberValuePair } ] )
*
* MarkerAnnotation:
* \@ TypeName
*
* SingleMemberAnnotation:
* \@ TypeName ( Expression )
*
* MemberValuePair:
* SimpleName = Expression
*
* </pre>
*
* @param annotation the {@link Annotation annotation} being recorded (cannot be <code>null</code>)
* @param parentNode the parent {@link Node node} where the annotation will be created (cannot be <code>null</code>)
* @throws Exception if there is a problem
*/
protected void record( final Annotation annotation,
final Node parentNode ) throws Exception {
final String name = annotation.getTypeName().getFullyQualifiedName();
final Node annotationNode = parentNode.addNode(name, ClassFileSequencerLexicon.ANNOTATION);
annotationNode.setProperty(ClassFileSequencerLexicon.NAME, name);
if (annotation.isMarkerAnnotation()) {
annotationNode.setProperty(ClassFileSequencerLexicon.ANNOTATION_TYPE,
ClassFileSequencerLexicon.AnnotationType.MARKER.toString());
LOGGER.debug("Marker annotation {0} created", name);
} else if (annotation.isNormalAnnotation()) {
annotationNode.setProperty(ClassFileSequencerLexicon.ANNOTATION_TYPE,
ClassFileSequencerLexicon.AnnotationType.NORMAL.toString());
@SuppressWarnings( "unchecked" )
final List<MemberValuePair> entries = ((NormalAnnotation)annotation).values();
if ((entries != null) && !entries.isEmpty()) {
for (final MemberValuePair entry : entries) {
final String memberName = entry.getName().getFullyQualifiedName();
final Expression expression = entry.getValue();
recordAnnotationMember(memberName, expression, annotationNode);
}
}
LOGGER.debug("Normal annotation {0} created", name);
} else if (annotation.isSingleMemberAnnotation()) {
annotationNode.setProperty(ClassFileSequencerLexicon.ANNOTATION_TYPE,
ClassFileSequencerLexicon.AnnotationType.SINGLE_MEMBER.toString());
final Expression expression = ((SingleMemberAnnotation)annotation).getValue();
recordAnnotationMember(null, expression, annotationNode);
LOGGER.debug("Single member annotation {0} created", name);
} else {
assert false;
LOGGER.error(JavaFileI18n.unhandledAnnotationType, annotation.getClass().getName(), parentNode.getName());
}
recordSourceReference(annotation, annotationNode);
}
/**
* <pre>
* AnnotationTypeDeclaration:
* [ Javadoc ] { ExtendedModifier } @ interface Identifier
* { { AnnotationTypeBodyDeclaration | ; } }
*
* AnnotationTypeBodyDeclaration:
* AnnotationTypeMemberDeclaration
* FieldDeclaration
* TypeDeclaration
* EnumDeclaration
* AnnotationTypeDeclaration
*
* AnnotationTypeMemberDeclaration:
* [ Javadoc ] { ExtendedModifier }
* Type Identifier ( ) [ default Expression ] ;
* </pre>
*
* @param annotationType the {@link AnnotationTypeDeclaration annotation type} being recorded (cannot be <code>null</code>)
* @param outputNode the output node where the annotation type should be created (cannot be <code>null</code>)
* @return the node representing the annotation type (never <code>null</code>)
* @throws Exception if there is a problem
*/
protected Node record( final AnnotationTypeDeclaration annotationType,
final Node outputNode ) throws Exception {
final String name = annotationType.getName().getFullyQualifiedName();
final Node annotationTypeNode = outputNode.addNode(name, ClassFileSequencerLexicon.ANNOTATION_TYPE);
annotationTypeNode.setProperty(ClassFileSequencerLexicon.NAME, name);
annotationTypeNode.setProperty(ClassFileSequencerLexicon.VISIBILITY, getVisibility(annotationType.getModifiers()));
annotationTypeNode.setProperty(ClassFileSequencerLexicon.SEQUENCED_DATE, this.context.getTimestamp());
{ // javadocs
final Javadoc javadoc = annotationType.getJavadoc();
if (javadoc != null) {
record(javadoc, annotationTypeNode);
}
}
{ // annotations
@SuppressWarnings( "unchecked" )
final List<IExtendedModifier> modifiers = annotationType.modifiers();
recordAnnotations(modifiers, annotationTypeNode);
}
{ // body
@SuppressWarnings( "unchecked" )
final List<BodyDeclaration> body = annotationType.bodyDeclarations();
if ((body != null) && !body.isEmpty()) {
Node fieldsNode = null;
Node membersNode = null;
Node nestedTypesNode = null;
for (final BodyDeclaration declaration : body) {
if (declaration instanceof AnnotationTypeMemberDeclaration) {
if (membersNode == null) {
membersNode = annotationTypeNode.addNode(ClassFileSequencerLexicon.ANNOTATION_TYPE_MEMBERS,
ClassFileSequencerLexicon.ANNOTATION_TYPE_MEMBERS);
}
record((AnnotationTypeMemberDeclaration)declaration, membersNode);
} else if (declaration instanceof FieldDeclaration) {
if (fieldsNode == null) {
fieldsNode = annotationTypeNode.addNode(ClassFileSequencerLexicon.FIELDS,
ClassFileSequencerLexicon.FIELDS);
}
record((FieldDeclaration)declaration, fieldsNode);
} else if (declaration instanceof AbstractTypeDeclaration) {
if (nestedTypesNode == null) {
nestedTypesNode = annotationTypeNode.addNode(ClassFileSequencerLexicon.NESTED_TYPES,
ClassFileSequencerLexicon.NESTED_TYPES);
}
if (declaration instanceof TypeDeclaration) {
record((TypeDeclaration)declaration, nestedTypesNode);
} else if (declaration instanceof EnumDeclaration) {
record((EnumDeclaration)declaration, nestedTypesNode);
} else if (declaration instanceof AnnotationTypeDeclaration) {
record((AnnotationTypeDeclaration)declaration, nestedTypesNode);
} else {
assert false;
LOGGER.error(JavaFileI18n.unhandledAnnotationTypeBodyDeclarationType,
declaration.getClass().getName(),
annotationType.getName());
}
} else {
assert false;
LOGGER.error(JavaFileI18n.unhandledAnnotationTypeBodyDeclarationType,
declaration.getClass().getName(),
annotationType.getName());
}
}
}
}
recordSourceReference(annotationType, annotationTypeNode);
return annotationTypeNode;
}
/**
* <pre>
* AnnotationTypeMemberDeclaration:
* [ Javadoc ] { ExtendedModifier }
* Type Identifier ( ) [ default Expression ] ;
* </pre>
*
* @param annotationTypeMember the {@link AnnotationTypeMemberDeclaration annotation type member} being recorded (cannot be
* <code>null</code>)
* @param parentNode the parent {@link Node node} where the annotation type members will be added (cannot be <code>null</code>
* )
* @throws Exception if there is a problem
*/
protected void record( final AnnotationTypeMemberDeclaration annotationTypeMember,
final Node parentNode ) throws Exception {
final Node memberNode = parentNode.addNode(ClassFileSequencerLexicon.ANNOTATION_TYPE_MEMBER,
ClassFileSequencerLexicon.ANNOTATION_TYPE_MEMBER);
memberNode.setProperty(ClassFileSequencerLexicon.NAME, annotationTypeMember.getName().getFullyQualifiedName());
{ // modifiers
final int modifiers = annotationTypeMember.getModifiers();
memberNode.setProperty(ClassFileSequencerLexicon.ABSTRACT, (modifiers & Modifier.ABSTRACT) != 0);
memberNode.setProperty(ClassFileSequencerLexicon.VISIBILITY, getVisibility(modifiers));
}
{ // javadocs
final Javadoc javadoc = annotationTypeMember.getJavadoc();
if (javadoc != null) {
record(javadoc, memberNode);
}
}
{ // annotations
@SuppressWarnings( "unchecked" )
final List<IExtendedModifier> modifiers = annotationTypeMember.modifiers();
recordAnnotations(modifiers, memberNode);
}
{ // type
final Type type = annotationTypeMember.getType();
record(type, ClassFileSequencerLexicon.TYPE, memberNode);
}
{ // default expression
final Expression expression = annotationTypeMember.getDefault();
if (expression != null) {
recordExpression(expression, ClassFileSequencerLexicon.DEFAULT, memberNode);
}
}
}
/**
* <pre>
* Block:
* { { Statement } }
* </pre>
*
* @param block the {@link Block block} being recorded (cannot be <code>null</code>)
* @param blockNode the parent {@link Node node} where the statements will be added (cannot be <code>null</code>)
* @throws Exception if there is a problem
*/
protected void record( final Block block,
final Node blockNode ) throws Exception {
if (block != null) {
@SuppressWarnings( "unchecked" )
final List<Statement> statements = block.statements();
if ((statements != null) && !statements.isEmpty()) {
for (final Statement statement : statements) {
// TODO handle each type of statement
final Node stmtNode = blockNode.addNode(ClassFileSequencerLexicon.STATEMENT,
ClassFileSequencerLexicon.STATEMENT);
stmtNode.setProperty(ClassFileSequencerLexicon.CONTENT, statement.toString());
recordSourceReference(statement, stmtNode);
}
}
}
}
/**
* <pre>
* Comment:
* LineComment
* BlockComment
* Javadoc
* </pre>
*
* @param comment the comment being recorded (cannot be <code>null</code>)
* @param parentNode the {@link Node parent node} (cannot be <code>null</code>)
* @throws Exception if there is a problem
*/
protected void record( final Comment comment,
final Node parentNode ) throws Exception {
Node commentNode = null;
String commentType = null;
if (comment.isDocComment()) {
commentNode = parentNode.addNode(ClassFileSequencerLexicon.JAVADOC, ClassFileSequencerLexicon.JAVADOC);
} else {
commentNode = parentNode.addNode(ClassFileSequencerLexicon.COMMENT, ClassFileSequencerLexicon.COMMENT);
if (comment.isBlockComment()) {
commentType = ClassFileSequencerLexicon.CommentType.BLOCK.toString();
} else if (comment.isLineComment()) {
commentType = ClassFileSequencerLexicon.CommentType.LINE.toString();
} else {
assert false;
LOGGER.error(JavaFileI18n.unhandledCommentType, comment.getClass().getName());
}
commentNode.setProperty(ClassFileSequencerLexicon.COMMENT_TYPE, commentType);
}
final String code = getSourceCode(comment.getStartPosition(), comment.getLength());
if (!StringUtil.isBlank(code)) {
commentNode.setProperty(ClassFileSequencerLexicon.COMMENT, code);
}
recordSourceReference(comment, commentNode);
}
/**
* <pre>
* CompilationUnit:
* [ PackageDeclaration ]
* { ImportDeclaration }
* { TypeDeclaration | EnumDeclaration | AnnotationTypeDeclaration | ; }
* </pre>
*
* @param unit the {@link CompilationUnit compilation unit} being recorded (cannot be <code>null</code>)
* @param compilationUnitNode the output node associated with the compilation unit (cannot be <code>null</code>)
* @throws Exception if there is a problem
*/
protected void record( final CompilationUnit unit,
final Node compilationUnitNode ) throws Exception {
LOGGER.debug("recording unit comments");
recordComments(unit, compilationUnitNode);
LOGGER.debug("recording unit import nodes");
recordImports(unit, compilationUnitNode);
LOGGER.debug("recording unit compiler messages");
recordCompilerMessages(unit, compilationUnitNode);
LOGGER.debug("recording unit package nodes");
final Node pkgNode = recordPackage(unit, compilationUnitNode);
LOGGER.debug("recording unit type nodes");
recordTypes(unit, compilationUnitNode, pkgNode);
LOGGER.debug("recording unit source reference");
recordSourceReference(unit, compilationUnitNode);
compilationUnitNode.setProperty(ClassFileSequencerLexicon.SEQUENCED_DATE, this.context.getTimestamp());
}
/**
* {@inheritDoc}
*
* @see org.modeshape.sequencer.javafile.SourceFileRecorder#record(org.modeshape.jcr.api.sequencer.Sequencer.Context,
* java.io.InputStream, long, java.lang.String, javax.jcr.Node)
*/
@Override
public void record( final Context context,
final InputStream inputStream,
final long length,
final String encoding,
final Node outputNode ) throws Exception {
final char[] sourceCode = JavaMetadataUtil.getJavaSourceFromTheInputStream(inputStream, length, encoding);
record(context, sourceCode, outputNode);
}
/**
* <pre>
* EnumConstantDeclaration:
* [ Javadoc ] { ExtendedModifier } Identifier
* [ ( [ Expression { , Expression } ] ) ]
* [ AnonymousClassDeclaration ]
* </pre>
*
* @param enumConstant the enum constant being processed (cannot be <code>null</code>)
* @param parentNode the {@link Node node} where the enum constant node will be created (cannot be <code>null</code>)
* @throws Exception if there is a problem
*/
protected void record( final EnumConstantDeclaration enumConstant,
final Node parentNode ) throws Exception {
final String name = enumConstant.getName().getIdentifier();
final Node constantNode = parentNode.addNode(name, ClassFileSequencerLexicon.ENUM_CONSTANT);
{ // javadocs
final Javadoc javadoc = enumConstant.getJavadoc();
if (javadoc != null) {
record(javadoc, constantNode);
}
}
{ // no modifiers but can have annotations
@SuppressWarnings( "unchecked" )
final List<IExtendedModifier> modifiers = enumConstant.modifiers();
recordAnnotations(modifiers, constantNode);
}
{ // args
@SuppressWarnings( "unchecked" )
final List<Expression> args = enumConstant.arguments();
if ((args != null) && !args.isEmpty()) {
final Node containerNode = constantNode.addNode(ClassFileSequencerLexicon.ARGUMENTS,
ClassFileSequencerLexicon.ARGUMENTS);
for (final Expression arg : args) {
recordExpression(arg, ClassFileSequencerLexicon.ARGUMENT, containerNode);
}
}
}
// anonymous classes
final AnonymousClassDeclaration acd = enumConstant.getAnonymousClassDeclaration();
if (acd != null) {
recordBodyDeclarations(acd, constantNode);
}
recordSourceReference(enumConstant, constantNode);
}
/**
* <pre>
* EnumDeclaration:
* [ Javadoc ] { ExtendedModifier } enum Identifier
* [ implements Type { , Type } ]
* {
* [ EnumConstantDeclaration { , EnumConstantDeclaration } ] [ , ]
* [ ; { ClassBodyDeclaration | ; } ]
* }
* </pre>
*
* @param enumType the {@link EnumDeclaration enum} being recorded (cannot be <code>null</code>)
* @param parentNode the parent {@link Node node} (cannot be <code>null</code>)
* @return the node representing the enum being recorded (never <code>null</code>)
* @throws Exception if there is a problem
*/
protected Node record( final EnumDeclaration enumType,
final Node parentNode ) throws Exception {
final String name = enumType.getName().getFullyQualifiedName();
final Node enumNode = parentNode.addNode(name, ClassFileSequencerLexicon.ENUM);
enumNode.setProperty(ClassFileSequencerLexicon.NAME, name);
enumNode.setProperty(ClassFileSequencerLexicon.SEQUENCED_DATE, this.context.getTimestamp());
enumNode.setProperty(ClassFileSequencerLexicon.INTERFACE, false);
{ // javadocs
final Javadoc javadoc = enumType.getJavadoc();
if (javadoc != null) {
record(javadoc, enumNode);
}
}
{ // modifiers
final int modifiers = enumType.getModifiers();
enumNode.setProperty(ClassFileSequencerLexicon.ABSTRACT, (modifiers & Modifier.ABSTRACT) != 0);
enumNode.setProperty(ClassFileSequencerLexicon.FINAL, (modifiers & Modifier.FINAL) != 0);
enumNode.setProperty(ClassFileSequencerLexicon.STATIC, (modifiers & Modifier.STATIC) != 0);
enumNode.setProperty(ClassFileSequencerLexicon.STRICT_FP, (modifiers & Modifier.STRICTFP) != 0);
enumNode.setProperty(ClassFileSequencerLexicon.VISIBILITY, getVisibility(modifiers));
}
{ // annotations
@SuppressWarnings( "unchecked" )
final List<IExtendedModifier> modifiers = enumType.modifiers();
recordAnnotations(modifiers, enumNode);
}
{ // implements
@SuppressWarnings( "unchecked" )
final List<Type> interfaces = enumType.superInterfaceTypes();
if ((interfaces != null) && !interfaces.isEmpty()) {
final String[] interfaceNames = new String[interfaces.size()];
final Node containerNode = enumNode.addNode(ClassFileSequencerLexicon.IMPLEMENTS, ClassFileSequencerLexicon.TYPES);
int i = 0;
for (final Type superInterfaceType : interfaces) {
interfaceNames[i] = getTypeName(superInterfaceType);
record(superInterfaceType, ClassFileSequencerLexicon.INTERFACE, containerNode);
++i;
}
enumNode.setProperty(ClassFileSequencerLexicon.INTERFACES, interfaceNames);
}
}
{ // enum constants
@SuppressWarnings( "unchecked" )
final List<EnumConstantDeclaration> enumValues = enumType.enumConstants();
if ((enumValues != null) && !enumValues.isEmpty()) {
final String[] values = new String[enumValues.size()];
final Node containerNode = enumNode.addNode(ClassFileSequencerLexicon.ENUM_CONSTANTS,
ClassFileSequencerLexicon.ENUM_CONSTANTS);
int i = 0;
for (final EnumConstantDeclaration enumConstant : enumValues) {
values[i++] = enumConstant.getName().getFullyQualifiedName();
record(enumConstant, containerNode);
}
enumNode.setProperty(ClassFileSequencerLexicon.ENUM_VALUES, values);
}
}
recordBodyDeclarations(enumType, enumNode);
recordSourceReference(enumType, enumNode);
return enumNode;
}
/**
* <pre>
* FieldDeclaration:
* [Javadoc] { ExtendedModifier } Type VariableDeclarationFragment
* { , VariableDeclarationFragment } ;
*
* VariableDeclarationFragment:
* Identifier { [] } [ = Expression ]
* </pre>
*
* A field container node will be created if one does not exist under <code>node</node>.
*
* @param field the {@link FieldDeclaration field} being recorded {cannot be <code>null</code>
* @param parentNode the parent {@link Node node} (cannot be <code>null</code>)
* @throws Exception if there is a problem
*/
protected void record( final FieldDeclaration field,
final Node parentNode ) throws Exception {
@SuppressWarnings( "unchecked" )
final List<VariableDeclarationFragment> frags = field.fragments();
final String name = frags.get(0).getName().getFullyQualifiedName();
final Node fieldNode = parentNode.addNode(name, ClassFileSequencerLexicon.FIELD);
fieldNode.setProperty(ClassFileSequencerLexicon.NAME, name);
{ // javadocs
final Javadoc javadoc = field.getJavadoc();
if (javadoc != null) {
record(javadoc, fieldNode);
}
}
{ // type
final Type type = field.getType();
final String typeName = getTypeName(type);
fieldNode.setProperty(ClassFileSequencerLexicon.TYPE_CLASS_NAME, typeName);
record(type, ClassFileSequencerLexicon.TYPE, fieldNode);
}
{ // modifiers
final int modifiers = field.getModifiers();
fieldNode.setProperty(ClassFileSequencerLexicon.FINAL, (modifiers & Modifier.FINAL) != 0);
fieldNode.setProperty(ClassFileSequencerLexicon.STATIC, (modifiers & Modifier.STATIC) != 0);
fieldNode.setProperty(ClassFileSequencerLexicon.TRANSIENT, (modifiers & Modifier.TRANSIENT) != 0);
fieldNode.setProperty(ClassFileSequencerLexicon.VISIBILITY, getVisibility(modifiers));
fieldNode.setProperty(ClassFileSequencerLexicon.VOLATILE, (modifiers & Modifier.VOLATILE) != 0);
}
{ // annotations
@SuppressWarnings( "unchecked" )
final List<IExtendedModifier> modifiers = field.modifiers();
recordAnnotations(modifiers, fieldNode);
}
{ // fragments
@SuppressWarnings( "unchecked" )
final List<VariableDeclarationFragment> fragments = field.fragments();
if ((fragments != null) && !fragments.isEmpty()) {
for (final VariableDeclarationFragment var : fragments) {
final Expression initializer = var.getInitializer();
if (initializer != null) {
recordExpression(initializer, ClassFileSequencerLexicon.INITIALIZER, fieldNode);
}
}
}
}
recordSourceReference(field, fieldNode);
}
/**
* <pre>
* Initializer:
* [ static ] Block
*
* Block:
* { { Statement } }
* </pre>
*
* @param initializer the {@link Initializer initializer} being recorded (cannot be <code>null</code>)
* @param nodeName the name of the node being created that represents the initializer (cannot be <code>null</code> or empty)
* @param parentNode the parent {@link Node node} (cannot be <code>null</code>)
* @throws Exception if there is a problem
*/
protected void record( final Initializer initializer,
final String nodeName,
final Node parentNode ) throws Exception {
final Block block = initializer.getBody();
if (block != null) {
@SuppressWarnings( "unchecked" )
final List<Statement> statements = block.statements();
if ((statements != null) && !statements.isEmpty()) {
final Node initializerNode = parentNode.addNode(nodeName, ClassFileSequencerLexicon.STATEMENTS);
record(block, initializerNode);
}
}
}
/**
* <pre>
* MethodDeclaration:
* [ Javadoc ] { ExtendedModifier }
* [ < TypeParameter { , TypeParameter } > ]
* ( Type | void ) Identifier (
* [ FormalParameter
* { , FormalParameter } ] ) {[ ] }
* [ throws TypeName { , TypeName } ] ( Block | ; )
*
* ConstructorDeclaration:
* [ Javadoc ] { ExtendedModifier }
* [ < TypeParameter { , TypeParameter } > ]
* Identifier (
* [ FormalParameter
* { , FormalParameter } ] )
* [throws TypeName { , TypeName } ] Block
*
* </pre>
*
* @param method the {@link MethodDeclaration method} being recorded (cannot be <code>null</code>)
* @param parentNode the parent {@link Node node} (cannot be <code>null</code>)
* @throws Exception if there is a problem
*/
protected void record( final MethodDeclaration method,
final Node parentNode ) throws Exception {
final String name = method.getName().getFullyQualifiedName();
final Node methodNode = parentNode.addNode(name, ClassFileSequencerLexicon.METHOD);
methodNode.setProperty(ClassFileSequencerLexicon.NAME, name);
{ // javadocs
final Javadoc javadoc = method.getJavadoc();
if (javadoc != null) {
record(javadoc, methodNode);
}
}
{ // type parameters
@SuppressWarnings( "unchecked" )
final List<TypeParameter> typeParams = method.typeParameters();
if ((typeParams != null) && !typeParams.isEmpty()) {
final Node containerNode = methodNode.addNode(ClassFileSequencerLexicon.TYPE_PARAMETERS,
ClassFileSequencerLexicon.TYPE_PARAMETERS);
for (final TypeParameter param : typeParams) {
record(param, containerNode);
}
}
}
{ // modifiers
final int modifiers = method.getModifiers();
methodNode.setProperty(ClassFileSequencerLexicon.ABSTRACT, (modifiers & Modifier.ABSTRACT) != 0);
methodNode.setProperty(ClassFileSequencerLexicon.FINAL, (modifiers & Modifier.FINAL) != 0);
methodNode.setProperty(ClassFileSequencerLexicon.NATIVE, (modifiers & Modifier.NATIVE) != 0);
methodNode.setProperty(ClassFileSequencerLexicon.STATIC, (modifiers & Modifier.STATIC) != 0);
methodNode.setProperty(ClassFileSequencerLexicon.STRICT_FP, (modifiers & Modifier.STRICTFP) != 0);
methodNode.setProperty(ClassFileSequencerLexicon.SYNCHRONIZED, (modifiers & Modifier.SYNCHRONIZED) != 0);
methodNode.setProperty(ClassFileSequencerLexicon.VISIBILITY, getVisibility(modifiers));
}
{ // annotations
@SuppressWarnings( "unchecked" )
final List<IExtendedModifier> modifiers = method.modifiers();
recordAnnotations(modifiers, methodNode);
}
{ // parameters
@SuppressWarnings( "unchecked" )
final List<SingleVariableDeclaration> params = method.parameters();
if ((params != null) && !params.isEmpty()) {
final Node containerNode = methodNode.addNode(ClassFileSequencerLexicon.METHOD_PARAMETERS,
ClassFileSequencerLexicon.PARAMETERS);
for (final SingleVariableDeclaration param : params) {
record(param, containerNode);
}
}
}
{ // return type
if (method.isConstructor()) {
methodNode.setProperty(ClassFileSequencerLexicon.RETURN_TYPE_CLASS_NAME, Void.TYPE.getCanonicalName());
} else {
final Type returnType = method.getReturnType2();
methodNode.setProperty(ClassFileSequencerLexicon.RETURN_TYPE_CLASS_NAME, getTypeName(returnType));
record(returnType, ClassFileSequencerLexicon.RETURN_TYPE, methodNode);
}
}
{ // thrown exceptions
@SuppressWarnings( "unchecked" )
final List<Name> errors = method.thrownExceptions();
if ((errors != null) && !errors.isEmpty()) {
final String[] errorNames = new String[errors.size()];
int i = 0;
for (final Name error : errors) {
errorNames[i++] = error.getFullyQualifiedName();
}
methodNode.setProperty(ClassFileSequencerLexicon.THROWN_EXCEPTIONS, errorNames);
}
}
{ // body
final Block body = method.getBody();
if ((body != null) && (body.statements() != null) && !body.statements().isEmpty()) {
final Node bodyNode = methodNode.addNode(ClassFileSequencerLexicon.BODY, ClassFileSequencerLexicon.STATEMENTS);
record(body, bodyNode);
}
}
recordSourceReference(method, methodNode);
}
/**
* Convert the compilation unit into JCR nodes.
*
* @param context the sequencer context
* @param sourceCode the source code being recorded (can be <code>null</code> if there is no source code)
* @param outputNode the {@link Node node} where the output will be saved (cannot be <code>null</code>)
* @throws Exception if there is a problem
*/
protected void record( final Sequencer.Context context,
final char[] sourceCode,
final Node outputNode ) throws Exception {
if ((sourceCode == null) || (sourceCode.length == 0)) {
LOGGER.debug("No source code was found for output node {0}", outputNode.getName());
return;
}
this.context = context;
this.sourceCode = new String(sourceCode);
this.compilationUnit = (CompilationUnit)CompilationUnitParser.runJLS3Conversion(sourceCode, true);
outputNode.addMixin(ClassFileSequencerLexicon.COMPILATION_UNIT);
record(this.compilationUnit, outputNode);
}
/**
* <pre>
* SingleVariableDeclaration:
* { ExtendedModifier } Type [ ... ] Identifier { [] } [ = Expression ]
* </pre>
*
* @param variable the {@link SingleVariableDeclaration variable} being recorded (cannot be <code>null</code>)
* @param parentNode the parent {@link Node node} where the variable is being recorded (cannot be <code>null</code>)
* @throws Exception if there is a problem
*/
protected void record( final SingleVariableDeclaration variable,
final Node parentNode ) throws Exception {
final String name = variable.getName().getFullyQualifiedName();
final Node paramNode = parentNode.addNode(name, ClassFileSequencerLexicon.PARAMETER);
paramNode.setProperty(ClassFileSequencerLexicon.NAME, name);
paramNode.setProperty(ClassFileSequencerLexicon.FINAL, (variable.getModifiers() & Modifier.FINAL) != 0);
paramNode.setProperty(ClassFileSequencerLexicon.VARARGS, variable.isVarargs());
{ // type
final Type type = variable.getType();
final String typeName = getTypeName(type);
paramNode.setProperty(ClassFileSequencerLexicon.TYPE_CLASS_NAME, typeName);
record(type, ClassFileSequencerLexicon.TYPE, paramNode);
}
{ // initializer
final Expression initializer = variable.getInitializer();
if (initializer != null) {
recordExpression(initializer, ClassFileSequencerLexicon.INITIALIZER, paramNode);
}
}
{ // annotations
@SuppressWarnings( "unchecked" )
final List<IExtendedModifier> modifiers = variable.modifiers();
recordAnnotations(modifiers, paramNode);
}
recordSourceReference(variable, paramNode);
}
/**
* <pre>
* Type:
* PrimitiveType
* ArrayType
* SimpleType
* QualifiedType
* ParameterizedType
* WildcardType
*
* PrimitiveType:
* byte
* short
* char
* int
* long
* float
* double
* boolean
* void
*
* ArrayType:
* Type [ ]
*
* SimpleType:
* TypeName
*
* ParameterizedType:
* Type < Type { , Type } >
*
* QualifiedType:
* Type . SimpleName
*
* WildcardType:
* ? [ ( extends | super) Type ]
* </pre>
*
* @param type the type {@link Type type} being recorded (cannot be <code>null</code>)
* @param typeNodeName the name of the type node being recorded (cannot be <code>null</code> or empty)
* @param parentNode the parent {@link Node node} where the type will be recorded (cannot be <code>null</code>)
* @throws Exception if there is a problem
*/
protected void record( final Type type,
final String typeNodeName,
final Node parentNode ) throws Exception {
if (type.isSimpleType()) {
final Node typeNode = parentNode.addNode(typeNodeName, ClassFileSequencerLexicon.SIMPLE_TYPE);
typeNode.setProperty(ClassFileSequencerLexicon.TYPE_CLASS_NAME, getTypeName(type));
LOGGER.debug("Simple type created at '{0}'", typeNode.getPath());
} else if (type.isPrimitiveType()) {
final Node typeNode = parentNode.addNode(typeNodeName, ClassFileSequencerLexicon.PRIMITIVE_TYPE);
typeNode.setProperty(ClassFileSequencerLexicon.TYPE_CLASS_NAME, getTypeName(type));
LOGGER.debug("Primitive type created at '{0}'", typeNode.getPath());
} else if (type.isArrayType()) {
final Node typeNode = parentNode.addNode(typeNodeName, ClassFileSequencerLexicon.ARRAY_TYPE);
typeNode.setProperty(ClassFileSequencerLexicon.TYPE_CLASS_NAME, getTypeName(type));
final ArrayType arrayType = ((ArrayType)type);
typeNode.setProperty(ClassFileSequencerLexicon.DIMENSIONS, arrayType.getDimensions());
final Type componentType = arrayType.getComponentType();
record(componentType, ClassFileSequencerLexicon.COMPONENT_TYPE, typeNode);
LOGGER.debug("Array type created at '{0}'", typeNode.getPath());
} else if (type.isParameterizedType()) {
final Node typeNode = parentNode.addNode(typeNodeName, ClassFileSequencerLexicon.PARAMETERIZED_TYPE);
typeNode.setProperty(ClassFileSequencerLexicon.TYPE_CLASS_NAME, getTypeName(type));
final ParameterizedType paramType = (ParameterizedType)type;
final Type baseType = paramType.getType();
record(baseType, ClassFileSequencerLexicon.BASE_TYPE, typeNode);
@SuppressWarnings( "unchecked" )
final List<Type> arguments = ((ParameterizedType)type).typeArguments();
if ((arguments != null) && !arguments.isEmpty()) {
final Node containerNode = typeNode.addNode(ClassFileSequencerLexicon.ARGUMENTS, ClassFileSequencerLexicon.TYPES);
for (final Type arg : arguments) {
record(arg, ClassFileSequencerLexicon.ARGUMENT, containerNode);
}
}
LOGGER.debug("Parameterized type created at '{0}'", typeNode.getPath());
} else if (type.isQualifiedType()) {
final Node typeNode = parentNode.addNode(typeNodeName, ClassFileSequencerLexicon.QUALIFIED_TYPE);
typeNode.setProperty(ClassFileSequencerLexicon.TYPE_CLASS_NAME, getTypeName(type));
final QualifiedType qualifiedType = (QualifiedType)type;
record(qualifiedType.getQualifier(), ClassFileSequencerLexicon.QUALIFIER, typeNode);
LOGGER.debug("Qualified type created at '{0}'", typeNode.getPath());
} else if (type.isWildcardType()) {
final Node typeNode = parentNode.addNode("?", ClassFileSequencerLexicon.WILDCARD_TYPE);
typeNode.setProperty(ClassFileSequencerLexicon.TYPE_CLASS_NAME, getTypeName(type));
final WildcardType wildcardType = (WildcardType)type;
final String bound = wildcardType.isUpperBound() ? ClassFileSequencerLexicon.WildcardTypeBound.UPPER.toString() : ClassFileSequencerLexicon.WildcardTypeBound.LOWER.toString();
typeNode.setProperty(ClassFileSequencerLexicon.BOUND_TYPE, bound);
if (wildcardType.getBound() != null) {
record(wildcardType.getBound(), ClassFileSequencerLexicon.BOUND, typeNode);
}
LOGGER.debug("Wildcard type created at '{0}'", typeNode.getPath());
} else {
assert false;
LOGGER.error(JavaFileI18n.unhandledType, type.getClass().getName(), typeNodeName);
}
}
/**
* <pre>
* TypeDeclaration:
* ClassDeclaration
* InterfaceDeclaration
*
* ClassDeclaration:
* [ Javadoc ] { ExtendedModifier } class Identifier
* [ < TypeParameter { , TypeParameter } > ]
* [ extends Type ]
* [ implements Type { , Type } ]
* { { ClassBodyDeclaration | ; } }
*
* InterfaceDeclaration:
* [ Javadoc ] { ExtendedModifier } interface Identifier
* [ < TypeParameter { , TypeParameter } > ]
* [ extends Type { , Type } ]
* { { InterfaceBodyDeclaration | ; } }
* </pre>
*
* @param type the {@link TypeDeclaration type} being recorded (cannot be <code>null</code>)
* @param parentNode the parent {@link Node node} where the new type will be created (cannot be <code>null</code>)
* @return the node representing the type being recorded (never <code>null</code>)
* @throws Exception if there is a problem
*/
protected Node record( final TypeDeclaration type,
final Node parentNode ) throws Exception {
final String name = type.getName().getFullyQualifiedName();
final Node typeNode = parentNode.addNode(name, ClassFileSequencerLexicon.CLASS);
typeNode.setProperty(ClassFileSequencerLexicon.NAME, name);
typeNode.setProperty(ClassFileSequencerLexicon.SEQUENCED_DATE, this.context.getTimestamp());
final boolean isInterface = type.isInterface();
typeNode.setProperty(ClassFileSequencerLexicon.INTERFACE, isInterface);
// extends and implements
@SuppressWarnings( "unchecked" )
final List<Type> interfaces = type.superInterfaceTypes();
{ // extends
if (isInterface) {
if ((interfaces != null) && !interfaces.isEmpty()) {
final Node extendsNode = typeNode.addNode(ClassFileSequencerLexicon.EXTENDS, ClassFileSequencerLexicon.TYPES);
for (final Type interfaceType : interfaces) {
record(interfaceType, ClassFileSequencerLexicon.INTERFACE, extendsNode);
}
}
} else {
final Type superType = type.getSuperclassType();
String superTypeName = null;
if (superType == null) {
superTypeName = Object.class.getCanonicalName();
} else {
superTypeName = getTypeName(superType);
}
assert !StringUtil.isBlank(superTypeName);
typeNode.setProperty(ClassFileSequencerLexicon.SUPER_CLASS_NAME, superTypeName);
if (superType != null) {
final Node extendsNode = typeNode.addNode(ClassFileSequencerLexicon.EXTENDS, ClassFileSequencerLexicon.TYPES);
record(superType, getTypeName(superType), extendsNode);
}
}
}
{ // implements
if (!isInterface && (interfaces != null) && !interfaces.isEmpty()) {
final Node implementsNode = typeNode.addNode(ClassFileSequencerLexicon.IMPLEMENTS,
ClassFileSequencerLexicon.TYPES);
final String[] interfaceNames = new String[interfaces.size()];
for (int i = 0, size = interfaces.size(); i < size; ++i) {
final Type interfaceType = interfaces.get(i);
interfaceNames[i] = getTypeName(interfaceType);
record(interfaceType, interfaceNames[i], implementsNode);
}
typeNode.setProperty(ClassFileSequencerLexicon.INTERFACES, interfaceNames);
}
}
{ // javadocs
final Javadoc javadoc = type.getJavadoc();
if (javadoc != null) {
record(javadoc, typeNode);
}
}
{ // modifiers
final int modifiers = type.getModifiers();
typeNode.setProperty(ClassFileSequencerLexicon.ABSTRACT, (modifiers & Modifier.ABSTRACT) != 0);
typeNode.setProperty(ClassFileSequencerLexicon.FINAL, (modifiers & Modifier.FINAL) != 0);
typeNode.setProperty(ClassFileSequencerLexicon.STATIC, (modifiers & Modifier.STATIC) != 0);
typeNode.setProperty(ClassFileSequencerLexicon.STRICT_FP, (modifiers & Modifier.STRICTFP) != 0);
typeNode.setProperty(ClassFileSequencerLexicon.VISIBILITY, getVisibility(modifiers));
}
{ // annotations
@SuppressWarnings( "unchecked" )
final List<IExtendedModifier> modifiers = type.modifiers();
recordAnnotations(modifiers, typeNode);
}
{ // type parameters
@SuppressWarnings( "unchecked" )
final List<TypeParameter> typeParams = type.typeParameters();
if ((typeParams != null) && !typeParams.isEmpty()) {
final Node containerNode = typeNode.addNode(ClassFileSequencerLexicon.TYPE_PARAMETERS,
ClassFileSequencerLexicon.TYPE_PARAMETERS);
for (final TypeParameter param : typeParams) {
record(param, containerNode);
}
}
}
recordBodyDeclarations(type, typeNode);
recordSourceReference(type, typeNode);
return typeNode;
}
/**
* <pre>
* TypeParameter:
* TypeVariable [ extends Type { & Type } ]
* </pre>
*
* @param param the {@link TypeParameter type parameter} being recorded (cannot be <code>null</code>)
* @param parentNode the parent {@link Node node} where the type parameter will be recorded (cannot be <code>null</code>)
* @throws Exception if there is a problem
*/
protected void record( final TypeParameter param,
final Node parentNode ) throws Exception {
final String paramName = param.getName().getFullyQualifiedName();
final Node paramNode = parentNode.addNode(paramName, ClassFileSequencerLexicon.TYPE_PARAMETER);
@SuppressWarnings( "unchecked" )
final List<Type> bounds = param.typeBounds();
if ((bounds != null) && !bounds.isEmpty()) {
final Node containerNode = paramNode.addNode(ClassFileSequencerLexicon.BOUNDS, ClassFileSequencerLexicon.TYPES);
for (final Type bound : bounds) {
record(bound, getTypeName(bound), containerNode);
}
}
recordSourceReference(param, paramNode);
}
protected void recordAnnotationMember( final String memberName,
final Expression expression,
final Node parentNode ) throws Exception {
final String name = (StringUtil.isBlank(memberName) ? "default" : memberName);
final Node node = parentNode.addNode(name, ClassFileSequencerLexicon.ANNOTATION_MEMBER);
node.setProperty(ClassFileSequencerLexicon.NAME, name);
String value = null;
if (expression instanceof StringLiteral) {
value = ((StringLiteral)expression).getLiteralValue();
} else if (expression instanceof Name) {
value = ((Name)expression).getFullyQualifiedName();
} else if (expression instanceof BooleanLiteral) {
value = Boolean.toString(((BooleanLiteral)expression).booleanValue());
} else if (expression instanceof CharacterLiteral) {
value = Character.toString(((CharacterLiteral)expression).charValue());
} else {
value = expression.toString();
}
node.setProperty(ClassFileSequencerLexicon.VALUE, value);
recordExpression(expression, name, node);
}
protected void recordAnnotations( final List<IExtendedModifier> extendedModifiers,
final Node node ) throws Exception {
if ((extendedModifiers != null) && !extendedModifiers.isEmpty()) {
Node containerNode = null;
for (final IExtendedModifier modifier : extendedModifiers) {
if (modifier.isAnnotation()) {
if (containerNode == null) {
containerNode = node.addNode(ClassFileSequencerLexicon.ANNOTATIONS, ClassFileSequencerLexicon.ANNOTATIONS);
}
record((Annotation)modifier, containerNode);
}
}
}
}
protected void recordBodyDeclarations( final AbstractTypeDeclaration type,
final Node typeNode ) throws Exception {
Node constructorsContainer = null;
Node fieldsContainer = null;
Node methodsContainer = null;
Node nestedTypesContainer = null;
for (final Object bodyDeclaration : type.bodyDeclarations()) {
if (bodyDeclaration instanceof FieldDeclaration) {
if (fieldsContainer == null) {
fieldsContainer = typeNode.addNode(ClassFileSequencerLexicon.FIELDS, ClassFileSequencerLexicon.FIELDS);
}
record((FieldDeclaration)bodyDeclaration, fieldsContainer);
} else if (bodyDeclaration instanceof MethodDeclaration) {
final MethodDeclaration method = (MethodDeclaration)bodyDeclaration;
if (method.isConstructor()) {
if (constructorsContainer == null) {
constructorsContainer = typeNode.addNode(ClassFileSequencerLexicon.CONSTRUCTORS,
ClassFileSequencerLexicon.CONSTRUCTORS);
}
record(method, constructorsContainer);
} else {
if (methodsContainer == null) {
methodsContainer = typeNode.addNode(ClassFileSequencerLexicon.METHODS, ClassFileSequencerLexicon.METHODS);
}
record(method, methodsContainer);
}
} else if (bodyDeclaration instanceof TypeDeclaration) {
if (nestedTypesContainer == null) {
nestedTypesContainer = typeNode.addNode(ClassFileSequencerLexicon.NESTED_TYPES,
ClassFileSequencerLexicon.NESTED_TYPES);
}
record((TypeDeclaration)bodyDeclaration, nestedTypesContainer);
} else if (bodyDeclaration instanceof EnumDeclaration) {
if (nestedTypesContainer == null) {
nestedTypesContainer = typeNode.addNode(ClassFileSequencerLexicon.NESTED_TYPES,
ClassFileSequencerLexicon.NESTED_TYPES);
}
record((EnumDeclaration)bodyDeclaration, nestedTypesContainer);
} else if (bodyDeclaration instanceof Initializer) {
record(((Initializer)bodyDeclaration), ClassFileSequencerLexicon.INITIALIZER, typeNode);
} else {
assert false;
LOGGER.error(JavaFileI18n.unhandledBodyDeclarationType, bodyDeclaration.getClass().getName());
}
}
}
protected void recordBodyDeclarations( final AnonymousClassDeclaration anonClass,
final Node enumConstantNode ) throws Exception {
Node fieldsContainer = null;
Node methodsContainer = null;
Node nestedTypesContainer = null;
for (final Object bodyDeclaration : anonClass.bodyDeclarations()) {
if (bodyDeclaration instanceof FieldDeclaration) {
if (fieldsContainer == null) {
fieldsContainer = enumConstantNode.addNode(ClassFileSequencerLexicon.FIELDS, ClassFileSequencerLexicon.FIELDS);
}
record((FieldDeclaration)bodyDeclaration, fieldsContainer);
} else if (bodyDeclaration instanceof MethodDeclaration) {
if (methodsContainer == null) {
methodsContainer = enumConstantNode.addNode(ClassFileSequencerLexicon.METHODS,
ClassFileSequencerLexicon.METHODS);
}
record((MethodDeclaration)bodyDeclaration, methodsContainer);
} else if (bodyDeclaration instanceof TypeDeclaration) {
if (nestedTypesContainer == null) {
nestedTypesContainer = enumConstantNode.addNode(ClassFileSequencerLexicon.NESTED_TYPES,
ClassFileSequencerLexicon.NESTED_TYPES);
}
record((TypeDeclaration)bodyDeclaration, nestedTypesContainer);
} else {
assert false;
LOGGER.error(JavaFileI18n.unhandledBodyDeclarationType, bodyDeclaration.getClass().getName());
}
}
}
/**
* <pre>
* Comment:
* LineComment
* BlockComment
* Javadoc
* </pre>
*
* @param compilationUnit the {@link CompilationUnit compilation unit} being recorded (cannot be <code>null</code>)
* @param outputNode the parent {@link Node node} (cannot be <code>null</code>)
* @throws Exception if there is a problem
*/
protected void recordComments( final CompilationUnit compilationUnit,
final Node outputNode ) throws Exception {
@SuppressWarnings( "unchecked" )
final List<Comment> comments = compilationUnit.getCommentList();
if ((comments != null) && !comments.isEmpty()) {
final Node containerNode = outputNode.addNode(ClassFileSequencerLexicon.COMMENTS, ClassFileSequencerLexicon.COMMENTS);
for (final Comment comment : comments) {
// javadocs are stored with the object they pertain to
if (!comment.isDocComment()) {
record(comment, containerNode);
}
}
}
}
protected void recordCompilerMessages( final CompilationUnit unit,
final Node parentNode ) throws Exception {
final Message[] messages = unit.getMessages();
if ((messages != null) && (messages.length != 0)) {
final Node containerNode = parentNode.addNode(ClassFileSequencerLexicon.MESSAGES, ClassFileSequencerLexicon.MESSAGES);
for (final Message message : messages) {
final Node messageNode = containerNode.addNode(ClassFileSequencerLexicon.MESSAGE,
ClassFileSequencerLexicon.MESSAGE);
messageNode.setProperty(ClassFileSequencerLexicon.MESSAGE, message.getMessage());
messageNode.setProperty(ClassFileSequencerLexicon.START_POSITION, message.getStartPosition());
messageNode.setProperty(ClassFileSequencerLexicon.LENGTH, message.getLength());
}
}
}
/**
* <pre>
* Expression:
*
* Annotation,
* ArrayAccess,
* ArrayCreation,
* ArrayInitializer,
* Assignment,
* BooleanLiteral,
* CastExpression,
* CharacterLiteral,
* ClassInstanceCreation,
* ConditionalExpression,
* FieldAccess,
* InfixExpression,
* InstanceofExpression,
* MethodInvocation,
* Name,
* NullLiteral,
* NumberLiteral,
* ParenthesizedExpression,
* PostfixExpression,
* PrefixExpression,
* StringLiteral,
* SuperFieldAccess,
* SuperMethodInvocation,
* ThisExpression,
* TypeLiteral,
* VariableDeclarationExpression
* </pre>
*
* @param expression the {@link Expression expression} being recorded (cannot be <code>null</code>)
* @param nodeName the name of the expression node that is created (cannot be <code>null</code> or empty)
* @param parentNode the parent {@link Node node} where the expression is being recorded (cannot be <code>null</code>)
* @throws Exception if there is a problem
*/
protected void recordExpression( final Expression expression,
final String nodeName,
final Node parentNode ) throws Exception {
// TODO handle all the different types of expressions
final Node expressionNode = parentNode.addNode(nodeName, ClassFileSequencerLexicon.EXPRESSION);
expressionNode.setProperty(ClassFileSequencerLexicon.CONTENT, expression.toString());
recordSourceReference(expression, expressionNode);
}
/**
* <pre>
* ImportDeclaration:
* import [ static ] Name [ . * ] ;
* </pre>
*
* @param compilationUnit the {@link CompilationUnit compilation unit} being recorded (cannot be <code>null</code>)
* @param outputNode the parent {@link Node node} (cannot be <code>null</code>)
* @throws Exception if there is a problem
*/
protected void recordImports( final CompilationUnit compilationUnit,
final Node outputNode ) throws Exception {
@SuppressWarnings( "unchecked" )
final List<ImportDeclaration> imports = compilationUnit.imports();
if ((imports != null) && !imports.isEmpty()) {
final Node containerNode = outputNode.addNode(ClassFileSequencerLexicon.IMPORTS, ClassFileSequencerLexicon.IMPORTS);
for (final ImportDeclaration mport : imports) {
final Node importNode = containerNode.addNode(mport.getName().getFullyQualifiedName(),
ClassFileSequencerLexicon.IMPORT);
importNode.setProperty(ClassFileSequencerLexicon.STATIC, mport.isStatic());
importNode.setProperty(ClassFileSequencerLexicon.ON_DEMAND, mport.isOnDemand());
recordSourceReference(mport, importNode);
}
}
}
/**
* <pre>
* PackageDeclaration:
* [ Javadoc ] { Annotation } package Name ;
* </pre>
*
* @param compilationUnit the {@link CompilationUnit compilation unit} whose package is being recorded (cannot be
* <code>null</code>)
* @param outputNode the output node (cannot be <code>null</code>)
* @return the package {@link Node node} or the passed in <code>outputNode</code> if a package is not found
* @throws Exception if there is a problem
*/
protected Node recordPackage( final CompilationUnit compilationUnit,
final Node outputNode ) throws Exception {
final PackageDeclaration pkg = compilationUnit.getPackage();
if (pkg == null) {
return outputNode;
}
Node pkgNode = outputNode;
{ // create node for each segment of the package name
final String pkgName = pkg.getName().getFullyQualifiedName();
final String[] packagePath = pkgName.split("\\.");
if (pkgName.length() > 0) {
for (final String segment : packagePath) {
pkgNode = pkgNode.addNode(segment);
pkgNode.addMixin(ClassFileSequencerLexicon.PACKAGE);
}
}
}
{ // Javadocs
final Javadoc javadoc = pkg.getJavadoc();
if (javadoc != null) {
record(javadoc, pkgNode);
}
}
{ // annotations
@SuppressWarnings( "unchecked" )
final List<Annotation> annotations = pkg.annotations();
if ((annotations != null) && !annotations.isEmpty()) {
for (final Annotation annotation : annotations) {
record(annotation, pkgNode);
}
}
}
recordSourceReference(pkg, pkgNode);
return pkgNode;
}
protected void recordSourceReference( final ASTNode astNode,
final Node jcrNode ) throws Exception {
jcrNode.setProperty(ClassFileSequencerLexicon.START_POSITION, astNode.getStartPosition());
jcrNode.setProperty(ClassFileSequencerLexicon.LENGTH, astNode.getLength());
}
protected void recordTypes( final CompilationUnit unit,
final Node compilationUnitNode,
final Node pkgNode ) throws Exception {
@SuppressWarnings( "unchecked" )
final List<AbstractTypeDeclaration> topLevelTypes = unit.types();
if ((topLevelTypes != null) && !topLevelTypes.isEmpty()) {
final List<Node> types = new ArrayList<>(topLevelTypes.size());
for (final AbstractTypeDeclaration type : topLevelTypes) {
if (type instanceof TypeDeclaration) {
types.add(record((TypeDeclaration)type, pkgNode));
} else if (type instanceof EnumDeclaration) {
types.add(record((EnumDeclaration)type, pkgNode));
} else if (type instanceof AnnotationTypeDeclaration) {
types.add(record((AnnotationTypeDeclaration)type, pkgNode));
} else {
assert false;
LOGGER.error(JavaFileI18n.unhandledTopLevelType, type.getName().getFullyQualifiedName());
}
}
final ValueFactory factory = this.context.valueFactory();
final Value[] refs = new Value[topLevelTypes.size()];
int i = 0;
for (final Node typeNode : types) {
refs[i++] = factory.createValue(typeNode);
}
compilationUnitNode.setProperty(ClassFileSequencerLexicon.TYPES, refs);
}
recordSourceReference(compilationUnit, compilationUnitNode);
}
}