/** * Copyright (c) 2006-2011 Floggy Open Source Group. All rights reserved. * * 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 net.sourceforge.floggy.persistence.codegen; import java.util.Vector; import javassist.CannotCompileException; import javassist.ClassPool; import javassist.CtClass; import javassist.CtConstructor; import javassist.CtField; import javassist.CtNewConstructor; import javassist.CtNewMethod; import javassist.Modifier; import javassist.NotFoundException; import javassist.bytecode.AccessFlag; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import net.sourceforge.floggy.persistence.Configuration; import net.sourceforge.floggy.persistence.FloggyException; import net.sourceforge.floggy.persistence.IDable; import net.sourceforge.floggy.persistence.Weaver; import net.sourceforge.floggy.persistence.formatter.CodeFormatter; import net.sourceforge.floggy.persistence.impl.IndexMetadata; import net.sourceforge.floggy.persistence.impl.PersistableMetadata; /** * DOCUMENT ME! * * @author <a href="mailto:thiago.moreira@floggy.org">Thiago Moreira</a> * @version $Revision$ */ public abstract class CodeGenerator { private static final Log LOG = LogFactory.getLog(CodeGenerator.class); /** * DOCUMENT ME! */ protected ClassPool classPool; /** * DOCUMENT ME! */ protected Configuration configuration; /** Class to be modified; */ protected CtClass ctClass; /** * DOCUMENT ME! */ protected StringBuffer source; /** * Creates a new code generator for the class. * * @param ctClass * Class to be modified. * @param configuration * the configuration object */ public CodeGenerator(CtClass ctClass, ClassPool classPool, Configuration configuration) throws FloggyException, NotFoundException { this.ctClass = ctClass; this.classPool = classPool; this.configuration = configuration; } /** * Generate all the necessary source code for this class. * * @throws NotFoundException * @throws CannotCompileException */ public final void generateCode() throws NotFoundException, CannotCompileException { if (configuration.isGenerateSource()) { source = new StringBuffer(); } this.generateDefaultConstructor(); this.generateNonFinalFields(); this.generateSpecificMethods(); this.generatePersistableInterface(); this.generateGetIndexValueMethod(); } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public String getSource() { return source.toString(); } /** * DOCUMENT ME! * * @param ctClass DOCUMENT ME! * * @return DOCUMENT ME! * * @throws NotFoundException DOCUMENT ME! */ public boolean isIDable(CtClass ctClass) throws NotFoundException { CtClass idableClass = classPool.get(IDable.class.getName()); return ctClass.subtypeOf(idableClass); } /** * DOCUMENT ME! * * @param buffer DOCUMENT ME! * * @throws CannotCompileException DOCUMENT ME! */ protected final void addField(StringBuffer buffer) throws CannotCompileException { String temp = buffer.toString(); if (configuration.isGenerateSource()) { source.append(CodeFormatter.format(temp)); } try { ctClass.addField(CtField.make(temp, ctClass)); } catch (CannotCompileException ccex) { LOG.error("Adding field: \n" + CodeFormatter.format(temp)); throw ccex; } } /** * DOCUMENT ME! * * @param buffer DOCUMENT ME! * * @throws CannotCompileException DOCUMENT ME! */ protected final void addMethod(StringBuffer buffer) throws CannotCompileException { String temp = buffer.toString(); if (configuration.isGenerateSource()) { source.append(CodeFormatter.format(temp)); } try { ctClass.addMethod(CtNewMethod.make(temp, ctClass)); } catch (CannotCompileException ccex) { LOG.error("Adding method: \n" + CodeFormatter.format(temp)); throw ccex; } } /** * DOCUMENT ME! * * @throws NotFoundException DOCUMENT ME! * @throws CannotCompileException DOCUMENT ME! */ protected void generateDefaultConstructor() throws NotFoundException, CannotCompileException { CtConstructor constructor = null; try { constructor = ctClass.getConstructor("()V"); if (AccessFlag.PUBLIC != constructor.getModifiers()) { throw new CannotCompileException( "You must provide a public default constructor to class: " + ctClass.getName()); } } catch (NotFoundException e) { if (configuration.isAddDefaultConstructor()) { constructor = CtNewConstructor.defaultConstructor(ctClass); ctClass.addConstructor(constructor); } else { throw new CannotCompileException( "You must provide a public default constructor to class: " + ctClass.getName()); } } } /** * DOCUMENT ME! * * @throws CannotCompileException * @throws NotFoundException DOCUMENT ME! */ protected void generateGetIndexValueMethod() throws CannotCompileException, NotFoundException { PersistableMetadata metadata = configuration.getPersistableMetadata(ctClass.getName()); StringBuffer buffer = new StringBuffer(); buffer.append("public Object __getIndexValue(String indexName) {\n"); Vector indexMetadatas = metadata.getIndexMetadatas(); if (indexMetadatas != null) { int indexMetadatasSize = indexMetadatas.size(); for (int i = 0; i < indexMetadatasSize; i++) { IndexMetadata indexMetadata = (IndexMetadata) indexMetadatas.elementAt(i); buffer.append("if (\"" + indexMetadata.getName() + "\".equals(indexName)) {\n"); Vector fields = indexMetadata.getFields(); int fieldsSize = fields.size(); for (int j = 0; j < fieldsSize; j++) { String fieldName = (String) fields.get(j); CtClass fieldType = ctClass.getField(fieldName).getType(); if (fieldType.isPrimitive()) { buffer.append("return new " + PrimitiveTypeGenerator.getWrapperNameClass(fieldType) + "(this." + fieldName + ");\n"); } else { buffer.append("return this." + fieldName + ";\n"); } } buffer.append("}\n"); } } buffer.append("return null;\n"); buffer.append("}\n"); addMethod(buffer); } /** * DOCUMENT ME! * * @throws CannotCompileException * @throws NotFoundException DOCUMENT ME! */ protected void generateGetRecordStoreNameMethod() throws CannotCompileException, NotFoundException { try { ctClass.getDeclaredMethod("getRecordStoreName"); } catch (NotFoundException nfex) { StringBuffer buffer = new StringBuffer(); buffer.append("public String getRecordStoreName() {\n"); buffer.append("return \"" + configuration.getPersistableMetadata(ctClass.getName()) .getRecordStoreName() + "\";\n"); buffer.append("}\n"); addMethod(buffer); } } /** * DOCUMENT ME! */ protected void generateNonFinalFields() { CtField[] fields = null; fields = ctClass.getDeclaredFields(); if (fields != null) { for (int i = 0; i < fields.length; i++) { CtField field = fields[i]; if (Modifier.isFinal(field.getModifiers())) { field.setModifiers(field.getModifiers() & ~Modifier.FINAL); } } } } /** * DOCUMENT ME! * * @throws NotFoundException */ protected void generatePersistableInterface() throws NotFoundException { this.ctClass.addInterface(this.ctClass.getClassPool() .get(Weaver.__PERSISTABLE_CLASSNAME)); } /** * DOCUMENT ME! * * @throws NotFoundException DOCUMENT ME! * @throws CannotCompileException DOCUMENT ME! */ protected abstract void generateSpecificMethods() throws NotFoundException, CannotCompileException; /** * DOCUMENT ME! * * @param field DOCUMENT ME! * * @return DOCUMENT ME! */ protected boolean ignoreField(CtField field) { boolean ignore = false; if (field.getName().equals("__id") || field.getName().equals("__persistableMetadata")) { ignore = true; } int modifier = field.getModifiers(); if (Modifier.isTransient(modifier) || Modifier.isStatic(modifier)) { ignore = true; } return ignore; } }