Author: tchemit Date: 2011-05-15 21:03:09 +0200 (Sun, 15 May 2011) New Revision: 2282 Url: http://nuiton.org/repositories/revision/topia/2282 Log: Evolution #610: Merge all transformers for DAO in one unique Transformer Added: trunk/topia-persistence/src/main/java/org/nuiton/topia/generator/EntityDAOTransformer.java Modified: trunk/topia-persistence/src/main/java/org/nuiton/topia/generator/TopiaGeneratorUtil.java trunk/topia-persistence/src/main/java/org/nuiton/topia/generator/TopiaMetaTransformer.java Added: trunk/topia-persistence/src/main/java/org/nuiton/topia/generator/EntityDAOTransformer.java =================================================================== --- trunk/topia-persistence/src/main/java/org/nuiton/topia/generator/EntityDAOTransformer.java (rev 0) +++ trunk/topia-persistence/src/main/java/org/nuiton/topia/generator/EntityDAOTransformer.java 2011-05-15 19:03:09 UTC (rev 2282) @@ -0,0 +1,1103 @@ +/* + * #%L + * ToPIA :: Persistence + * + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2011 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.generator; + +/*{generator option: parentheses = false}*/ +/*{generator option: writeString = +}*/ + +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.eugene.GeneratorUtil; +import org.nuiton.eugene.java.ObjectModelTransformerToJava; +import org.nuiton.eugene.models.object.ObjectModel; +import org.nuiton.eugene.models.object.ObjectModelAssociationClass; +import org.nuiton.eugene.models.object.ObjectModelAttribute; +import org.nuiton.eugene.models.object.ObjectModelClass; +import org.nuiton.eugene.models.object.ObjectModelClassifier; +import org.nuiton.eugene.models.object.ObjectModelDependency; +import org.nuiton.eugene.models.object.ObjectModelInterface; +import org.nuiton.eugene.models.object.ObjectModelModifier; +import org.nuiton.eugene.models.object.ObjectModelOperation; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.persistence.TopiaDAO; +import org.nuiton.topia.persistence.TopiaDAOLegacy; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.util.StringUtil; + +import java.security.Permission; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * To generate all <code>DAO</code> related classes for a given entity. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.5.4 + * @plexus.component role="org.nuiton.eugene.Template" role-hint="org.nuiton.topia.generator.EntityDAOTransformer" + */ +public class EntityDAOTransformer extends ObjectModelTransformerToJava { + + /** Logger. */ + private static final Log log = + LogFactory.getLog(EntityDAOTransformer.class); + + /** + * map of direct usages (values) for each entity (key). + * <p/> + * This map is used to generate the findUsages methods for DAOAbstract. + */ + protected Map<ObjectModelClass, Set<ObjectModelClass>> usages; + + /** + * All entities fqn of the model (used to detect if an attribute is not + * an entity). + */ + Set<String> allEntitiesFqn; + + /** + * The class of abstract dao to use. + * @since 2.5 + */ + protected Class<?> daoImplementation; + + /** + * Map of extra operations for DAO. The key of the map is the qualified + * name of the entity relative to the DAO. + */ + Map<String, Collection<ObjectModelOperation>> extraOperations = + new HashMap<String, Collection<ObjectModelOperation>>(); + + @Override + public void transformFromModel(ObjectModel model) { + + usages = TopiaGeneratorUtil.searchDirectUsages(model); + boolean extendLegacyDAO = + Boolean.valueOf(model.getTagValue(TopiaTagValues.TAG_USE_LEGACY_DAO)); + if (extendLegacyDAO) { + log.warn("Using a deprecated tag value " + + TopiaTagValues.TAG_USE_LEGACY_DAO + + ", prefer use the tag value " + + TopiaTagValues.TAG_DAO_IMPLEMENTATION); + daoImplementation = TopiaDAOLegacy.class; + } else { + daoImplementation = + TopiaGeneratorUtil.getDAOImplementation(model); + } + + // keep all classifiers on the model which are entities + List<ObjectModelClass> allEntities = + TopiaGeneratorUtil.getEntityClasses(model, true); + allEntitiesFqn = new HashSet<String>(allEntities.size()); + for (ObjectModelClass entity : allEntities) { + String fqn = entity.getQualifiedName(); + allEntitiesFqn.add(fqn); + Collection<ObjectModelOperation> daoOperations = + new HashSet<ObjectModelOperation>(); + for (ObjectModelOperation op : entity.getOperations()) { + if (TopiaGeneratorUtil.hasDaoStereotype(op)) { + daoOperations.add(op); + } + } + + if (daoOperations.isEmpty()) { + + // found some dao operations + extraOperations.put(fqn, daoOperations); + } + } + } + + @Override + public void transformFromInterface(ObjectModelInterface interfacez) { + if (!TopiaGeneratorUtil.hasDaoStereotype(interfacez)) { + return; + } + + /** + * EVO #636 : Manage extra operations for DAO from "dao" dependency + * between an interface with stereotype <<dao>> (dependency client) and + * a class with stereotype <<entity>> (dependency supplier). + */ + + ObjectModelDependency dependency = + interfacez.getDependency(TopiaGeneratorUtil.DEPENDENCIES_DAO); + + if (dependency == null) { + if (log.isWarnEnabled()) { + log.warn("Could not find dependency " + + TopiaGeneratorUtil.DEPENDENCIES_DAO + + " but DAO stereotype was placed on the interface " + + interfacez.getName()); + } + return; + } + + ObjectModelClassifier classifier = dependency.getSupplier(); + + if (!TopiaGeneratorUtil.hasEntityStereotype(classifier)) { + + // dependency supplier is not an entity... + if (log.isWarnEnabled()) { + log.warn("Dependency supplier " + + classifier.getQualifiedName() + + " is not an entity."); + } + return; + } + + // keep only direct operations + Collection<ObjectModelOperation> operations = + interfacez.getOperations(); + + if (CollectionUtils.isEmpty(operations)) { + + // no operations on interface, this is not normal + if (log.isWarnEnabled()) { + log.warn("No operation found on interface with DAO " + + "stereotype "+classifier.getQualifiedName()); + } + return; + } + if (log.isDebugEnabled()) { + log.debug("add "+operations.size()+" extra operation(s) for DAO"); + } + + extraOperations.put(classifier.getQualifiedName(), operations); + } + + @Override + public void transformFromClass(ObjectModelClass clazz) { + if (!TopiaGeneratorUtil.hasEntityStereotype(clazz)) { + return; + } + String clazzName = clazz.getName(); + String clazzFQN = clazz.getQualifiedName(); + + // generate DAO + generateDAOClass(clazz, clazzName, clazzFQN); + + // generate DAOImpl + generateDAOImpl(clazz, clazzName, clazzFQN); + + // generate DAOAbstract + generateDAOAbstract(clazz, clazzName, clazzFQN); + } + + protected void generateDAOClass(ObjectModelClass clazz, String clazzName, String clazzFQN) { + ObjectModelClass daoClass = createClass(clazzName + "DAO", clazz.getPackageName()); + setDocumentation(daoClass, "/**\n" + + " * Cette classe etend le DAOImpl pour parametrer la classe avec le bon type\n" + + " * Cette classe est marque finale car l'heritage entre les DAO se fait\n" + + " * sur les DOAImpl, c-a-d que DAOAbstract peut etendre le DAOImpl\n" + + " */"); + setSuperClass(daoClass, clazzFQN + "DAOImpl<" + clazzName + ">"); + } + + protected void generateDAOImpl(ObjectModelClass clazz, + String clazzName, + String clazzFQN) { + + Collection<ObjectModelOperation> moreOperations = + extraOperations.get(clazz.getQualifiedName()); + + if (CollectionUtils.isEmpty(moreOperations)) { + + // no business dao found, can safely generate the daoImpl class + + ObjectModelClass daoImplClass = createClass(clazzName + "DAOImpl<E extends " + clazzName + ">", clazz.getPackageName()); + setDocumentation(daoImplClass, "/**\n" + + " Implantation du DAO pour l'entité " + clazzName + ".\n" + + " * L'utilisateur peut remplacer cette classe par la sienne en la mettant \n" + + " * simplement dans ces sources. Cette classe générée sera alors simplement\n" + + " * écrasée\n" + + " */"); + setSuperClass(daoImplClass, clazzFQN + "DAOAbstract<E>"); + } + } + + protected void generateDAOAbstract(ObjectModelClass clazz, + String clazzName, + String clazzFQN) { + ObjectModelClass daoAbstractClass = createAbstractClass( + clazzName + "DAOAbstract<E extends " + clazzName + '>', + clazz.getPackageName()); + + // super class + + String extendClass = ""; + for (ObjectModelClass parent : clazz.getSuperclasses()) { + extendClass = parent.getQualifiedName(); + if (TopiaGeneratorUtil.hasEntityStereotype(parent)) { + extendClass += "DAOImpl<E>"; + // in java no multi-inheritance + break; + } + } + if (extendClass.length() == 0) { + extendClass = daoImplementation.getName() + "<E>"; + } + if (log.isDebugEnabled()) { + log.debug("super class = " + extendClass); + } + setSuperClass(daoAbstractClass, extendClass); + + addInterface(daoAbstractClass, TopiaDAO.class.getName() + "<E>"); + + String prefix = getConstantPrefix(clazz, ""); + setConstantPrefix(prefix); + + // imports + + Collection<ObjectModelOperation> DAOoperations = + getDAOOperations(clazz); + + if (TopiaGeneratorUtil.isCollectionNeeded(DAOoperations)) { + addImport(daoAbstractClass, Collection.class); + } + if (TopiaGeneratorUtil.isSetNeeded(DAOoperations)) { + addImport(daoAbstractClass, Set.class); + } + addImport(daoAbstractClass, List.class); + addImport(daoAbstractClass, Arrays.class); + addImport(daoAbstractClass, TopiaException.class); + addImport(daoAbstractClass, TopiaContextImplementor.class); + + boolean enableSecurity = TopiaGeneratorUtil.isClassWithSecurity(clazz); + + if (enableSecurity) { + addImport(daoAbstractClass, ArrayList.class); + addImport(daoAbstractClass, Permission.class); + addImport(daoAbstractClass, "org.nuiton.topia.taas.entities.TaasAuthorizationImpl"); + addImport(daoAbstractClass, "org.nuiton.topia.taas.jaas.TaasPermission"); + addImport(daoAbstractClass, "org.nuiton.topia.taas.TaasUtil"); + addImport(daoAbstractClass, TopiaDAO.class); + + //FIXME : how to do static imports ? +//import static org.nuiton.topia.taas.TaasUtil.CREATE; +//import static org.nuiton.topia.taas.TaasUtil.DELETE; +//import static org.nuiton.topia.taas.TaasUtil.LOAD; +//import static org.nuiton.topia.taas.TaasUtil.UPDATE; + } + + ObjectModelOperation op; + + // getEntityClass + + op = addOperation(daoAbstractClass, + "getEntityClass", + "Class<E>", + ObjectModelModifier.PUBLIC); + setOperationBody(op, "" +/*{ + return (Class<E>)<%=clazzName%>.class; + }*/ + ); + + generateDAOOperations(daoAbstractClass, DAOoperations); + + generateDelete(clazz, daoAbstractClass); + + generateNaturalId(daoAbstractClass, clazz); + + for (ObjectModelAttribute attr : clazz.getAttributes()) { + if (!attr.isNavigable()) { + continue; + } + + if (!GeneratorUtil.isNMultiplicity(attr)) { + generateNoNMultiplicity(clazzName, daoAbstractClass, attr, false); + } else { + generateNMultiplicity(clazzName, daoAbstractClass, attr); + } + } + + if (clazz instanceof ObjectModelAssociationClass) { + ObjectModelAssociationClass assocClass = + (ObjectModelAssociationClass) clazz; + for (ObjectModelAttribute attr : assocClass.getParticipantsAttributes()) { + if (attr != null) { + if (!GeneratorUtil.isNMultiplicity(attr)) { + generateNoNMultiplicity(clazzName, daoAbstractClass, attr, true); + } else { + generateNMultiplicity(clazzName, daoAbstractClass, attr); + } + } + } + } + + if (enableSecurity) { + + // getRequestPermission + + op = addOperation(daoAbstractClass, + "getRequestPermission", + "List<Permission>", + ObjectModelModifier.PUBLIC); + setDocumentation(op, "Retourne les permissions a verifier pour " + + "l'acces a l'entite pour le service Taas"); + addException(op, TopiaException.class); + addParameter(op, String.class, "topiaId"); + addParameter(op, int.class, "actions"); + StringBuilder buffer = new StringBuilder(); + buffer.append("" +/*{ + List<Permission> resultPermissions = new ArrayList<Permission>(); + if ((actions & TaasUtil.CREATE) == TaasUtil.CREATE) { +}*/ + ); + buffer.append(generateSecurity(daoAbstractClass, clazz, + TopiaGeneratorUtil.getSecurityCreateTagValue(clazz))); + buffer.append("" +/*{ + } + if ((actions & TaasUtil.LOAD) == TaasUtil.LOAD) { +}*/ + ); + buffer.append(generateSecurity(daoAbstractClass, clazz, + TopiaGeneratorUtil.getSecurityLoadTagValue(clazz))); + buffer.append("" +/*{ + } + if ((actions & TaasUtil.UPDATE) == TaasUtil.UPDATE) { +}*/ + ); + buffer.append(generateSecurity(daoAbstractClass, clazz, + TopiaGeneratorUtil.getSecurityUpdateTagValue(clazz))); + buffer.append("" +/*{ + } + if ((actions & TaasUtil.DELETE) == TaasUtil.DELETE) { +}*/ + ); + buffer.append(generateSecurity(daoAbstractClass, clazz, + TopiaGeneratorUtil.getSecurityDeleteTagValue(clazz))); + buffer.append("" +/*{ + } + return resultPermissions; + }*/ + ); + + setOperationBody(op, buffer.toString()); + + // THIMEL : Le code suivant doit pouvoir être déplacé dans DAODelegator ? + + // getRequestPermission + + + op = addOperation(daoAbstractClass, + "getRequestPermission", + "List<Permission>", + ObjectModelModifier.PROTECTED); + addParameter(op, String.class, "topiaId"); + addParameter(op, int.class, "actions"); + addParameter(op, String.class, "query"); + addParameter(op, Class.class, "daoClass"); + addException(op, TopiaException.class); + setDocumentation(op, "Retourne les permissions a verifier pour " + + "l'acces a l'entite pour le service Taas"); + setOperationBody(op, "" +/*{ TopiaContextImplementor context = getContext(); + List<String> result = context.find(query, "id", topiaId); + + List<Permission> resultPermissions = new ArrayList<Permission>(); + for (String topiaIdPermission : result) { + TopiaDAO dao = context.getDAO(daoClass); + List<Permission> permissions = dao.getRequestPermission(topiaIdPermission, actions); + if(permissions != null) { + resultPermissions.addAll(permissions); + } else { + TaasPermission permission = new TaasPermission(topiaIdPermission, actions); + resultPermissions.add(permission); + } + } + return resultPermissions; + }*/ + ); + } + + Set<ObjectModelClass> usagesForclass = usages.get(clazz); + generateFindUsages(clazz, daoAbstractClass, usagesForclass); + } + + protected void generateDelete(ObjectModelClass clazz, + ObjectModelClass result) { + ObjectModelOperation op; + op = addOperation(result, "delete", "void", ObjectModelModifier.PUBLIC); + addException(op, TopiaException.class); + addParameter(op, "E", "entity"); + StringBuilder body = new StringBuilder(); + String modelName = StringUtils.capitalize(model.getName()); + String providerFQN = getOutputProperties().getProperty( + PROP_DEFAULT_PACKAGE) + '.' + modelName + + "DAOHelper.getImplementationClass"; + + for (ObjectModelAttribute attr : clazz.getAttributes()) { + + String attrType = GeneratorUtil.getSimpleName(attr.getType()); + + String reverseAttrName = attr.getReverseAttributeName(); + ObjectModelAttribute reverse = attr.getReverseAttribute(); + if (attr.hasAssociationClass() || + reverse == null || !reverse.isNavigable()) { + + // never treate a non reverse and navigable attribute + // never treate an association class attribute + continue; + } + + // at this point we are sure to have a attribute which is + // - reverse + // - navigable + // - not from an association class + if (!allEntitiesFqn.contains(attr.getType())) { + + // this attribute is not from an entity, don't treate it + if (log.isDebugEnabled()) { + log.debug("[" + result.getName() + "] Skip attribute [" + + attr.getName() + "] with type " + attr.getType()); + } + continue; + } + + // At this point, the attribute type is a entity + if (GeneratorUtil.isNMultiplicity(attr) && + GeneratorUtil.isNMultiplicity(reverse)) { + // On doit absolument supprimer pour les relations many-to-many + // le this de la collection de l'autre cote + + String attrDBName = TopiaGeneratorUtil.getDbName(attr); + String attrClassifierDBName = TopiaGeneratorUtil.getDbName(attr.getClassifier()); + String attrJoinTableName = TopiaGeneratorUtil.getManyToManyTableName(attr); + String attrReverseDBName = TopiaGeneratorUtil.getReverseDbName(attr); + + //FIXME_-FC-20100413 Use a TopiaQuery (use HQLin elements) +// // Add DAOHelper +// String daoHelper = modelName + "DAOHelper"; +// String daoHelperFQN = getOutputProperties(). +// getProperty(PROP_DEFAULT_PACKAGE) + '.' + daoHelper; +// addImport(result, daoHelperFQN); +// +// // Add import for TopiaQuery +// addImport(result, TopiaQuery.class); +// +// // Entity DAO and reversePropertyName +// String entityDAO = attrType + "DAO"; +// String reverseAttrNameProperty = +// attrType + "." + getConstantName(reverseAttrName); +// +// +// <%=entityDAO%> dao = <%=daoHelper%>.get<%=entityDAO%>(getContext()); +// TopiaQuery query = dao.createQuery("B"). +// addFrom(entity.getClass(), "A"). +// add("A", entity). +// addInElements("A", "B." + <%=reverseAttrNameProperty%>); +// +// System.out.println("Query : " + query); +// List<<%=attrType%>> list = dao.findAllByQuery(query); + + + body.append("" +/*{ + { + List<<%=attrType%>> list = getContext().getHibernate().createSQLQuery( + "SELECT main.topiaid " + + "from <%=attrClassifierDBName%> main, <%=attrJoinTableName%> secondary " + + "where main.topiaid=secondary.<%=attrDBName%>" + + " and secondary.<%=attrReverseDBName%>='" + entity.getTopiaId() + "'") + .addEntity("main", <%=providerFQN%>(<%=attrType%>.class)).list(); + + for (<%=attrType%> item : list) { + item.remove<%=StringUtils.capitalize(reverseAttrName)%>(entity); + } + } +}*/ + ); + } else if (!GeneratorUtil.isNMultiplicity(reverse)) { + // On doit mettre a null les attributs qui ont cet objet sur les + // autres entites en one-to-* + // TODO peut-etre qu'hibernate est capable de faire ca tout seul ? + // THIMEL: J'ai remplacé reverse.getName() par reverseAttrName sans certitude + builder.addImport(result, attrType); + String attrSimpleType = TopiaGeneratorUtil.getClassNameFromQualifiedName(attrType); + + body.append("" + /*{ + { + List<<%=attrSimpleType%>> list = getContext() + .getDAO(<%=attrSimpleType%>.class) + .findAllByProperties(<%=attrSimpleType%>.<%=getConstantName(reverseAttrName)%>, entity); + for (<%=attrSimpleType%> item : list) { + item.set<%=StringUtils.capitalize(reverseAttrName)%>(null); + }*/ + ); + if (attr.isAggregate()) { + body.append("" +/*{ + getContext().getDAO(<%=attrSimpleType%>.class).delete(item); + //item.delete(); +}*/ + ); + } + body.append("" +/*{ + } + } +}*/ + ); + + } + } + body.append("" +/*{ + super.delete(entity); + }*/ + ); + + setOperationBody(op, body.toString()); + } + + protected void generateFindUsages(ObjectModelClass clazz, + ObjectModelClass result, + Set<ObjectModelClass> usagesForclass) { + + builder.addImport(result, ArrayList.class.getName()); + builder.addImport(result, Map.class.getName()); + builder.addImport(result, HashMap.class.getName()); + builder.addImport(result, TopiaEntity.class.getName()); + + if (clazz instanceof ObjectModelAssociationClass || + usagesForclass.isEmpty()) { + // not for an association class + // just let a null method + ObjectModelOperation operation; + operation = addOperation(result, + "findUsages", + "<U extends TopiaEntity> List<U>", + ObjectModelModifier.PUBLIC); + + addParameter(operation, "Class<U>", "type"); + addParameter(operation, "E", "entity"); + addException(operation, TopiaException.class); + addAnnotation(result, operation, "Override"); + setOperationBody(operation, "" +/*{ + return new ArrayList<U>(); + }*/ + ); + + operation = addOperation(result, + "findAllUsages", + "Map<Class<? extends TopiaEntity>, List<? extends TopiaEntity>>", + ObjectModelModifier.PUBLIC); + + addParameter(operation, "E", "entity"); + addException(operation, TopiaException.class); + addAnnotation(result, operation, "Override"); + setOperationBody(operation, "" +/*{ + return new HashMap<Class<? extends TopiaEntity>, List<? extends TopiaEntity>>(); + }*/ + ); + + return; + } + List<ObjectModelClass> allEntities; + Map<String, ObjectModelClass> allEntitiesByFQN; + + allEntities = TopiaGeneratorUtil.getEntityClasses(model, true); + allEntitiesByFQN = new TreeMap<String, ObjectModelClass>(); + + // prepare usages map and fill allEntitiesByFQN map + for (ObjectModelClass klass : allEntities) { + allEntitiesByFQN.put(klass.getQualifiedName(), klass); + } + + ObjectModelOperation operation; + operation = addOperation(result, + "findUsages", + "<U extends TopiaEntity> List<U>", + ObjectModelModifier.PUBLIC); + + addParameter(operation, "Class<U>", "type"); + addParameter(operation, "E", "entity"); + addException(operation, TopiaException.class); + addAnnotation(result, operation, "Override"); + StringBuilder buffer = new StringBuilder(300); + buffer.append("" +/*{ + List<?> result = new ArrayList(); + List tmp; +}*/ + ); + + for (ObjectModelClass usageClass : usagesForclass) { + String usageType = usageClass.getQualifiedName(); + builder.addImport(result, usageType); + String usageSimpleType = + TopiaGeneratorUtil.getClassNameFromQualifiedName(usageType); + String usageSimplePropertyMethod = "findAllBy" + usageSimpleType; + String usageCollectionPropertyMethod = "findAllContaining" + usageSimpleType; + for (ObjectModelAttribute attr : usageClass.getAttributes()) { + if (!attr.isNavigable()) { + // skip this case + continue; + } + String type; + String attrName = attr.getName(); + if (attr.hasAssociationClass()) { + //FIXME-TC20100224 dont known how to do this ? + continue; +// type = attr.getAssociationClass().getQualifiedName(); +// //FIXME-TC20100224 : this is crazy ??? must find the good name +// // Perhaps need to make different cases? +// attrName = attrName + "_" + TopiaGeneratorUtil.toLowerCaseFirstLetter(attr.getAssociationClass().getName()); + } else { + type = attr.getType(); + } + if (!allEntitiesByFQN.containsKey(type)) { + // not a entity, can skip for this attribute + continue; + } + ObjectModelClass targetEntity = allEntitiesByFQN.get(type); +// if (!type.equals(clazz.getQualifiedName())) { + if (!targetEntity.equals(clazz)) { + // not a good attribute reference + continue; + } + // found something to seek + + String methodNameSuffix = StringUtils.capitalize(attrName); + String methodName; + if (TopiaGeneratorUtil.isNMultiplicity(attr)) { + methodName = "findAllContains" + methodNameSuffix; + } else { + methodName = "findAllBy" + methodNameSuffix; + } + String daoName = StringUtils.capitalize(usageSimpleType) + "DAO"; + + builder.addImport(result, usageClass.getPackageName() + '.' + daoName); + + buffer.append("" +/*{ + if (type == <%=usageSimpleType%>.class) { + <%=daoName%> dao = (<%=daoName%>) + getContext().getDAO(<%=usageSimpleType%>.class); + tmp = dao.<%=methodName%>(entity); + result.addAll(tmp); + } +}*/ + ); + } + } + + buffer.append("" +/*{ + return (List<U>) result; + }*/ + ); + setOperationBody(operation, buffer.toString()); + + operation = addOperation(result, + "findAllUsages", + "Map<Class<? extends TopiaEntity>, List<? extends TopiaEntity>>", + ObjectModelModifier.PUBLIC); + + addParameter(operation, "E", "entity"); + addException(operation, TopiaException.class); + addAnnotation(result, operation, "Override"); + + buffer = new StringBuilder(300); + buffer.append("" +/*{ + Map<Class<? extends TopiaEntity>,List<? extends TopiaEntity>> result; + result = new HashMap<Class<? extends TopiaEntity>, List<? extends TopiaEntity>>(<%=usagesForclass.size()%>); + + List<? extends TopiaEntity> list; +}*/ + ); + for (ObjectModelClass usageClass : usagesForclass) { + + String fqn = usageClass.getName(); + buffer.append("" +/*{ + list = findUsages(<%=fqn%>.class, entity); + if (!list.isEmpty()) { + result.put(<%=fqn%>.class, list); + } +}*/ + ); + + } + buffer.append("" +/*{ + return result; + }*/ + ); + + setOperationBody(operation, buffer.toString()); + } + + /** + * Generation of DAO operations signatures from class. These operations are + * abstract and identified by <<dao>> stereotype in the model. The + * developper must defined these methods in the DAOImpl associated to this + * DAOAbstract. + * + * @param result clazz where to add operations + * @param operations operations to generate + */ + private void generateDAOOperations(ObjectModelClass result, + Collection<ObjectModelOperation> + operations) { + if (CollectionUtils.isEmpty(operations)) { + + // no extra operations to generate + return; + } + + for (ObjectModelOperation op : operations) { + + Set<String> exceptions = op.getExceptions(); + exceptions.add(TopiaException.class.getName()); + cloneOperation(op, + result, + true, + ObjectModelModifier.ABSTRACT, + ObjectModelModifier.toValue(op.getVisibility()) + ); + +// ObjectModelOperation op2; +// op2 = addOperation(result, +// op.getName(), +// op.getReturnType(), +// ObjectModelModifier.ABSTRACT, +// ObjectModelModifier.toValue(op.getVisibility())); +// setDocumentation(op2, op.getDocumentation()); +// +// // parameters +// +// for (ObjectModelParameter param : op.getParameters()) { +// ObjectModelParameter param2 = addParameter(op2, +// param.getType(), param.getName()); +// setDocumentation(param2, param.getDocumentation()); +// } +// +// // exceptions +// Set<String> exceptions = op.getExceptions(); +// exceptions.add(TopiaException.class.getName()); +// for (String exception : exceptions) { +// addException(op2, exception); +// } + } + } + + + + + private String generateSecurity(ObjectModelClass result, + ObjectModelClass clazz, + String tagValue) { + StringBuilder buffer = new StringBuilder(); + + if (StringUtils.isNotEmpty(tagValue)) { + String security = tagValue; + Pattern propertiesPattern = Pattern + .compile("((?:[_a-zA-Z0-9]+\\.)+(?:_?[A-Z][_a-zA-Z0-9]*\\.)+)attribute\\.(?:([_a-z0-9][_a-zA-Z0-9]*))#(?:(create|load|update|delete))"); + String[] valuesSecurity = security.split(":"); + + for (String valueSecurity : valuesSecurity) { + Matcher matcher = propertiesPattern.matcher(valueSecurity); + matcher.find(); + // className is fully qualified name of class + String className = matcher.group(1); + className = StringUtil.substring(className, 0, -1); // remove ended + // . + // target is class, attribute or operation + String attributeName = matcher.group(2); + String actions = matcher.group(3).toUpperCase(); + + String query = ""; + String daoClass = ""; + if (className.equals(clazz.getQualifiedName())) { + query = "select " + attributeName + ".topiaId from " + clazz.getQualifiedName() + " where topiaId = :id"; + daoClass = clazz.getAttribute(attributeName).getClassifier().getQualifiedName(); + } else { + query = "select at.topiaId from " + className + " at inner join at." + attributeName + " cl where cl.topiaId = :id"; + daoClass = className; + } + buffer.append("" +/*{ + resultPermissions.addAll(getRequestPermission(topiaId, + <%=actions%>, + "<%=query%>", + <%=daoClass%>.class)); +}*/ + ); + } + } else { + buffer.append("" +/*{ return null; + }*/ + ); + } + return buffer.toString(); + } + + protected void generateNoNMultiplicity(String clazzName, + ObjectModelClass result, + ObjectModelAttribute attr, + boolean isAssoc) { + String attrName = attr.getName(); + String attrType = attr.getType(); + String propertyName = attrName; + if (!isAssoc && attr.hasAssociationClass()) { + propertyName = TopiaGeneratorUtil.toLowerCaseFirstLetter( + attr.getAssociationClass().getName()) + '.' + propertyName; + } + ObjectModelOperation op; + op = addOperation(result, + "findBy" + StringUtils.capitalize(attrName), + "E", + ObjectModelModifier.PUBLIC); + addException(op, TopiaException.class); + addParameter(op, attrType, "v"); + setDocumentation(op, "Retourne le premier élément trouvé ayant comme valeur pour l'attribut " + attrName + " le paramètre."); + setOperationBody(op, "" +/*{ + E result = findByProperty(<%=clazzName + "." + getConstantName(propertyName)%>, v); + return result; + }*/ + ); + + op = addOperation(result, + "findAllBy" + StringUtils.capitalize(attrName), + "List<E>", + ObjectModelModifier.PUBLIC); + addException(op, TopiaException.class); + addParameter(op, attrType, "v"); + setDocumentation(op, "Retourne les éléments ayant comme valeur pour " + + "l'attribut " + attrName + " le paramètre."); + setOperationBody(op, "" +/*{ + List<E> result = findAllByProperty(<%=clazzName + "." + getConstantName(propertyName)%>, v); + return result; + }*/ + ); + + if (attr.hasAssociationClass()) { + String assocClassName = attr.getAssociationClass().getName(); + String assocClassFQN = attr.getAssociationClass().getQualifiedName(); + op = addOperation(result, + "findBy" + StringUtils.capitalize(assocClassName), + "E", + ObjectModelModifier.PUBLIC); + addException(op, TopiaException.class); + addParameter(op, assocClassFQN, "value"); + setDocumentation(op, "Retourne le premier élément trouvé ayant " + + "comme valeur pour l'attribut " + + TopiaGeneratorUtil.toLowerCaseFirstLetter(assocClassName) + + " le paramètre."); + setOperationBody(op, "" +/*{ + E result = findByProperty(<%=clazzName + "." + getConstantName(TopiaGeneratorUtil.toLowerCaseFirstLetter(assocClassName))%>, value); + return result; + }*/ + ); + + op = addOperation(result, + "findAllBy" + StringUtils.capitalize(assocClassName), + "List<E>", + ObjectModelModifier.PUBLIC); + addException(op, TopiaException.class); + addParameter(op, assocClassFQN, "value"); + setDocumentation(op, "Retourne les éléments ayant comme valeur pour" + + " l'attribut " + + TopiaGeneratorUtil.toLowerCaseFirstLetter(assocClassName) + + " le paramètre."); + setOperationBody(op, "" +/*{ + List<E> result = findAllByProperty(<%=clazzName + "." + getConstantName(TopiaGeneratorUtil.toLowerCaseFirstLetter(assocClassName))%>, value); + return result; + }*/ + ); + } + } + + protected void generateNMultiplicity(String clazzName, + ObjectModelClass result, + ObjectModelAttribute attr) { + String attrName = attr.getName(); + String attrType = attr.getType(); + if (attr.hasAssociationClass()) { + // do nothing for association class, too complex... + return; + } + ObjectModelOperation op; + // Since 2.4 do nothing, findContains and findAllContains are not generated anymore + op = addOperation(result, + "findContains" + StringUtils.capitalize(attrName), + "E", + ObjectModelModifier.PUBLIC); + addException(op, TopiaException.class); + addParameter(op, attrType, "v"); + setDocumentation(op, "Retourne le premier élément ayant comme valeur pour" + + " l'attribut " + + TopiaGeneratorUtil.toLowerCaseFirstLetter(attrName) + + " le paramètre."); + setOperationBody(op, "" +/*{ + E result = findContains(<%=clazzName + "." + getConstantName(attrName)%>, v); + return result; + }*/ + ); + + op = addOperation(result, + "findAllContains" + StringUtils.capitalize(attrName), + "List<E>", + ObjectModelModifier.PUBLIC); + addException(op, TopiaException.class); + addParameter(op, attrType, "v"); + setDocumentation(op, "Retourne les éléments ayant comme valeur pour" + + " l'attribut " + + TopiaGeneratorUtil.toLowerCaseFirstLetter(attrName) + + " le paramètre."); + setOperationBody(op, "" +/*{ + List<E> result = findAllContains(<%=clazzName + "." + getConstantName(attrName)%>, v); + return result; + }*/ + ); + } + + + /** + * Obtain business operations of the DAO. + * + * This operations can not be generated, but must be written by developper. + * + * @param clazz the clazz to test. + * @return collections of extra operations, or empty collection if none found. + */ + public Collection<ObjectModelOperation> getDAOOperations( + ObjectModelClass clazz) { + +// // Note : this collection will contains extra operations for DAO. +// // Overriding existing generated methods is not managed yet +// Collection<ObjectModelOperation> results = +// new ArrayList<ObjectModelOperation>(); + +// // This code will be deprecated +// for (ObjectModelOperation op : clazz.getOperations()) { +// if (TopiaGeneratorUtil.hasDaoStereotype(op)) { +// results.add(op); +// } +// } + Collection<ObjectModelOperation> extra = + extraOperations.get(clazz.getQualifiedName()); + return extra; +// if (extra != null) { +// for (ObjectModelOperation op : extra) { +// results.add(op); +// } +// } + +// return results; + } + + private void generateNaturalId(ObjectModelClass result, + ObjectModelClass clazz) { + List<ObjectModelAttribute> props = + TopiaGeneratorUtil.getNaturalIdAttributes(clazz); + + if (!props.isEmpty()) { + + if (log.isDebugEnabled()) { + log.debug("generateNaturalId for " + props); + } + ObjectModelOperation findByNaturalId = addOperation(result, + "findByNaturalId", "E", ObjectModelModifier.PUBLIC); + addException(findByNaturalId, TopiaException.class); + + ObjectModelOperation existByNaturalId = addOperation(result, + "existByNaturalId", "boolean", ObjectModelModifier.PUBLIC); + addException(existByNaturalId, TopiaException.class); + + ObjectModelOperation create = addOperation(result, + "create", "E", ObjectModelModifier.PUBLIC); + addException(create, TopiaException.class); + + // used for calling findByProperties in findByNaturalId + String searchProperties = ""; + // used for calling findByNaturalId in existByNaturalId + String params = ""; + String clazzName = clazz.getName(); + for (ObjectModelAttribute attr : props) { + String propName = attr.getName(); + // add property as param in both methods + addParameter(findByNaturalId, attr.getType(), propName); + addParameter(existByNaturalId, attr.getType(), propName); + addParameter(create, attr.getType(), propName); + + searchProperties += + ", " + clazzName + '.' + getConstantName(propName) + + ", " + propName; + //params += ", " + propName; + } + searchProperties = searchProperties.substring(2); + //params = params.substring(2); + + setOperationBody(findByNaturalId, "" +/*{ + return findByProperties(<%=searchProperties%>); + }*/ + ); + + setOperationBody(existByNaturalId, "" +/*{ + return existByProperties(<%=searchProperties%>); + }*/ + ); + + setOperationBody(create, "" +/*{ + return create(<%=searchProperties%>); + }*/ + ); + } + } +} Property changes on: trunk/topia-persistence/src/main/java/org/nuiton/topia/generator/EntityDAOTransformer.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision Added: svn:eol-style + native Modified: trunk/topia-persistence/src/main/java/org/nuiton/topia/generator/TopiaGeneratorUtil.java =================================================================== --- trunk/topia-persistence/src/main/java/org/nuiton/topia/generator/TopiaGeneratorUtil.java 2011-05-15 19:02:48 UTC (rev 2281) +++ trunk/topia-persistence/src/main/java/org/nuiton/topia/generator/TopiaGeneratorUtil.java 2011-05-15 19:03:09 UTC (rev 2282) @@ -25,6 +25,7 @@ package org.nuiton.topia.generator; +import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -1077,6 +1078,34 @@ } } + public static boolean isImportNeeded(Collection<ObjectModelOperation> operations, + String importName) { + if (CollectionUtils.isNotEmpty(operations)) { + for (ObjectModelOperation op : operations) { + if (op.getReturnType().contains(importName)) { + return true; + } + for (ObjectModelParameter param : op.getParameters()) { + if (param.getType().contains(importName)) { + return true; + } + } + } + } + return false; + } + + public static boolean isCollectionNeeded( + Collection<ObjectModelOperation> operations) { + return isImportNeeded(operations, + Collection.class.getSimpleName()); + } + + public static boolean isSetNeeded(Collection<ObjectModelOperation> operations) { + return isImportNeeded(operations, + Set.class.getSimpleName()); + } + /** * Check if the given classifier has the * {@link TopiaStereoTypes#STEREOTYPE_FACADE} stereotype. Modified: trunk/topia-persistence/src/main/java/org/nuiton/topia/generator/TopiaMetaTransformer.java =================================================================== --- trunk/topia-persistence/src/main/java/org/nuiton/topia/generator/TopiaMetaTransformer.java 2011-05-15 19:02:48 UTC (rev 2281) +++ trunk/topia-persistence/src/main/java/org/nuiton/topia/generator/TopiaMetaTransformer.java 2011-05-15 19:03:09 UTC (rev 2282) @@ -62,9 +62,10 @@ setTemplateTypes( EntityTransformer.class, - DAOTransformer.class, - DAOImplTransformer.class, - DAOAbstractTransformer.class, + EntityDAOTransformer.class, +// DAOTransformer.class, +// DAOImplTransformer.class, +// DAOAbstractTransformer.class, DAOHelperTransformer.class, EntityHibernateMappingGenerator.class );