/* * 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 static org.modeshape.sequencer.classfile.ClassFileSequencerLexicon.ABSTRACT; import static org.modeshape.sequencer.classfile.ClassFileSequencerLexicon.ANNOTATION; import static org.modeshape.sequencer.classfile.ClassFileSequencerLexicon.ANNOTATIONS; import static org.modeshape.sequencer.classfile.ClassFileSequencerLexicon.ANNOTATION_MEMBER; import static org.modeshape.sequencer.classfile.ClassFileSequencerLexicon.CLASS; import static org.modeshape.sequencer.classfile.ClassFileSequencerLexicon.CONSTRUCTORS; import static org.modeshape.sequencer.classfile.ClassFileSequencerLexicon.ENUM; import static org.modeshape.sequencer.classfile.ClassFileSequencerLexicon.ENUM_VALUES; import static org.modeshape.sequencer.classfile.ClassFileSequencerLexicon.FIELD; import static org.modeshape.sequencer.classfile.ClassFileSequencerLexicon.FIELDS; import static org.modeshape.sequencer.classfile.ClassFileSequencerLexicon.FINAL; import static org.modeshape.sequencer.classfile.ClassFileSequencerLexicon.IMPORTS; import static org.modeshape.sequencer.classfile.ClassFileSequencerLexicon.INTERFACE; import static org.modeshape.sequencer.classfile.ClassFileSequencerLexicon.INTERFACES; import static org.modeshape.sequencer.classfile.ClassFileSequencerLexicon.METHOD; import static org.modeshape.sequencer.classfile.ClassFileSequencerLexicon.METHODS; import static org.modeshape.sequencer.classfile.ClassFileSequencerLexicon.METHOD_PARAMETERS; import static org.modeshape.sequencer.classfile.ClassFileSequencerLexicon.NAME; import static org.modeshape.sequencer.classfile.ClassFileSequencerLexicon.NATIVE; import static org.modeshape.sequencer.classfile.ClassFileSequencerLexicon.PACKAGE; import static org.modeshape.sequencer.classfile.ClassFileSequencerLexicon.PARAMETER; import static org.modeshape.sequencer.classfile.ClassFileSequencerLexicon.PARAMETERS; import static org.modeshape.sequencer.classfile.ClassFileSequencerLexicon.RETURN_TYPE_CLASS_NAME; import static org.modeshape.sequencer.classfile.ClassFileSequencerLexicon.SEQUENCED_DATE; import static org.modeshape.sequencer.classfile.ClassFileSequencerLexicon.STATIC; import static org.modeshape.sequencer.classfile.ClassFileSequencerLexicon.STRICT_FP; import static org.modeshape.sequencer.classfile.ClassFileSequencerLexicon.SUPER_CLASS_NAME; import static org.modeshape.sequencer.classfile.ClassFileSequencerLexicon.SYNCHRONIZED; import static org.modeshape.sequencer.classfile.ClassFileSequencerLexicon.TRANSIENT; import static org.modeshape.sequencer.classfile.ClassFileSequencerLexicon.TYPE_CLASS_NAME; import static org.modeshape.sequencer.classfile.ClassFileSequencerLexicon.VALUE; import static org.modeshape.sequencer.classfile.ClassFileSequencerLexicon.VISIBILITY; import static org.modeshape.sequencer.classfile.ClassFileSequencerLexicon.VOLATILE; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.jcr.Node; import javax.jcr.RepositoryException; 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.metadata.Visibility; import org.modeshape.sequencer.javafile.metadata.AbstractMetadata; import org.modeshape.sequencer.javafile.metadata.AnnotationMetadata; import org.modeshape.sequencer.javafile.metadata.EnumMetadata; import org.modeshape.sequencer.javafile.metadata.FieldMetadata; import org.modeshape.sequencer.javafile.metadata.ImportMetadata; import org.modeshape.sequencer.javafile.metadata.JavaMetadata; import org.modeshape.sequencer.javafile.metadata.MethodMetadata; import org.modeshape.sequencer.javafile.metadata.TypeMetadata; /** * A source file recorder that writes the Java metadata from the source file to the repository, using the same structure as the * default mode of the Java Class File sequencer. */ public class ClassSourceFileRecorder implements SourceFileRecorder { /** * {@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 { JavaMetadata javaMetadata = JavaMetadata.instance(inputStream, length, encoding); record(context, outputNode, javaMetadata); } private void record( Sequencer.Context context, Node outputNode, JavaMetadata javaMetadata ) throws RepositoryException { String packageName = javaMetadata.getPackageMetadata().getName(); for (TypeMetadata typeMetadata : javaMetadata.getTypeMetadata()) { Node typeNode = getTypeNode(packageName, typeMetadata, outputNode); writeClassMetadata(context, typeNode, typeMetadata); writeImports(typeNode, javaMetadata.getImports()); } } private Node getTypeNode( String packageName, TypeMetadata typeMetadata, Node outputNode ) throws RepositoryException { final String[] packagePath = packageName.split("\\."); if (packageName.length() > 0) { for (final String pkg : packagePath) { outputNode = outputNode.addNode(pkg); outputNode.addMixin(PACKAGE); } } final Node classNode = outputNode.addNode(typeMetadata.getName()); final String actualType = typeMetadata.getType().equals(TypeMetadata.Type.ENUM) ? ENUM : CLASS; classNode.setPrimaryType(actualType); return classNode; } private void writeClassMetadata( Sequencer.Context context, Node typeNode, TypeMetadata typeMetadata ) throws RepositoryException { setTypeMetaInformation(context, typeNode, typeMetadata); List<MethodMetadata> methods = new ArrayList<MethodMetadata>(); List<MethodMetadata> ctors = new ArrayList<MethodMetadata>(); for (MethodMetadata method : typeMetadata.getMethods()) { if (method.getType() == MethodMetadata.Type.CONSTRUCTOR) { ctors.add(method); } else { methods.add(method); } } Node constructorsNode = typeNode.addNode(CONSTRUCTORS, CONSTRUCTORS); writeMethods(constructorsNode, ctors); Node methodsNode = typeNode.addNode(METHODS, METHODS); writeMethods(methodsNode, methods); Node fieldsNode = typeNode.addNode(FIELDS, FIELDS); writeFieldsNode(fieldsNode, typeMetadata.getFields()); writeAnnotationsNode(typeNode, typeMetadata.getAnnotations()); } private void setTypeMetaInformation( Sequencer.Context context, Node typeNode, TypeMetadata typeMetadata ) throws RepositoryException { /* - class:name (string) mandatory - class:superTypeName (string) - class:visibility (string) mandatory < 'public', 'protected', 'package', 'private' - class:abstract (boolean) mandatory - class:interface (boolean) mandatory - class:final (boolean) mandatory - class:strictFp (boolean) mandatory - class:interfaces (string) multiple + class:annotations (class:annotations) = class:annotations + class:constructors (class:constructors) = class:constructors + class:methods (class:methods) = class:methods + class:fields (class:fields) = class:fields */ typeNode.setProperty(NAME, typeMetadata.getName()); typeNode.setProperty(SEQUENCED_DATE, context.getTimestamp()); String superTypeName = typeMetadata.getSuperTypeName(); if (StringUtil.isBlank(superTypeName)) { superTypeName = Object.class.getCanonicalName(); } typeNode.setProperty(SUPER_CLASS_NAME, superTypeName); typeNode.setProperty(VISIBILITY, visibilityFor(typeMetadata).getDescription()); typeNode.setProperty(ABSTRACT, typeMetadata.hasAbstractModifier()); typeNode.setProperty(INTERFACE, typeMetadata.getType() == TypeMetadata.Type.INTERFACE); typeNode.setProperty(FINAL, typeMetadata.hasFinalModifier()); typeNode.setProperty(STRICT_FP, typeMetadata.hasStrictFPModifier()); typeNode.setProperty(INTERFACES, typeMetadata.getInterfaceNames().toArray(new String[0])); if (typeMetadata instanceof EnumMetadata) { typeNode.setProperty(ENUM_VALUES, ((EnumMetadata)typeMetadata).getValues().toArray(new String[0])); } } private Visibility visibilityFor( AbstractMetadata typeMetadata ) { if (typeMetadata.hasPublicVisibility()) { return Visibility.PUBLIC; } if (typeMetadata.hasProtectedVisibility()) { return Visibility.PROTECTED; } if (typeMetadata.hasPrivateVisibility()) { return Visibility.PRIVATE; } return Visibility.PACKAGE; } private void writeAnnotationsNode( Node rootNode, List<AnnotationMetadata> annotations ) throws RepositoryException { /* [class:annotationMember] - class:name (string) mandatory - class:value (string) [class:annotation] - class:name (string) mandatory + * (class:annotationMember) = class:annotationMember [class:annotations] + * (class:annotation) = class:annotation */ if (annotations.isEmpty()) { return; } Node annotationsContainer = rootNode.addNode(ANNOTATIONS, ANNOTATIONS); for (AnnotationMetadata annotationMetadata : annotations) { Node annotation = annotationsContainer.addNode(ANNOTATION, ANNOTATION); annotation.setProperty(NAME, annotationMetadata.getName()); for (Map.Entry<String, String> entry : annotationMetadata.getMemberValues().entrySet()) { String key = entry.getKey(); if (key == null) { key = "default"; } Node annotationMember = annotation.addNode(key, ANNOTATION_MEMBER); annotationMember.setProperty(NAME, key); annotationMember.setProperty(VALUE, entry.getValue()); } } } private void writeFieldsNode( Node fields, List<FieldMetadata> fieldsMetadata ) throws RepositoryException { /* [class:field] - class:name (string) mandatory - class:typeClassName (string) mandatory - class:visibility (string) mandatory < 'public', 'protected', 'package', 'private' - class:static (boolean) mandatory - class:final (boolean) mandatory - class:transient (boolean) mandatory - class:volatile (boolean) mandatory + class:annotations (class:annotations) = class:annotations [class:fields] + * (class:field) = class:field */ for (FieldMetadata fieldMetadata : fieldsMetadata) { Node field = fields.addNode(fieldMetadata.getName(), FIELD); field.setProperty(NAME, fieldMetadata.getName()); field.setProperty(TYPE_CLASS_NAME, fieldMetadata.getType()); field.setProperty(VISIBILITY, visibilityFor(fieldMetadata).getDescription()); field.setProperty(STATIC, fieldMetadata.hasStaticModifier()); field.setProperty(FINAL, fieldMetadata.hasFinalModifier()); field.setProperty(TRANSIENT, fieldMetadata.hasTransientModifier()); field.setProperty(VOLATILE, fieldMetadata.hasVolatileModifier()); writeAnnotationsNode(field, fieldMetadata.getAnnotations()); } } private void writeMethods( Node rootNode, List<MethodMetadata> methods ) throws RepositoryException { /* [class:method] - class:name (string) mandatory - class:returnTypeClassName (string) mandatory - class:visibility (string) mandatory < 'public', 'protected', 'package', 'private' - class:static (boolean) mandatory - class:final (boolean) mandatory - class:abstract (boolean) mandatory - class:strictFp (boolean) mandatory - class:native (boolean) mandatory - class:synchronized (boolean) mandatory - class:parameters (string) multiple // NO LONGER USED! + class:annotations (class:annotations) = class:annotations + class:methodParameters (class:parameters) = class:parameters */ for (MethodMetadata methodMetadata : methods) { Node method = rootNode.addNode(methodMetadata.getId(), METHOD); method.setProperty(NAME, methodMetadata.getName()); method.setProperty(RETURN_TYPE_CLASS_NAME, methodMetadata.getReturnTypeName()); method.setProperty(VISIBILITY, visibilityFor(methodMetadata).getDescription()); method.setProperty(STATIC, methodMetadata.hasStaticModifier()); method.setProperty(FINAL, methodMetadata.hasFinalModifier()); method.setProperty(ABSTRACT, methodMetadata.hasAbstractModifier()); method.setProperty(STRICT_FP, methodMetadata.hasStrictFPModifier()); method.setProperty(NATIVE, methodMetadata.hasNativeModifier()); method.setProperty(SYNCHRONIZED, methodMetadata.hasSynchronizedModifier()); writeParameters(method, methodMetadata.getParameters()); writeAnnotationsNode(method, methodMetadata.getAnnotations()); } } private void writeParameters( Node method, List<FieldMetadata> fieldsMetadata ) throws RepositoryException { // Always create the container node ... Node parametersContainer = method.addNode(METHOD_PARAMETERS, PARAMETERS); if (!fieldsMetadata.isEmpty()) { /* [class:parameters] + * (class:parameter) = class:parameter [class:parameter] - class:name (string) mandatory - class:typeClassName (string) mandatory - class:final (boolean) mandatory + class:annotations (class:annotations) = class:annotations */ for (FieldMetadata fieldMetadata : fieldsMetadata) { Node field = parametersContainer.addNode(fieldMetadata.getName(), PARAMETER); field.setProperty(NAME, fieldMetadata.getName()); field.setProperty(TYPE_CLASS_NAME, fieldMetadata.getType()); field.setProperty(FINAL, fieldMetadata.hasFinalModifier()); writeAnnotationsNode(field, fieldMetadata.getAnnotations()); } } } private void writeImports( final Node typeNode, final List<ImportMetadata> imports ) throws RepositoryException { if ((imports != null) && !imports.isEmpty()) { final String[] values = new String[imports.size()]; int i = 0; for (final ImportMetadata importMetadata : imports) { values[i++] = importMetadata.getName(); } typeNode.setProperty(IMPORTS, values); } } }