/*
* 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.classfile;
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.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.JcrConstants;
import org.modeshape.jcr.api.sequencer.Sequencer;
import org.modeshape.sequencer.classfile.metadata.AnnotationMetadata;
import org.modeshape.sequencer.classfile.metadata.ClassMetadata;
import org.modeshape.sequencer.classfile.metadata.EnumMetadata;
import org.modeshape.sequencer.classfile.metadata.FieldMetadata;
import org.modeshape.sequencer.classfile.metadata.MethodMetadata;
import org.modeshape.sequencer.javafile.metadata.ImportMetadata;
public class DefaultClassFileRecorder implements ClassFileRecorder {
@Override
public void recordClass( Sequencer.Context context,
Node outputNode,
ClassMetadata classMetadata ) throws RepositoryException {
Node classNode = getClassNode(classMetadata, outputNode);
writeClassMetaInformation(context, classMetadata, classNode);
Node constructorsNode = classNode.addNode(CONSTRUCTORS, CONSTRUCTORS);
writeMethods(constructorsNode, classMetadata.getConstructors());
Node methodsNode = classNode.addNode(METHODS, METHODS);
writeMethods(methodsNode, classMetadata.getMethods());
Node fieldsNode = classNode.addNode(FIELDS, FIELDS);
writeFieldsNode(fieldsNode, classMetadata.getFields());
writeImports(classNode, classMetadata.getImports());
}
private void writeClassMetaInformation( Sequencer.Context context,
ClassMetadata classMetadata,
Node classNode ) throws RepositoryException {
/*
- class:name (string) mandatory
- class:superClassName (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
*/
classNode.setProperty(NAME, classMetadata.getClassName());
classNode.setProperty(SEQUENCED_DATE, context.getTimestamp());
String superClassName = classMetadata.getSuperclassName();
if (StringUtil.isBlank(superClassName)) {
superClassName = Object.class.getCanonicalName();
}
classNode.setProperty(SUPER_CLASS_NAME, superClassName);
classNode.setProperty(VISIBILITY, classMetadata.getVisibility().getDescription());
classNode.setProperty(ABSTRACT, classMetadata.isAbstract());
classNode.setProperty(INTERFACE, classMetadata.isInterface());
classNode.setProperty(FINAL, classMetadata.isFinal());
classNode.setProperty(STRICT_FP, classMetadata.isStrictFp());
classNode.setProperty(INTERFACES, classMetadata.getInterfaces());
if (ENUM.equalsIgnoreCase(classNode.getProperty(JcrConstants.JCR_PRIMARY_TYPE).getString())) {
classNode.setProperty(ENUM_VALUES, ((EnumMetadata)classMetadata).getValues().toArray(new String[0]));
}
}
private Node getClassNode( ClassMetadata classMetadata,
Node outputNode ) throws RepositoryException {
final String[] packagePath = classMetadata.getClassName().split("\\.");
int i = 0;
// create a series of nt:unstructured nodes as the package path
if (packagePath.length > 1) {
for (final int numPkgs = (packagePath.length - 1); i < numPkgs; ++i) {
outputNode = outputNode.addNode(packagePath[i]);
outputNode.addMixin(PACKAGE);
}
}
final Node classNode = outputNode.addNode(packagePath[i]);
final String actualType = (classMetadata.isEnumeration() ? ENUM : CLASS);
classNode.setPrimaryType(actualType);
return classNode;
}
private void writeFieldsNode( Node fieldsNode,
List<FieldMetadata> fields ) 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
*/
for (FieldMetadata field : fields) {
Node fieldNode = fieldsNode.addNode(field.getName(), FIELD);
fieldNode.setProperty(NAME, field.getName());
fieldNode.setProperty(TYPE_CLASS_NAME, field.getTypeName());
fieldNode.setProperty(VISIBILITY, field.getVisibility().getDescription());
fieldNode.setProperty(STATIC, field.isStatic());
fieldNode.setProperty(FINAL, field.isFinal());
fieldNode.setProperty(TRANSIENT, field.isTransient());
fieldNode.setProperty(VOLATILE, field.isVolatile());
writeAnnotationsNode(fieldNode, field.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
+ class:methodParameters (class:parameters) = class:parameters
+ class:annotations (class:annotations) = class:annotations
[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 (MethodMetadata method : methods) {
Node methodNode = rootNode.addNode(method.getId(), METHOD);
methodNode.setProperty(NAME, method.getName());
methodNode.setProperty(RETURN_TYPE_CLASS_NAME, method.getReturnType());
methodNode.setProperty(VISIBILITY, method.getVisibility().getDescription());
methodNode.setProperty(STATIC, method.isStatic());
methodNode.setProperty(FINAL, method.isFinal());
methodNode.setProperty(ABSTRACT, method.isAbstract());
methodNode.setProperty(STRICT_FP, method.isStrictFp());
methodNode.setProperty(NATIVE, method.isNative());
methodNode.setProperty(SYNCHRONIZED, method.isSynchronized());
Node parametersNode = methodNode.addNode(METHOD_PARAMETERS, PARAMETERS);
int num = 0;
for (String paramType : method.getParameters()) {
String paramName = "p" + (++num); // not included in bytecode
Node field = parametersNode.addNode(paramName, PARAMETER);
field.setProperty(NAME, paramName);
field.setProperty(TYPE_CLASS_NAME, paramType);
field.setProperty(FINAL, false);
}
writeAnnotationsNode(methodNode, method.getAnnotations());
}
}
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 annotationsNode = rootNode.addNode(ANNOTATIONS, ANNOTATIONS);
for (AnnotationMetadata annotation : annotations) {
Node annotationNode = annotationsNode.addNode(ANNOTATION, ANNOTATION);
annotationNode.setProperty(NAME, annotation.getAnnotationClassName());
for (Map.Entry<String, String> entry : annotation.getMemberValues().entrySet()) {
Node annotationMember = annotationNode.addNode(entry.getKey(), ANNOTATION_MEMBER);
annotationMember.setProperty(NAME, entry.getKey());
annotationMember.setProperty(VALUE, entry.getValue());
}
}
}
private void writeImports( final Node classNode,
final List<ImportMetadata> imports ) throws RepositoryException {
assert (classNode != null);
assert (imports != null);
if (!imports.isEmpty()) {
final String[] values = new String[imports.size()];
int i = 0;
for (final ImportMetadata imported : imports) {
values[i++] = imported.getName();
}
classNode.setProperty(IMPORTS, values);
}
}
}