From 88b162331b937680b8b80e5d6704c456b721e36f Mon Sep 17 00:00:00 2001 From: olga Date: Mon, 28 Mar 2016 12:00:51 +0300 Subject: [PATCH] CAY-2069 Including templates in dataMap --- .../cayenne/project/ConfigurationSaver.java | 10 + .../project/ConfigurationSourceGetter.java | 6 + .../project/ConfigurationSourceSetter.java | 7 + .../DefaultConfigurationNodeParentGetter.java | 6 + .../cayenne/project/SaveableNodesGetter.java | 13 + .../validation/DefaultProjectValidator.java | 9 + .../project/validation/TemplateValidator.java | 43 + .../apache/cayenne/project/TemplateTest.java | 65 + .../BaseConfigurationNodeVisitor.java | 5 + .../ConfigurationNodeVisitor.java | 3 + .../DefaultConfigurationNameMapper.java | 20 +- .../XMLDataChannelDescriptorLoader.java | 681 ++++++----- .../gen/ClassGeneratorResourceLoader.java | 2 +- .../org/apache/cayenne/dbimport/Catalog.java | 3 +- .../org/apache/cayenne/dbimport/Schema.java | 3 +- .../java/org/apache/cayenne/map/DataMap.java | 61 +- .../org/apache/cayenne/map/MapLoader.java | 50 +- .../cayenne/map/naming/NameCheckers.java | 19 + .../template}/ArtifactsGenerationMode.java | 2 +- .../template/ClassGenerationDescriptor.java | 59 + .../cayenne/map/template/ClassTemplate.java | 96 ++ .../cayenne/map/template}/TemplateType.java | 2 +- .../org/apache/cayenne/schema/8/modelMap.xsd | 31 +- .../java/org/apache/cayenne/gen/Artifact.java | 1 + .../cayenne/gen/ClassGenerationAction.java | 1085 +++++++++-------- .../gen/ClientClassGenerationAction.java | 11 +- .../apache/cayenne/gen/DataMapArtifact.java | 1 + .../cayenne/gen/EmbeddableArtifact.java | 1 + .../apache/cayenne/gen/EntityArtifact.java | 1 + .../cayenne/tools/CayenneGeneratorTask.java | 257 +++- .../apache/cayenne/tools/DbImporterTask.java | 80 +- .../gen/ClassGenerationActionTest.java | 353 +++--- .../cayenne/gen/ClassGenerationCase.java | 1 + ...eneratorTaskCrossMapRelationshipsTest.java | 1 + .../tools/CayenneGeneratorTaskTest.java | 1 + .../configuration/event/TemplateEvent.java | 67 + .../configuration/event/TemplateListener.java | 40 + .../cayenne/modeler/CayenneModelerFrame.java | 9 +- .../cayenne/modeler/CodeTemplateManager.java | 27 + .../cayenne/modeler/ProjectController.java | 90 +- .../modeler/ProjectFileChangeTracker.java | 7 + .../cayenne/modeler/ProjectTreeView.java | 33 +- .../cayenne/modeler/action/ActionManager.java | 2 + .../cayenne/modeler/action/CopyAction.java | 4 +- .../modeler/action/CreateTemplateAction.java | 85 ++ .../modeler/action/DefaultActionManager.java | 13 + .../cayenne/modeler/action/PasteAction.java | 27 +- .../cayenne/modeler/action/RemoveAction.java | 57 +- .../codegen/CodeGeneratorController.java | 4 +- .../dialog/codegen/CustomModeController.java | 5 + .../dialog/codegen/GeneratorController.java | 3 +- .../codegen/StandardModeController.java | 2 +- .../dialog/db/ReverseEngineeringView.java | 33 +- .../dialog/template/TemplateController.java | 103 ++ .../dialog/template/TemplateEditor.java | 43 + .../dialog/template/TemplateEditorView.java | 58 + .../dialog/template/TemplateListEditor.java | 41 + .../dialog/template/TemplateScrollPane.java | 34 + .../modeler/dialog/template/TemplateView.java | 513 ++++++++ .../dialog/template/TemplateViewModel.java | 57 + .../modeler/editor/DataMapTabbedView.java | 12 + .../cayenne/modeler/editor/EditorView.java | 11 +- .../modeler/event/TemplateDisplayEvent.java | 56 + .../event/TemplateDisplayListener.java | 28 + .../undo/CreateTemplateUndoableEdit.java | 62 + .../modeler/undo/PasteUndoableEdit.java | 7 + .../modeler/undo/RemoveUndoableEdit.java | 27 +- .../modeler/util/state/DisplayEventTypes.java | 7 + .../util/state/TemplateDisplayEventType.java | 19 + .../cayenne/tools/CayenneGeneratorMojo.java | 692 +++++++---- .../apache/cayenne/tools/DbImporterMojo.java | 76 +- .../tools/CayenneGeneratorMojoTest.java | 22 + .../resources/cgen/project-to-test/pom1.xml | 50 + .../src/test/resources/cgen/singleclass.vm | 145 +++ .../cgen/testDataMapWithTemplates.map.xml | 34 + 75 files changed, 4147 insertions(+), 1447 deletions(-) create mode 100644 cayenne-project/src/main/java/org/apache/cayenne/project/validation/TemplateValidator.java create mode 100644 cayenne-project/src/test/java/org/apache/cayenne/project/TemplateTest.java rename {cayenne-tools/src/main/java/org/apache/cayenne => cayenne-server/src/main/java/org/apache/cayenne/configuration}/gen/ClassGeneratorResourceLoader.java (98%) rename {cayenne-tools/src/main/java/org/apache/cayenne/gen => cayenne-server/src/main/java/org/apache/cayenne/map/template}/ArtifactsGenerationMode.java (97%) create mode 100644 cayenne-server/src/main/java/org/apache/cayenne/map/template/ClassGenerationDescriptor.java create mode 100644 cayenne-server/src/main/java/org/apache/cayenne/map/template/ClassTemplate.java rename {cayenne-tools/src/main/java/org/apache/cayenne/gen => cayenne-server/src/main/java/org/apache/cayenne/map/template}/TemplateType.java (97%) create mode 100644 modeler/cayenne-modeler/src/main/java/org/apache/cayenne/configuration/event/TemplateEvent.java create mode 100644 modeler/cayenne-modeler/src/main/java/org/apache/cayenne/configuration/event/TemplateListener.java create mode 100644 modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/CreateTemplateAction.java create mode 100644 modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/template/TemplateController.java create mode 100644 modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/template/TemplateEditor.java create mode 100644 modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/template/TemplateEditorView.java create mode 100644 modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/template/TemplateListEditor.java create mode 100644 modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/template/TemplateScrollPane.java create mode 100644 modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/template/TemplateView.java create mode 100644 modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/template/TemplateViewModel.java create mode 100644 modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/event/TemplateDisplayEvent.java create mode 100644 modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/event/TemplateDisplayListener.java create mode 100644 modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/undo/CreateTemplateUndoableEdit.java create mode 100644 modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/util/state/TemplateDisplayEventType.java create mode 100644 plugins/maven-cayenne-plugin/src/test/resources/cgen/project-to-test/pom1.xml create mode 100644 plugins/maven-cayenne-plugin/src/test/resources/cgen/singleclass.vm create mode 100644 plugins/maven-cayenne-plugin/src/test/resources/cgen/testDataMapWithTemplates.map.xml diff --git a/cayenne-project/src/main/java/org/apache/cayenne/project/ConfigurationSaver.java b/cayenne-project/src/main/java/org/apache/cayenne/project/ConfigurationSaver.java index 01d3989ff9..75e7c9a803 100644 --- a/cayenne-project/src/main/java/org/apache/cayenne/project/ConfigurationSaver.java +++ b/cayenne-project/src/main/java/org/apache/cayenne/project/ConfigurationSaver.java @@ -22,6 +22,7 @@ import org.apache.cayenne.configuration.BaseConfigurationNodeVisitor; import org.apache.cayenne.configuration.DataChannelDescriptor; +import org.apache.cayenne.map.template.ClassTemplate; import org.apache.cayenne.dbimport.ReverseEngineering; import org.apache.cayenne.map.DataMap; import org.apache.cayenne.util.XMLEncoder; @@ -63,6 +64,15 @@ public Void visitReverseEngineering(ReverseEngineering node) { return null; } + @Override + public Void visitClassTemplate(ClassTemplate classTemplate) { + if(classTemplate.getText() != null) { + printWriter.write(classTemplate.getText()); + printWriter.close(); + } + return null; + } + private void printXMLHeader(XMLEncoder encoder) { encoder.println(""); } diff --git a/cayenne-project/src/main/java/org/apache/cayenne/project/ConfigurationSourceGetter.java b/cayenne-project/src/main/java/org/apache/cayenne/project/ConfigurationSourceGetter.java index be68dadebd..c98582b1ab 100644 --- a/cayenne-project/src/main/java/org/apache/cayenne/project/ConfigurationSourceGetter.java +++ b/cayenne-project/src/main/java/org/apache/cayenne/project/ConfigurationSourceGetter.java @@ -19,6 +19,7 @@ package org.apache.cayenne.project; import org.apache.cayenne.configuration.BaseConfigurationNodeVisitor; +import org.apache.cayenne.map.template.ClassTemplate; import org.apache.cayenne.configuration.DataChannelDescriptor; import org.apache.cayenne.dbimport.ReverseEngineering; import org.apache.cayenne.map.DataMap; @@ -41,6 +42,11 @@ public Resource visitDataMap(DataMap dataMap) { return dataMap.getConfigurationSource(); } + @Override + public Resource visitClassTemplate(ClassTemplate classTemplate) { + return classTemplate.getConfigurationSource(); + } + @Override public Resource visitReverseEngineering(ReverseEngineering reverseEngineering) { return reverseEngineering.getConfigurationSource(); diff --git a/cayenne-project/src/main/java/org/apache/cayenne/project/ConfigurationSourceSetter.java b/cayenne-project/src/main/java/org/apache/cayenne/project/ConfigurationSourceSetter.java index 7a3f5d3480..91245a0f3a 100644 --- a/cayenne-project/src/main/java/org/apache/cayenne/project/ConfigurationSourceSetter.java +++ b/cayenne-project/src/main/java/org/apache/cayenne/project/ConfigurationSourceSetter.java @@ -22,6 +22,7 @@ import org.apache.cayenne.configuration.DataChannelDescriptor; import org.apache.cayenne.configuration.DataNodeDescriptor; import org.apache.cayenne.dbimport.ReverseEngineering; +import org.apache.cayenne.map.template.ClassTemplate; import org.apache.cayenne.map.DataMap; import org.apache.cayenne.resource.Resource; @@ -56,6 +57,12 @@ public Void visitDataMap(DataMap node) { return null; } + @Override + public Void visitClassTemplate(ClassTemplate node) { + node.setConfigurationSource(configurationSource); + return null; + } + @Override public Void visitReverseEngineering(ReverseEngineering node) { node.setConfigurationSource(configurationSource); diff --git a/cayenne-project/src/main/java/org/apache/cayenne/project/DefaultConfigurationNodeParentGetter.java b/cayenne-project/src/main/java/org/apache/cayenne/project/DefaultConfigurationNodeParentGetter.java index dc3c8399fd..3e2c70738b 100644 --- a/cayenne-project/src/main/java/org/apache/cayenne/project/DefaultConfigurationNodeParentGetter.java +++ b/cayenne-project/src/main/java/org/apache/cayenne/project/DefaultConfigurationNodeParentGetter.java @@ -18,6 +18,7 @@ ****************************************************************/ package org.apache.cayenne.project; +import org.apache.cayenne.map.template.ClassTemplate; import org.apache.cayenne.configuration.BaseConfigurationNodeVisitor; import org.apache.cayenne.configuration.ConfigurationNode; import org.apache.cayenne.configuration.ConfigurationNodeVisitor; @@ -115,6 +116,11 @@ public ConfigurationNode visitQuery(QueryDescriptor query) { return query.getDataMap(); } + @Override + public ConfigurationNode visitClassTemplate(ClassTemplate classTemplate) { + return classTemplate; + } + @Override public ConfigurationNode visitReverseEngineering(ReverseEngineering reverseEngineering) { return reverseEngineering; diff --git a/cayenne-project/src/main/java/org/apache/cayenne/project/SaveableNodesGetter.java b/cayenne-project/src/main/java/org/apache/cayenne/project/SaveableNodesGetter.java index 61c94682fa..da46f6200f 100644 --- a/cayenne-project/src/main/java/org/apache/cayenne/project/SaveableNodesGetter.java +++ b/cayenne-project/src/main/java/org/apache/cayenne/project/SaveableNodesGetter.java @@ -22,6 +22,8 @@ import java.util.Collection; import java.util.Collections; +import org.apache.cayenne.map.template.ClassGenerationDescriptor; +import org.apache.cayenne.map.template.ClassTemplate; import org.apache.cayenne.configuration.BaseConfigurationNodeVisitor; import org.apache.cayenne.configuration.ConfigurationNode; import org.apache.cayenne.configuration.DataChannelDescriptor; @@ -43,6 +45,12 @@ public Collection visitDataChannelDescriptor( for (DataMap map : descriptor.getDataMaps()) { nodes.add(map); + if (map.getClassGenerationDescriptor() != null) { + ClassGenerationDescriptor classGenerationDescriptor = map.getClassGenerationDescriptor(); + for(ClassTemplate template: classGenerationDescriptor.getTemplates().values()) { + nodes.add(template); + } + } if (map.getReverseEngineering() != null) { nodes.add(map.getReverseEngineering()); } @@ -60,4 +68,9 @@ public Collection visitDataMap(DataMap dataMap) { public Collection visitReverseEngineering(ReverseEngineering reverseEngineering) { return null; } + + @Override + public Collection visitClassTemplate(ClassTemplate node) { + return null; + } } diff --git a/cayenne-project/src/main/java/org/apache/cayenne/project/validation/DefaultProjectValidator.java b/cayenne-project/src/main/java/org/apache/cayenne/project/validation/DefaultProjectValidator.java index f240fc87df..da8c7bce0f 100644 --- a/cayenne-project/src/main/java/org/apache/cayenne/project/validation/DefaultProjectValidator.java +++ b/cayenne-project/src/main/java/org/apache/cayenne/project/validation/DefaultProjectValidator.java @@ -20,6 +20,7 @@ import java.util.Iterator; +import org.apache.cayenne.map.template.ClassTemplate; import org.apache.cayenne.configuration.ConfigurationNode; import org.apache.cayenne.configuration.ConfigurationNodeVisitor; import org.apache.cayenne.configuration.DataChannelDescriptor; @@ -65,6 +66,7 @@ public class DefaultProjectValidator implements ProjectValidator { private ProcedureQueryValidator procedureQueryValidator; private EJBQLQueryValidator ejbqlQueryValidator; private SQLTemplateValidator sqlTemplateValidator; + private TemplateValidator templateValidator; private ReverseEngineeringValidator reverseEngineeringValidator; DefaultProjectValidator() { @@ -85,6 +87,7 @@ public class DefaultProjectValidator implements ProjectValidator { procedureQueryValidator = new ProcedureQueryValidator(); ejbqlQueryValidator = new EJBQLQueryValidator(); sqlTemplateValidator = new SQLTemplateValidator(); + templateValidator = new TemplateValidator(); reverseEngineeringValidator = new ReverseEngineeringValidator(); } @@ -274,6 +277,12 @@ public ValidationResult visitQuery(QueryDescriptor query) { return validationResult; } + @Override + public ValidationResult visitClassTemplate(ClassTemplate classTemplate) { + templateValidator.validate(classTemplate, validationResult); + return validationResult; + } + public ValidationResult visitReverseEngineering(ReverseEngineering reverseEngineering) { reverseEngineeringValidator.validate(reverseEngineering, validationResult); return validationResult; diff --git a/cayenne-project/src/main/java/org/apache/cayenne/project/validation/TemplateValidator.java b/cayenne-project/src/main/java/org/apache/cayenne/project/validation/TemplateValidator.java new file mode 100644 index 0000000000..e370f03b15 --- /dev/null +++ b/cayenne-project/src/main/java/org/apache/cayenne/project/validation/TemplateValidator.java @@ -0,0 +1,43 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.cayenne.project.validation; + +import org.apache.cayenne.map.template.ClassTemplate; +import org.apache.cayenne.util.Util; +import org.apache.cayenne.validation.ValidationResult; + +/** + * @since 4.0 + */ +public class TemplateValidator extends ConfigurationNodeValidator{ + void validate(ClassTemplate classTemplate, ValidationResult validationResult) { + validateName(classTemplate, validationResult); + } + + void validateName(ClassTemplate classTemplate, ValidationResult validationResult) { + String name = classTemplate.getName(); + + // Must have name + if (Util.isEmptyString(name)) { + addFailure(validationResult, name, "Unnamed Template"); + return; + } + } +} diff --git a/cayenne-project/src/test/java/org/apache/cayenne/project/TemplateTest.java b/cayenne-project/src/test/java/org/apache/cayenne/project/TemplateTest.java new file mode 100644 index 0000000000..f6218c24ac --- /dev/null +++ b/cayenne-project/src/test/java/org/apache/cayenne/project/TemplateTest.java @@ -0,0 +1,65 @@ +package org.apache.cayenne.project; + +import org.apache.cayenne.map.template.ClassTemplate; +import org.apache.cayenne.configuration.*; +import org.apache.cayenne.di.Binder; +import org.apache.cayenne.di.DIBootstrap; +import org.apache.cayenne.di.Injector; +import org.apache.cayenne.di.Module; +import org.apache.cayenne.map.DataMap; +import org.apache.cayenne.project.unit.Project2Case; +import org.apache.cayenne.resource.URLResource; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.net.MalformedURLException; + +import static junit.framework.TestCase.assertTrue; + +/** + * @since 4.0 + */ +public class TemplateTest extends Project2Case { + private FileProjectSaver saver; + + @Before + public void setUp() throws Exception { + Module testModule = new Module() { + + public void configure(Binder binder) { + binder.bind(ConfigurationNameMapper.class).to( + DefaultConfigurationNameMapper.class); + } + }; + + saver = new FileProjectSaver(); + Injector injector = DIBootstrap.createInjector(testModule); + injector.injectMembers(saver); + } + + @Test + public void testSaveTemplate() throws MalformedURLException { + File testFolder = setupTestDirectory("testSaverRE"); + + DataChannelDescriptor rootNode = new DataChannelDescriptor(); + rootNode.setName("test"); + + DataMap dataMap = new DataMap("datamap1"); + ClassTemplate classTemplate = new ClassTemplate(); + classTemplate.setName("template"); + dataMap.getClassGenerationDescriptor().getTemplates().put(classTemplate.getName(), classTemplate); + classTemplate.setDataMap(dataMap); + rootNode.getDataMaps().add(dataMap); + + Project project = new Project(new ConfigurationTree<>(rootNode)); + saver.saveAs(project, new URLResource(testFolder.toURL())); + + DataMapLoader dataMapLoader = new XMLDataMapLoader(); + DataMap dataMap1 = dataMapLoader.load(dataMap.getConfigurationSource()); + String templateName = dataMap1.getClassGenerationDescriptor().getTemplates().get(classTemplate.getName()).getName(); + + File templateFile = new File(testFolder, templateName + ".vm"); + assertTrue(templateFile.exists()); + } +} diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/BaseConfigurationNodeVisitor.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/BaseConfigurationNodeVisitor.java index f34c81396e..b3e65c1fbc 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/configuration/BaseConfigurationNodeVisitor.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/BaseConfigurationNodeVisitor.java @@ -18,6 +18,7 @@ ****************************************************************/ package org.apache.cayenne.configuration; +import org.apache.cayenne.map.template.ClassTemplate; import org.apache.cayenne.dbimport.ReverseEngineering; import org.apache.cayenne.map.DataMap; import org.apache.cayenne.map.DbAttribute; @@ -102,4 +103,8 @@ public T visitQuery(QueryDescriptor query) { public T visitReverseEngineering(ReverseEngineering reverseEngineering) { throw new UnsupportedOperationException("Not implemented for ReverseEngineering"); } + + public T visitClassTemplate(ClassTemplate classTemplate) { + throw new UnsupportedOperationException("Not implemented for ClassTemplate"); + } } diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/ConfigurationNodeVisitor.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/ConfigurationNodeVisitor.java index 508a5f8f0a..c7ab9379b3 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/configuration/ConfigurationNodeVisitor.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/ConfigurationNodeVisitor.java @@ -18,6 +18,7 @@ ****************************************************************/ package org.apache.cayenne.configuration; +import org.apache.cayenne.map.template.ClassTemplate; import org.apache.cayenne.dbimport.ReverseEngineering; import org.apache.cayenne.map.DataMap; import org.apache.cayenne.map.DbAttribute; @@ -68,5 +69,7 @@ public interface ConfigurationNodeVisitor { T visitQuery(QueryDescriptor query); + T visitClassTemplate(ClassTemplate classTemplate); + T visitReverseEngineering(ReverseEngineering reverseEngineering); } diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/DefaultConfigurationNameMapper.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/DefaultConfigurationNameMapper.java index 16b782a2cb..a9d21b2557 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/configuration/DefaultConfigurationNameMapper.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/DefaultConfigurationNameMapper.java @@ -18,6 +18,7 @@ ****************************************************************/ package org.apache.cayenne.configuration; +import org.apache.cayenne.map.template.ClassTemplate; import org.apache.cayenne.dbimport.ReverseEngineering; import org.apache.cayenne.map.DataMap; import org.apache.cayenne.resource.Resource; @@ -32,6 +33,8 @@ public class DefaultConfigurationNameMapper implements ConfigurationNameMapper { private static final String DATA_MAP_SUFFIX = ".map.xml"; + private static final String TEMPLATE_SUFFIX = ".vm"; + private static final String REVERSE_ENGINEERING_SUFFIX = ".xml"; protected ConfigurationNodeVisitor nameMapper; @@ -51,6 +54,8 @@ public String configurationLocation(Class type, Str return getDataChannelName(name); } else if (DataMap.class.isAssignableFrom(type)) { return getDataMapName(name); + } else if (ClassTemplate.class.isAssignableFrom(type)) { + return getClassTemplateName(name); } else if (ReverseEngineering.class.isAssignableFrom(type)) { return getReverseEngineeringName(name); } @@ -109,7 +114,7 @@ protected String getDataMapName(String name) { return name + DATA_MAP_SUFFIX; } - private String getReverseEngineeringName(String name) { + protected String getReverseEngineeringName(String name) { if (name == null) { throw new NullPointerException("Null Reverse Engineering name"); } @@ -117,6 +122,14 @@ private String getReverseEngineeringName(String name) { return name + REVERSE_ENGINEERING_SUFFIX; } + protected String getClassTemplateName(String name) { + if (name == null) { + throw new NullPointerException("Null Class Template name"); + } + + return name + TEMPLATE_SUFFIX; + } + final class NameMapper extends BaseConfigurationNodeVisitor { @Override @@ -129,6 +142,11 @@ public String visitDataMap(DataMap dataMap) { return getDataMapName(dataMap.getName()); } + @Override + public String visitClassTemplate(ClassTemplate classTemplate) { + return getClassTemplateName(classTemplate.getName()); + } + @Override public String visitReverseEngineering(ReverseEngineering reverseEngineering) { return getReverseEngineeringName(reverseEngineering.getName()); diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/XMLDataChannelDescriptorLoader.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/XMLDataChannelDescriptorLoader.java index 6e71e6a2b0..83f90dba85 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/configuration/XMLDataChannelDescriptorLoader.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/XMLDataChannelDescriptorLoader.java @@ -18,14 +18,12 @@ ****************************************************************/ package org.apache.cayenne.configuration; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; +import java.io.*; import java.net.MalformedURLException; import java.net.URL; import org.apache.cayenne.ConfigurationException; +import org.apache.cayenne.map.template.ClassTemplate; import org.apache.cayenne.conn.DataSourceInfo; import org.apache.cayenne.dbimport.DefaultReverseEngineeringLoader; import org.apache.cayenne.dbimport.ReverseEngineering; @@ -39,179 +37,176 @@ import org.apache.commons.logging.LogFactory; import org.xml.sax.*; -import javax.xml.parsers.ParserConfigurationException; - /** * @since 3.1 */ public class XMLDataChannelDescriptorLoader implements DataChannelDescriptorLoader { - private static Log logger = LogFactory.getLog(XMLDataChannelDescriptorLoader.class); - - static final String DOMAIN_TAG = "domain"; - static final String MAP_TAG = "map"; - static final String NODE_TAG = "node"; - static final String PROPERTY_TAG = "property"; - static final String MAP_REF_TAG = "map-ref"; - static final String DATA_SOURCE_TAG = "data-source"; - - /** - * @deprecated the caller should use password resolving strategy instead of - * resolving the password on the spot. For one thing this can be - * used in the Modeler and no password may be available. - */ - @Deprecated - private static String passwordFromURL(URL url) { - InputStream inputStream = null; - String password = null; - - try { - inputStream = url.openStream(); - password = passwordFromInputStream(inputStream); - } catch (IOException exception) { - // Log the error while trying to open the stream. A null - // password will be returned as a result. - logger.warn(exception); - } - - return password; - } - - /** - * @deprecated the caller should use password resolving strategy instead of - * resolving the password on the spot. For one thing this can be - * used in the Modeler and no password may be available. - */ - @Deprecated - private static String passwordFromInputStream(InputStream inputStream) { - String password = null; - - try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));) { - - password = bufferedReader.readLine(); - } catch (IOException exception) { - logger.warn(exception); - } finally { - - try { - inputStream.close(); - } catch (IOException exception) { - } - } - - return password; - } - - @Inject - protected DataMapLoader dataMapLoader; - - @Inject - protected ConfigurationNameMapper nameMapper; + private static Log logger = LogFactory.getLog(XMLDataChannelDescriptorLoader.class); + + static final String DOMAIN_TAG = "domain"; + static final String MAP_TAG = "map"; + static final String NODE_TAG = "node"; + static final String PROPERTY_TAG = "property"; + static final String MAP_REF_TAG = "map-ref"; + static final String DATA_SOURCE_TAG = "data-source"; + + /** + * @deprecated the caller should use password resolving strategy instead of + * resolving the password on the spot. For one thing this can be + * used in the Modeler and no password may be available. + */ + @Deprecated + private static String passwordFromURL(URL url) { + InputStream inputStream = null; + String password = null; + + try { + inputStream = url.openStream(); + password = passwordFromInputStream(inputStream); + } catch (IOException exception) { + // Log the error while trying to open the stream. A null + // password will be returned as a result. + logger.warn(exception); + } + + return password; + } + + /** + * @deprecated the caller should use password resolving strategy instead of + * resolving the password on the spot. For one thing this can be + * used in the Modeler and no password may be available. + */ + @Deprecated + private static String passwordFromInputStream(InputStream inputStream) { + String password = null; + + try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));) { + + password = bufferedReader.readLine(); + } catch (IOException exception) { + logger.warn(exception); + } finally { + + try { + inputStream.close(); + } catch (IOException exception) { + } + } + + return password; + } + + @Inject + protected DataMapLoader dataMapLoader; + + @Inject + protected ConfigurationNameMapper nameMapper; + + @Inject + protected AdhocObjectFactory objectFactory; + + @Override + public ConfigurationTree load(Resource configurationResource) throws ConfigurationException { + + if (configurationResource == null) { + throw new NullPointerException("Null configurationResource"); + } + + URL configurationURL = configurationResource.getURL(); + + logger.info("Loading XML configuration resource from " + configurationURL); + + DataChannelDescriptor descriptor = new DataChannelDescriptor(); + descriptor.setConfigurationSource(configurationResource); + descriptor.setName(nameMapper.configurationNodeName(DataChannelDescriptor.class, configurationResource)); + + DataChannelHandler rootHandler; + + InputStream in = null; + + try { + in = configurationURL.openStream(); + XMLReader parser = Util.createXmlReader(); + + rootHandler = new DataChannelHandler(descriptor, parser); + parser.setContentHandler(rootHandler); + parser.setErrorHandler(rootHandler); + parser.parse(new InputSource(in)); + } catch (Exception e) { + throw new ConfigurationException("Error loading configuration from %s", e, configurationURL); + } finally { + try { + if (in != null) { + in.close(); + } + } catch (IOException ioex) { + logger.info("failure closing input stream for " + configurationURL + ", ignoring", ioex); + } + } + + // TODO: andrus 03/10/2010 - actually provide load failures here... + return new ConfigurationTree<>(descriptor, null); + } - @Inject - protected AdhocObjectFactory objectFactory; - - @Override - public ConfigurationTree load(Resource configurationResource) throws ConfigurationException { - - if (configurationResource == null) { - throw new NullPointerException("Null configurationResource"); - } - - URL configurationURL = configurationResource.getURL(); - - logger.info("Loading XML configuration resource from " + configurationURL); - - DataChannelDescriptor descriptor = new DataChannelDescriptor(); - descriptor.setConfigurationSource(configurationResource); - descriptor.setName(nameMapper.configurationNodeName(DataChannelDescriptor.class, configurationResource)); - - DataChannelHandler rootHandler; - - InputStream in = null; - - try { - in = configurationURL.openStream(); - XMLReader parser = Util.createXmlReader(); - - rootHandler = new DataChannelHandler(descriptor, parser); - parser.setContentHandler(rootHandler); - parser.setErrorHandler(rootHandler); - parser.parse(new InputSource(in)); - } catch (Exception e) { - throw new ConfigurationException("Error loading configuration from %s", e, configurationURL); - } finally { - try { - if (in != null) { - in.close(); - } - } catch (IOException ioex) { - logger.info("failure closing input stream for " + configurationURL + ", ignoring", ioex); - } - } - - // TODO: andrus 03/10/2010 - actually provide load failures here... - return new ConfigurationTree(descriptor, null); - } - - final class DataChannelHandler extends SAXNestedTagHandler { - - private DataChannelDescriptor descriptor; - - DataChannelHandler(DataChannelDescriptor dataChannelDescriptor, XMLReader parser) { - super(parser, null); - this.descriptor = dataChannelDescriptor; - } + final class DataChannelHandler extends SAXNestedTagHandler { - @Override - protected ContentHandler createChildTagHandler(String namespaceURI, String localName, String name, - Attributes attributes) { + private DataChannelDescriptor descriptor; - if (localName.equals(DOMAIN_TAG)) { - return new DataChannelChildrenHandler(parser, this); - } + DataChannelHandler(DataChannelDescriptor dataChannelDescriptor, XMLReader parser) { + super(parser, null); + this.descriptor = dataChannelDescriptor; + } - logger.info(unexpectedTagMessage(localName, DOMAIN_TAG)); - return super.createChildTagHandler(namespaceURI, localName, name, attributes); - } - } + @Override + protected ContentHandler createChildTagHandler(String namespaceURI, String localName, String name, + Attributes attributes) { - final class DataChannelChildrenHandler extends SAXNestedTagHandler { + if (localName.equals(DOMAIN_TAG)) { + return new DataChannelChildrenHandler(parser, this); + } - private DataChannelDescriptor descriptor; + logger.info(unexpectedTagMessage(localName, DOMAIN_TAG)); + return super.createChildTagHandler(namespaceURI, localName, name, attributes); + } + } - DataChannelChildrenHandler(XMLReader parser, DataChannelHandler parentHandler) { - super(parser, parentHandler); - this.descriptor = parentHandler.descriptor; - } + final class DataChannelChildrenHandler extends SAXNestedTagHandler { - @Override - protected ContentHandler createChildTagHandler(String namespaceURI, String localName, String name, - Attributes attributes) { + private DataChannelDescriptor descriptor; - if (localName.equals(PROPERTY_TAG)) { + DataChannelChildrenHandler(XMLReader parser, DataChannelHandler parentHandler) { + super(parser, parentHandler); + this.descriptor = parentHandler.descriptor; + } - String key = attributes.getValue("", "name"); - String value = attributes.getValue("", "value"); - if (key != null && value != null) { - descriptor.getProperties().put(key, value); - } - } else if (localName.equals(MAP_TAG)) { + @Override + protected ContentHandler createChildTagHandler(String namespaceURI, String localName, String name, + Attributes attributes) { - String dataMapName = attributes.getValue("", "name"); - Resource baseResource = descriptor.getConfigurationSource(); + if (localName.equals(PROPERTY_TAG)) { - String dataMapLocation = nameMapper.configurationLocation(DataMap.class, dataMapName); + String key = attributes.getValue("", "name"); + String value = attributes.getValue("", "value"); + if (key != null && value != null) { + descriptor.getProperties().put(key, value); + } + } else if (localName.equals(MAP_TAG)) { + + String dataMapName = attributes.getValue("", "name"); + Resource baseResource = descriptor.getConfigurationSource(); - Resource dataMapResource = baseResource.getRelativeResource(dataMapLocation); + String dataMapLocation = nameMapper.configurationLocation(DataMap.class, dataMapName); + Resource dataMapResource = baseResource.getRelativeResource(dataMapLocation); - logger.info("Loading XML DataMap resource from " + dataMapResource.getURL()); + logger.info("Loading XML DataMap resource from " + dataMapResource.getURL()); - DataMap dataMap = dataMapLoader.load(dataMapResource); - dataMap.setName(dataMapName); - dataMap.setLocation(dataMapLocation); - dataMap.setConfigurationSource(dataMapResource); - dataMap.setDataChannelDescriptor(descriptor); + DataMap dataMap = dataMapLoader.load(dataMapResource); + dataMap.setName(dataMapName); + dataMap.setLocation(dataMapLocation); + dataMap.setConfigurationSource(dataMapResource); + dataMap.setDataChannelDescriptor(descriptor); try { if (dataMap.getReverseEngineering() != null) { @@ -230,179 +225,217 @@ protected ContentHandler createChildTagHandler(String namespaceURI, String local logger.info(e.getMessage(), e); } + if (dataMap.getClassGenerationDescriptor() != null) { + for (ClassTemplate template : dataMap.getClassGenerationDescriptor().getTemplates().values()) { + if (template != null) { + template.setConfigurationSource(getRelatedResource(template, baseResource)); + template.setText(getTemplateText(template.getConfigurationSource())); + template.setDataMap(dataMap); + } + } + } descriptor.getDataMaps().add(dataMap); } else if (localName.equals(NODE_TAG)) { - String nodeName = attributes.getValue("", "name"); - if (nodeName == null) { - throw new ConfigurationException("Error: without 'name'."); - } + String nodeName = attributes.getValue("", "name"); + if (nodeName == null) { + throw new ConfigurationException("Error: without 'name'."); + } + + DataNodeDescriptor nodeDescriptor = new DataNodeDescriptor(); + nodeDescriptor.setConfigurationSource(descriptor.getConfigurationSource()); + descriptor.getNodeDescriptors().add(nodeDescriptor); + + nodeDescriptor.setName(nodeName); + nodeDescriptor.setAdapterType(attributes.getValue("", "adapter")); + + String parameters = attributes.getValue("", "parameters"); + nodeDescriptor.setParameters(parameters); + + String dataSourceFactory = attributes.getValue("", "factory"); + nodeDescriptor.setDataSourceFactoryType(dataSourceFactory); + nodeDescriptor.setSchemaUpdateStrategyType(attributes.getValue("", "schema-update-strategy")); + nodeDescriptor.setDataChannelDescriptor(descriptor); + + return new DataNodeChildrenHandler(parser, this, nodeDescriptor); + } + + return super.createChildTagHandler(namespaceURI, localName, name, attributes); + } + } + + private String getTemplateText(Resource resource) { + URL url = resource.getURL(); + InputStream inputStream; + try { + inputStream = url.openStream(); + + BufferedInputStream bis = new BufferedInputStream(inputStream); + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + int result = bis.read(); + while (result != -1) { + buf.write((byte) result); + result = bis.read(); + } + buf.close(); + bis.close(); + inputStream.close(); + return buf.toString(); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + private Resource getRelatedResource(ClassTemplate template, Resource baseResource) { + String templateName = template.getName(); + String templateLocation = nameMapper.configurationLocation(ClassTemplate.class, templateName); + return baseResource.getRelativeResource(templateLocation); + } + + final class DataNodeChildrenHandler extends SAXNestedTagHandler { + + private DataNodeDescriptor nodeDescriptor; + + DataNodeChildrenHandler(XMLReader parser, SAXNestedTagHandler parentHandler, DataNodeDescriptor nodeDescriptor) { + super(parser, parentHandler); + this.nodeDescriptor = nodeDescriptor; + } + + @Override + protected ContentHandler createChildTagHandler(String namespaceURI, String localName, String name, + Attributes attributes) { + + if (localName.equals(MAP_REF_TAG)) { + + String mapName = attributes.getValue("", "name"); + nodeDescriptor.getDataMapNames().add(mapName); + } else if (localName.equals(DATA_SOURCE_TAG)) { + + DataSourceInfo dataSourceDescriptor = new DataSourceInfo(); + nodeDescriptor.setDataSourceDescriptor(dataSourceDescriptor); + return new DataSourceChildrenHandler(parser, this, dataSourceDescriptor); + } + + return super.createChildTagHandler(namespaceURI, localName, name, attributes); + } + } + + class DataSourceChildrenHandler extends SAXNestedTagHandler { + + private DataSourceInfo dataSourceDescriptor; + + DataSourceChildrenHandler(XMLReader parser, DataNodeChildrenHandler parentHandler, + DataSourceInfo dataSourceDescriptor) { + super(parser, parentHandler); + this.dataSourceDescriptor = dataSourceDescriptor; + } + + @Override + protected ContentHandler createChildTagHandler(String namespaceURI, String localName, String name, + Attributes attributes) { + + if (localName.equals("driver")) { + String className = attributes.getValue("", "value"); + dataSourceDescriptor.setJdbcDriver(className); + } else if (localName.equals("login")) { + + logger.info("loading user name and password."); + + String encoderClass = attributes.getValue("encoderClass"); + + String encoderKey = attributes.getValue("encoderKey"); + if (encoderKey == null) { + encoderKey = attributes.getValue("encoderSalt"); + } + + String password = attributes.getValue("password"); + String passwordLocation = attributes.getValue("passwordLocation"); + String passwordSource = attributes.getValue("passwordSource"); + if (passwordSource == null) { + passwordSource = DataSourceInfo.PASSWORD_LOCATION_MODEL; + } + + String username = attributes.getValue("userName"); + + dataSourceDescriptor.setPasswordEncoderClass(encoderClass); + dataSourceDescriptor.setPasswordEncoderKey(encoderKey); + dataSourceDescriptor.setPasswordLocation(passwordLocation); + dataSourceDescriptor.setPasswordSource(passwordSource); + dataSourceDescriptor.setUserName(username); + + // Replace {} in passwordSource with encoderSalt -- useful for + // EXECUTABLE + // & URL options + if (encoderKey != null) { + passwordSource = passwordSource.replaceAll("\\{\\}", encoderKey); + } - DataNodeDescriptor nodeDescriptor = new DataNodeDescriptor(); - nodeDescriptor.setConfigurationSource(descriptor.getConfigurationSource()); - descriptor.getNodeDescriptors().add(nodeDescriptor); + String encoderType = dataSourceDescriptor.getPasswordEncoderClass(); + PasswordEncoding passwordEncoder = null; + if (encoderType != null) { + passwordEncoder = objectFactory.newInstance(PasswordEncoding.class, encoderType); + } - nodeDescriptor.setName(nodeName); - nodeDescriptor.setAdapterType(attributes.getValue("", "adapter")); + if (passwordLocation != null) { + if (passwordLocation.equals(DataSourceInfo.PASSWORD_LOCATION_CLASSPATH)) { + + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + URL url = classLoader.getResource(username); + if (url != null) { + password = passwordFromURL(url); + } else { + logger.error("Could not find resource in CLASSPATH: " + passwordSource); + } + } else if (passwordLocation.equals(DataSourceInfo.PASSWORD_LOCATION_URL)) { + try { + password = passwordFromURL(new URL(passwordSource)); + } catch (MalformedURLException exception) { + logger.warn(exception); + } + } else if (passwordLocation.equals(DataSourceInfo.PASSWORD_LOCATION_EXECUTABLE)) { + if (passwordSource != null) { + try { + Process process = Runtime.getRuntime().exec(passwordSource); + password = passwordFromInputStream(process.getInputStream()); + process.waitFor(); + } catch (IOException exception) { + logger.warn(exception); + } catch (InterruptedException exception) { + logger.warn(exception); + } + } + } + } - String parameters = attributes.getValue("", "parameters"); - nodeDescriptor.setParameters(parameters); + if (password != null && passwordEncoder != null) { + dataSourceDescriptor.setPassword(passwordEncoder.decodePassword(password, encoderKey)); + } + } else if (localName.equals("url")) { + dataSourceDescriptor.setDataSourceUrl(attributes.getValue("value")); + } else if (localName.equals("connectionPool")) { + String min = attributes.getValue("min"); + if (min != null) { + try { + dataSourceDescriptor.setMinConnections(Integer.parseInt(min)); + } catch (NumberFormatException nfex) { + logger.info("Non-numeric 'min' attribute", nfex); + throw new ConfigurationException("Non-numeric 'min' attribute '%s'", nfex, min); + } + } - String dataSourceFactory = attributes.getValue("", "factory"); - nodeDescriptor.setDataSourceFactoryType(dataSourceFactory); - nodeDescriptor.setSchemaUpdateStrategyType(attributes.getValue("", "schema-update-strategy")); - nodeDescriptor.setDataChannelDescriptor(descriptor); + String max = attributes.getValue("max"); + if (max != null) { + try { + dataSourceDescriptor.setMaxConnections(Integer.parseInt(max)); + } catch (NumberFormatException nfex) { + logger.info("Non-numeric 'max' attribute", nfex); + throw new ConfigurationException("Non-numeric 'max' attribute '%s'", nfex, max); + } + } + } - return new DataNodeChildrenHandler(parser, this, nodeDescriptor); - } - - return super.createChildTagHandler(namespaceURI, localName, name, attributes); - } - } - - final class DataNodeChildrenHandler extends SAXNestedTagHandler { - - private DataNodeDescriptor nodeDescriptor; - - DataNodeChildrenHandler(XMLReader parser, SAXNestedTagHandler parentHandler, DataNodeDescriptor nodeDescriptor) { - super(parser, parentHandler); - this.nodeDescriptor = nodeDescriptor; - } - - @Override - protected ContentHandler createChildTagHandler(String namespaceURI, String localName, String name, - Attributes attributes) { - - if (localName.equals(MAP_REF_TAG)) { - - String mapName = attributes.getValue("", "name"); - nodeDescriptor.getDataMapNames().add(mapName); - } else if (localName.equals(DATA_SOURCE_TAG)) { - - DataSourceInfo dataSourceDescriptor = new DataSourceInfo(); - nodeDescriptor.setDataSourceDescriptor(dataSourceDescriptor); - return new DataSourceChildrenHandler(parser, this, dataSourceDescriptor); - } - - return super.createChildTagHandler(namespaceURI, localName, name, attributes); - } - } - - class DataSourceChildrenHandler extends SAXNestedTagHandler { - - private DataSourceInfo dataSourceDescriptor; - - DataSourceChildrenHandler(XMLReader parser, DataNodeChildrenHandler parentHandler, - DataSourceInfo dataSourceDescriptor) { - super(parser, parentHandler); - this.dataSourceDescriptor = dataSourceDescriptor; - } - - @Override - protected ContentHandler createChildTagHandler(String namespaceURI, String localName, String name, - Attributes attributes) { - - if (localName.equals("driver")) { - String className = attributes.getValue("", "value"); - dataSourceDescriptor.setJdbcDriver(className); - } else if (localName.equals("login")) { - - logger.info("loading user name and password."); - - String encoderClass = attributes.getValue("encoderClass"); - - String encoderKey = attributes.getValue("encoderKey"); - if (encoderKey == null) { - encoderKey = attributes.getValue("encoderSalt"); - } - - String password = attributes.getValue("password"); - String passwordLocation = attributes.getValue("passwordLocation"); - String passwordSource = attributes.getValue("passwordSource"); - if (passwordSource == null) { - passwordSource = DataSourceInfo.PASSWORD_LOCATION_MODEL; - } - - String username = attributes.getValue("userName"); - - dataSourceDescriptor.setPasswordEncoderClass(encoderClass); - dataSourceDescriptor.setPasswordEncoderKey(encoderKey); - dataSourceDescriptor.setPasswordLocation(passwordLocation); - dataSourceDescriptor.setPasswordSource(passwordSource); - dataSourceDescriptor.setUserName(username); - - // Replace {} in passwordSource with encoderSalt -- useful for - // EXECUTABLE - // & URL options - if (encoderKey != null) { - passwordSource = passwordSource.replaceAll("\\{\\}", encoderKey); - } - - String encoderType = dataSourceDescriptor.getPasswordEncoderClass(); - PasswordEncoding passwordEncoder = null; - if (encoderType != null) { - passwordEncoder = objectFactory.newInstance(PasswordEncoding.class, encoderType); - } - - if (passwordLocation != null) { - if (passwordLocation.equals(DataSourceInfo.PASSWORD_LOCATION_CLASSPATH)) { - - ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); - URL url = classLoader.getResource(username); - if (url != null) { - password = passwordFromURL(url); - } else { - logger.error("Could not find resource in CLASSPATH: " + passwordSource); - } - } else if (passwordLocation.equals(DataSourceInfo.PASSWORD_LOCATION_URL)) { - try { - password = passwordFromURL(new URL(passwordSource)); - } catch (MalformedURLException exception) { - logger.warn(exception); - } - } else if (passwordLocation.equals(DataSourceInfo.PASSWORD_LOCATION_EXECUTABLE)) { - if (passwordSource != null) { - try { - Process process = Runtime.getRuntime().exec(passwordSource); - password = passwordFromInputStream(process.getInputStream()); - process.waitFor(); - } catch (IOException exception) { - logger.warn(exception); - } catch (InterruptedException exception) { - logger.warn(exception); - } - } - } - } - - if (password != null && passwordEncoder != null) { - dataSourceDescriptor.setPassword(passwordEncoder.decodePassword(password, encoderKey)); - } - } else if (localName.equals("url")) { - dataSourceDescriptor.setDataSourceUrl(attributes.getValue("value")); - } else if (localName.equals("connectionPool")) { - String min = attributes.getValue("min"); - if (min != null) { - try { - dataSourceDescriptor.setMinConnections(Integer.parseInt(min)); - } catch (NumberFormatException nfex) { - logger.info("Non-numeric 'min' attribute", nfex); - throw new ConfigurationException("Non-numeric 'min' attribute '%s'", nfex, min); - } - } - - String max = attributes.getValue("max"); - if (max != null) { - try { - dataSourceDescriptor.setMaxConnections(Integer.parseInt(max)); - } catch (NumberFormatException nfex) { - logger.info("Non-numeric 'max' attribute", nfex); - throw new ConfigurationException("Non-numeric 'max' attribute '%s'", nfex, max); - } - } - } - - return super.createChildTagHandler(namespaceURI, localName, name, attributes); - } - } + return super.createChildTagHandler(namespaceURI, localName, name, attributes); + } + } } diff --git a/cayenne-tools/src/main/java/org/apache/cayenne/gen/ClassGeneratorResourceLoader.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/gen/ClassGeneratorResourceLoader.java similarity index 98% rename from cayenne-tools/src/main/java/org/apache/cayenne/gen/ClassGeneratorResourceLoader.java rename to cayenne-server/src/main/java/org/apache/cayenne/configuration/gen/ClassGeneratorResourceLoader.java index 8e8a1ee292..c0532ff2d7 100644 --- a/cayenne-tools/src/main/java/org/apache/cayenne/gen/ClassGeneratorResourceLoader.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/gen/ClassGeneratorResourceLoader.java @@ -17,7 +17,7 @@ * under the License. ****************************************************************/ -package org.apache.cayenne.gen; +package org.apache.cayenne.configuration.gen; import java.io.BufferedInputStream; import java.io.File; diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dbimport/Catalog.java b/cayenne-server/src/main/java/org/apache/cayenne/dbimport/Catalog.java index 99cf4cca0b..9888e195d0 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/dbimport/Catalog.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/dbimport/Catalog.java @@ -19,6 +19,7 @@ package org.apache.cayenne.dbimport; import javax.xml.bind.annotation.*; +import java.io.Serializable; import java.util.Collection; import java.util.LinkedList; @@ -26,7 +27,7 @@ * @since 4.0. */ @XmlAccessorType(XmlAccessType.FIELD) -public class Catalog extends FilterContainer { +public class Catalog extends FilterContainer implements Serializable { @XmlAttribute(name = "name") private String name; diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dbimport/Schema.java b/cayenne-server/src/main/java/org/apache/cayenne/dbimport/Schema.java index 8667891eed..506133e81c 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/dbimport/Schema.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/dbimport/Schema.java @@ -21,12 +21,13 @@ import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; +import java.io.Serializable; /** * @since 4.0. */ @XmlAccessorType(XmlAccessType.FIELD) -public class Schema extends FilterContainer { +public class Schema extends FilterContainer implements Serializable { @XmlAttribute(name = "name") private String name; diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/DataMap.java b/cayenne-server/src/main/java/org/apache/cayenne/map/DataMap.java index a570d665fc..5e32d30113 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/map/DataMap.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/map/DataMap.java @@ -22,6 +22,8 @@ import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.ObjectId; import org.apache.cayenne.Persistent; +import org.apache.cayenne.map.template.ClassGenerationDescriptor; +import org.apache.cayenne.map.template.ClassTemplate; import org.apache.cayenne.configuration.ConfigurationNode; import org.apache.cayenne.configuration.ConfigurationNodeVisitor; import org.apache.cayenne.configuration.DataChannelDescriptor; @@ -145,6 +147,7 @@ public class DataMap implements Serializable, ConfigurationNode, XMLSerializable private SortedMap procedureMap; private SortedMap queryDescriptorMap; private SortedMap results; + private ClassGenerationDescriptor classGenerationDescriptor; private ReverseEngineering reverseEngineering; /** @@ -174,10 +177,11 @@ public DataMap() { * Creates a new named DataMap. */ public DataMap(String mapName) { - this(mapName, Collections. emptyMap()); + this(mapName, Collections.emptyMap()); } public DataMap(String mapName, Map properties) { + classGenerationDescriptor = new ClassGenerationDescriptor(); embeddablesMap = new TreeMap(); objEntityMap = new TreeMap(); dbEntityMap = new TreeMap(); @@ -321,7 +325,25 @@ public void encodeAsXML(XMLEncoder encoder) { encoder.printProjectVersion(); encoder.println(">"); - if (reverseEngineering != null) { + if (classGenerationDescriptor.getTemplates().size() > 0) { + encoder.print(""); + encoder.indent(1); + for (ClassTemplate template : classGenerationDescriptor.getTemplates().values()) { + encoder.print(""); + } + encoder.indent(-1); + encoder.println(""); + } + if (reverseEngineering != null) { encoder.print(""); @@ -547,6 +569,15 @@ public void removeQueryDescriptor(String queryName) { queryDescriptorMap.remove(queryName); } + /** + * Removes a named template from the DataMap. + * + * @since 4.0 + */ + public void removeTemplate(String templateName) { + classGenerationDescriptor.getTemplates().remove(templateName); + } + /** * Removes all stored embeddable objects from the map. * @@ -1071,6 +1102,24 @@ public void addProcedure(Procedure procedure) { procedure.setDataMap(this); } + public void addTemplate(ClassTemplate template) { + if (template.getName() == null) { + throw new NullPointerException("Attempt to add template with no name."); + } + + Object existingTemplate = classGenerationDescriptor.getTemplates().get(template.getName()); + if (existingTemplate != null) { + if (existingTemplate == template) { + return; + } else { + throw new IllegalArgumentException("An attempt to override procedure '" + template.getName()); + } + } + + classGenerationDescriptor.getTemplates().put(template.getName(), template); + template.setDataMap(this); + } + public void removeProcedure(String name) { procedureMap.remove(name); } @@ -1407,4 +1456,12 @@ public ReverseEngineering getReverseEngineering() { public void setReverseEngineering(ReverseEngineering reverseEngineering) { this.reverseEngineering = reverseEngineering; } + + public ClassGenerationDescriptor getClassGenerationDescriptor() { + return classGenerationDescriptor; + } + + public void setClassGenerationDescriptor(ClassGenerationDescriptor classGenerationDescriptor) { + this.classGenerationDescriptor = classGenerationDescriptor; + } } diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/MapLoader.java b/cayenne-server/src/main/java/org/apache/cayenne/map/MapLoader.java index 213e38eca7..ca7263d86e 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/map/MapLoader.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/map/MapLoader.java @@ -24,7 +24,10 @@ import java.util.TreeMap; import org.apache.cayenne.CayenneRuntimeException; +import org.apache.cayenne.map.template.ClassTemplate; import org.apache.cayenne.dbimport.*; +import org.apache.cayenne.map.template.ClassGenerationDescriptor; +import org.apache.cayenne.map.template.TemplateType; import org.apache.cayenne.dba.TypesMapping; import org.apache.cayenne.exp.Expression; import org.apache.cayenne.util.Util; @@ -51,6 +54,9 @@ public class MapLoader extends DefaultHandler { */ public static final String REVERSE_ENGINEERING = "reverse-engineering-config"; + public static final String CODE_GENERATION = "class-generator-template"; + public static final String TEMPLATE = "template"; + public static final String PROPERTY_TAG = "property"; /** @@ -153,6 +159,8 @@ public class MapLoader extends DefaultHandler { private QueryDescriptorLoader queryBuilder; private String sqlKey; private ReverseEngineering reverseEngineering; + private ClassGenerationDescriptor classGenerationDescriptor; + private ClassTemplate classTemplate; private String descending; private String ignoreCase; @@ -177,6 +185,13 @@ void execute(Attributes attributes) throws SAXException { } }); + startTagOpMap.put(CODE_GENERATION, new StartClosure() { + @Override + void execute(Attributes attributes) throws SAXException { + processStartCodeGeneration(attributes); + } + }); + startTagOpMap.put(REVERSE_ENGINEERING, new StartClosure() { @Override @@ -185,7 +200,14 @@ void execute(Attributes attributes) throws SAXException { } }); - + startTagOpMap.put(TEMPLATE, new StartClosure() { + + @Override + void execute(Attributes attributes) throws SAXException { + processStartTemplate(attributes); + } + }); + startTagOpMap.put(DB_ENTITY_TAG, new StartClosure() { @Override @@ -584,7 +606,31 @@ private void processStartReverseEngineering(Attributes attributes) { dataMap.setReverseEngineering(reverseEngineering); } - + + private void processStartCodeGeneration(Attributes attributes) { + classGenerationDescriptor = new ClassGenerationDescriptor(); + + String mode = attributes.getValue("", "mode"); + if (mode != null) { + classGenerationDescriptor.setArtifactsGenerationMode(mode.toUpperCase()); + } + + dataMap.setClassGenerationDescriptor(classGenerationDescriptor); + } + + private void processStartTemplate(Attributes attributes) { + String type = attributes.getValue("", "type"); + String file = attributes.getValue("", "file"); + + ClassTemplate template = new ClassTemplate(); + template.setName(file); + if(type != null) { + template.setType(TemplateType.valueOf(type.toUpperCase())); + } + classGenerationDescriptor.getTemplates().put(template.getName(), template); + } + + private void processStartDataMap(Attributes attributes) { this.mapVersion = attributes.getValue("", "project-version"); } diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/naming/NameCheckers.java b/cayenne-server/src/main/java/org/apache/cayenne/map/naming/NameCheckers.java index 67639d6371..0583c187e1 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/map/naming/NameCheckers.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/map/naming/NameCheckers.java @@ -20,6 +20,7 @@ package org.apache.cayenne.map.naming; import org.apache.cayenne.access.DataDomain; +import org.apache.cayenne.map.template.ClassTemplate; import org.apache.cayenne.configuration.DataChannelDescriptor; import org.apache.cayenne.configuration.DataNodeDescriptor; import org.apache.cayenne.map.DataMap; @@ -63,6 +64,24 @@ public boolean isNameInUse(Object namingContext, String name) { return false; } }, + + template("template") { + @Override + public boolean isNameInUse(Object namingContext, String name) { + if (namingContext == null) { + return false; + } + + for (DataMap dataMap : ((DataChannelDescriptor) namingContext).getDataMaps()) { + for (ClassTemplate classTemplate : dataMap.getClassGenerationDescriptor().getTemplates().values()) { + if(name.equals(classTemplate.getName())) { + return true; + } + } + } + return false; + } + }, reverseEngineering("reverseEngineering") { @Override diff --git a/cayenne-tools/src/main/java/org/apache/cayenne/gen/ArtifactsGenerationMode.java b/cayenne-server/src/main/java/org/apache/cayenne/map/template/ArtifactsGenerationMode.java similarity index 97% rename from cayenne-tools/src/main/java/org/apache/cayenne/gen/ArtifactsGenerationMode.java rename to cayenne-server/src/main/java/org/apache/cayenne/map/template/ArtifactsGenerationMode.java index e76c1720e3..6b530ed4d6 100644 --- a/cayenne-tools/src/main/java/org/apache/cayenne/gen/ArtifactsGenerationMode.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/map/template/ArtifactsGenerationMode.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. ****************************************************************/ -package org.apache.cayenne.gen; +package org.apache.cayenne.map.template; /** * Code generator execution mode for a collection of artifacts. diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/template/ClassGenerationDescriptor.java b/cayenne-server/src/main/java/org/apache/cayenne/map/template/ClassGenerationDescriptor.java new file mode 100644 index 0000000000..62e7c4fcdf --- /dev/null +++ b/cayenne-server/src/main/java/org/apache/cayenne/map/template/ClassGenerationDescriptor.java @@ -0,0 +1,59 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.cayenne.map.template; + +import java.io.Serializable; +import java.util.*; + +/** + * @since 4.0 + */ +public class ClassGenerationDescriptor implements Serializable { + protected ArtifactsGenerationMode artifactsGenerationMode; + + protected Map templates; + + public ClassGenerationDescriptor() { + this.templates = new HashMap<>(6); + } + + + public ArtifactsGenerationMode getArtifactsGenerationMode() { + return artifactsGenerationMode; + } + + public Map getTemplates() { + return templates; + } + + public void setTemplates(Map templates) { + this.templates = templates; + } + + public void setArtifactsGenerationMode(String mode) { + if (ArtifactsGenerationMode.ENTITY.getLabel().equalsIgnoreCase(mode)) { + this.artifactsGenerationMode = ArtifactsGenerationMode.ENTITY; + } else if (ArtifactsGenerationMode.DATAMAP.getLabel().equalsIgnoreCase(mode)) { + this.artifactsGenerationMode = ArtifactsGenerationMode.DATAMAP; + } else { + this.artifactsGenerationMode = ArtifactsGenerationMode.ALL; + } + } +} diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/template/ClassTemplate.java b/cayenne-server/src/main/java/org/apache/cayenne/map/template/ClassTemplate.java new file mode 100644 index 0000000000..2c6aee560b --- /dev/null +++ b/cayenne-server/src/main/java/org/apache/cayenne/map/template/ClassTemplate.java @@ -0,0 +1,96 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.cayenne.map.template; + +import org.apache.cayenne.configuration.ConfigurationNode; +import org.apache.cayenne.configuration.ConfigurationNodeVisitor; +import org.apache.cayenne.map.DataMap; +import org.apache.cayenne.resource.Resource; +import org.apache.velocity.Template; + +import java.io.Serializable; + +/** + * @since 4.0 + */ +public class ClassTemplate implements ConfigurationNode, Serializable { + private TemplateType type; + private Resource configurationSource; + private String name; + private String text; + private DataMap dataMap; + + public ClassTemplate() { + } + + public ClassTemplate(TemplateType type, Resource configurationSource, String name, Template template, String text) { + this.type = type; + this.configurationSource = configurationSource; + this.name = name; + this.text = text; + } + + public ClassTemplate(String name) { + this.name = name; + } + + public TemplateType getType() { + return type; + } + + public void setType(TemplateType type) { + this.type = type; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Resource getConfigurationSource() { + return configurationSource; + } + + public void setConfigurationSource(Resource configurationSource) { + this.configurationSource = configurationSource; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public DataMap getDataMap() { + return dataMap; + } + + public void setDataMap(DataMap dataMap) { + this.dataMap = dataMap; + } + @Override + public T acceptVisitor(ConfigurationNodeVisitor visitor) { + return visitor.visitClassTemplate(this); + } +} diff --git a/cayenne-tools/src/main/java/org/apache/cayenne/gen/TemplateType.java b/cayenne-server/src/main/java/org/apache/cayenne/map/template/TemplateType.java similarity index 97% rename from cayenne-tools/src/main/java/org/apache/cayenne/gen/TemplateType.java rename to cayenne-server/src/main/java/org/apache/cayenne/map/template/TemplateType.java index 109627ed96..fec6459406 100644 --- a/cayenne-tools/src/main/java/org/apache/cayenne/gen/TemplateType.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/map/template/TemplateType.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. ****************************************************************/ -package org.apache.cayenne.gen; +package org.apache.cayenne.map.template; /** * Defines class generation template types. diff --git a/cayenne-server/src/main/resources/org/apache/cayenne/schema/8/modelMap.xsd b/cayenne-server/src/main/resources/org/apache/cayenne/schema/8/modelMap.xsd index 37f0f910a5..36175d788d 100644 --- a/cayenne-server/src/main/resources/org/apache/cayenne/schema/8/modelMap.xsd +++ b/cayenne-server/src/main/resources/org/apache/cayenne/schema/8/modelMap.xsd @@ -31,8 +31,9 @@ - + + @@ -44,6 +45,28 @@ + + + + + + + + + + + + + + + + + + + + + + @@ -207,12 +230,6 @@ - - - - - - A generic property used by other elements. diff --git a/cayenne-tools/src/main/java/org/apache/cayenne/gen/Artifact.java b/cayenne-tools/src/main/java/org/apache/cayenne/gen/Artifact.java index fcd9118fec..71e5c65680 100644 --- a/cayenne-tools/src/main/java/org/apache/cayenne/gen/Artifact.java +++ b/cayenne-tools/src/main/java/org/apache/cayenne/gen/Artifact.java @@ -18,6 +18,7 @@ ****************************************************************/ package org.apache.cayenne.gen; +import org.apache.cayenne.map.template.TemplateType; import org.apache.velocity.VelocityContext; /** diff --git a/cayenne-tools/src/main/java/org/apache/cayenne/gen/ClassGenerationAction.java b/cayenne-tools/src/main/java/org/apache/cayenne/gen/ClassGenerationAction.java index f7471a6718..a37f3fd815 100644 --- a/cayenne-tools/src/main/java/org/apache/cayenne/gen/ClassGenerationAction.java +++ b/cayenne-tools/src/main/java/org/apache/cayenne/gen/ClassGenerationAction.java @@ -30,6 +30,9 @@ import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.access.loader.NamePatternMatcher; +import org.apache.cayenne.map.template.ArtifactsGenerationMode; +import org.apache.cayenne.map.template.TemplateType; +import org.apache.cayenne.configuration.gen.ClassGeneratorResourceLoader; import org.apache.cayenne.map.DataMap; import org.apache.cayenne.map.Embeddable; import org.apache.cayenne.map.ObjEntity; @@ -42,543 +45,545 @@ import org.apache.velocity.runtime.log.NullLogSystem; public class ClassGenerationAction { - static final String TEMPLATES_DIR_NAME = "templates/v1_2/"; - - public static final String SINGLE_CLASS_TEMPLATE = TEMPLATES_DIR_NAME + "singleclass.vm"; - public static final String SUBCLASS_TEMPLATE = TEMPLATES_DIR_NAME + "subclass.vm"; - public static final String SUPERCLASS_TEMPLATE = TEMPLATES_DIR_NAME + "superclass.vm"; - - public static final String EMBEDDABLE_SINGLE_CLASS_TEMPLATE = TEMPLATES_DIR_NAME + "embeddable-singleclass.vm"; - public static final String EMBEDDABLE_SUBCLASS_TEMPLATE = TEMPLATES_DIR_NAME + "embeddable-subclass.vm"; - public static final String EMBEDDABLE_SUPERCLASS_TEMPLATE = TEMPLATES_DIR_NAME + "embeddable-superclass.vm"; - - public static final String DATAMAP_SINGLE_CLASS_TEMPLATE = TEMPLATES_DIR_NAME + "datamap-singleclass.vm"; - public static final String DATAMAP_SUBCLASS_TEMPLATE = TEMPLATES_DIR_NAME + "datamap-subclass.vm"; - public static final String DATAMAP_SUPERCLASS_TEMPLATE = TEMPLATES_DIR_NAME + "datamap-superclass.vm"; - - public static final String SUPERCLASS_PREFIX = "_"; - private static final String WILDCARD = "*"; - - protected Collection artifacts; - - protected String superPkg; - protected DataMap dataMap; - - protected ArtifactsGenerationMode artifactsGenerationMode; - protected boolean makePairs; - - protected Log logger; - protected File destDir; - protected boolean overwrite; - protected boolean usePkgPath; - - protected String template; - protected String superTemplate; - protected String embeddableTemplate; - protected String embeddableSuperTemplate; - protected String queryTemplate; - protected String querySuperTemplate; - protected long timestamp; - protected String outputPattern; - protected String encoding; - protected boolean createPropertyNames; - - // runtime ivars - protected VelocityContext context; - protected Map templateCache; - - public ClassGenerationAction() { - this.outputPattern = "*.java"; - this.timestamp = System.currentTimeMillis(); - this.usePkgPath = true; - this.makePairs = true; - this.context = new VelocityContext(); - this.templateCache = new HashMap<>(5); - - this.artifacts = new ArrayList(); - } - - protected String defaultTemplateName(TemplateType type) { - switch (type) { - case ENTITY_SINGLE_CLASS: - return ClassGenerationAction.SINGLE_CLASS_TEMPLATE; - case ENTITY_SUBCLASS: - return ClassGenerationAction.SUBCLASS_TEMPLATE; - case ENTITY_SUPERCLASS: - return ClassGenerationAction.SUPERCLASS_TEMPLATE; - case EMBEDDABLE_SUBCLASS: - return ClassGenerationAction.EMBEDDABLE_SUBCLASS_TEMPLATE; - case EMBEDDABLE_SUPERCLASS: - return ClassGenerationAction.EMBEDDABLE_SUPERCLASS_TEMPLATE; - case EMBEDDABLE_SINGLE_CLASS: - return ClassGenerationAction.EMBEDDABLE_SINGLE_CLASS_TEMPLATE; - case DATAMAP_SINGLE_CLASS: - return ClassGenerationAction.DATAMAP_SINGLE_CLASS_TEMPLATE; - case DATAMAP_SUPERCLASS: - return ClassGenerationAction.DATAMAP_SUPERCLASS_TEMPLATE; - case DATAMAP_SUBCLASS: - return ClassGenerationAction.DATAMAP_SUBCLASS_TEMPLATE; - default: - throw new IllegalArgumentException("Invalid template type: " + type); - } - } - - protected String customTemplateName(TemplateType type) { - switch (type) { - case ENTITY_SINGLE_CLASS: - return template; - case ENTITY_SUBCLASS: - return template; - case ENTITY_SUPERCLASS: - return superTemplate; - case EMBEDDABLE_SUBCLASS: - return embeddableTemplate; - case EMBEDDABLE_SUPERCLASS: - return embeddableSuperTemplate; - case DATAMAP_SINGLE_CLASS: - return queryTemplate; - case DATAMAP_SUPERCLASS: - return querySuperTemplate; - case DATAMAP_SUBCLASS: - return queryTemplate; - default: - throw new IllegalArgumentException("Invalid template type: " + type); - } - } - - /** - * Returns a String used to prefix class name to create a generated - * superclass. Default value is "_". - */ - protected String getSuperclassPrefix() { - return ClassGenerationAction.SUPERCLASS_PREFIX; - } - - /** - * VelocityContext initialization method called once per artifact. - */ - protected void resetContextForArtifact(Artifact artifact) { - StringUtils stringUtils = StringUtils.getInstance(); - - String qualifiedClassName = artifact.getQualifiedClassName(); - String packageName = stringUtils.stripClass(qualifiedClassName); - String className = stringUtils.stripPackageName(qualifiedClassName); - - String qualifiedBaseClassName = artifact.getQualifiedBaseClassName(); - String basePackageName = stringUtils.stripClass(qualifiedBaseClassName); - String baseClassName = stringUtils.stripPackageName(qualifiedBaseClassName); - - String superClassName = getSuperclassPrefix() + stringUtils.stripPackageName(qualifiedClassName); - - String superPackageName = this.superPkg; - if (superPackageName == null) { - superPackageName = packageName + ".auto"; - } - - context.put(Artifact.BASE_CLASS_KEY, baseClassName); - context.put(Artifact.BASE_PACKAGE_KEY, basePackageName); - - context.put(Artifact.SUB_CLASS_KEY, className); - context.put(Artifact.SUB_PACKAGE_KEY, packageName); - - context.put(Artifact.SUPER_CLASS_KEY, superClassName); - context.put(Artifact.SUPER_PACKAGE_KEY, superPackageName); - - context.put(Artifact.OBJECT_KEY, artifact.getObject()); - context.put(Artifact.STRING_UTILS_KEY, stringUtils); - - context.put(Artifact.CREATE_PROPERTY_NAMES, createPropertyNames); - } - - /** - * VelocityContext initialization method called once per each artifact and - * template type combination. - */ - protected void resetContextForArtifactTemplate(Artifact artifact, TemplateType templateType) { - context.put(Artifact.IMPORT_UTILS_KEY, new ImportUtils()); - artifact.postInitContext(context); - } - - /** - * Executes class generation once per each artifact. - */ - public void execute() throws Exception { - - validateAttributes(); - - try { - for (Artifact artifact : artifacts) { - execute(artifact); - } - } finally { - // must reset engine at the end of class generator run to avoid - // memory - // leaks and stale templates - this.templateCache.clear(); - } - } - - /** - * Executes class generation for a single artifact. - */ - protected void execute(Artifact artifact) throws Exception { - - resetContextForArtifact(artifact); - - ArtifactGenerationMode artifactMode = makePairs ? ArtifactGenerationMode.GENERATION_GAP - : ArtifactGenerationMode.SINGLE_CLASS; - - TemplateType[] templateTypes = artifact.getTemplateTypes(artifactMode); - for (TemplateType type : templateTypes) { - - try (Writer out = openWriter(type);) { - if (out != null) { - - resetContextForArtifactTemplate(artifact, type); - getTemplate(type).merge(context, out); - } - } - } - } - - protected Template getTemplate(TemplateType type) throws Exception { - - String templateName = customTemplateName(type); - if (templateName == null) { - templateName = defaultTemplateName(type); - } - - // Velocity < 1.5 has some memory problems, so we will create a - // VelocityEngine - // every time, and store templates in an internal cache, to avoid - // uncontrolled - // memory leaks... Presumably 1.5 fixes it. - - Template template = templateCache.get(templateName); - - if (template == null) { - - Properties props = new Properties(); - - // null logger that will prevent velocity.log from being generated - props.put(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, NullLogSystem.class.getName()); - props.put("resource.loader", "cayenne"); - props.put("cayenne.resource.loader.class", ClassGeneratorResourceLoader.class.getName()); - props.put("cayenne.resource.loader.cache", "false"); - - VelocityEngine velocityEngine = new VelocityEngine(); - velocityEngine.init(props); - - template = velocityEngine.getTemplate(templateName); - templateCache.put(templateName, template); - } - - return template; - } - - /** - * Validates the state of this class generator. Throws - * CayenneRuntimeException if it is in an inconsistent state. Called - * internally from "execute". - */ - protected void validateAttributes() { - if (destDir == null) { - throw new CayenneRuntimeException("'destDir' attribute is missing."); - } - - if (!destDir.isDirectory()) { - throw new CayenneRuntimeException("'destDir' is not a directory."); - } - - if (!destDir.canWrite()) { - throw new CayenneRuntimeException("Do not have write permissions for " + destDir); - } - } - - /** - * Sets the destDir. - */ - public void setDestDir(File destDir) { - this.destDir = destDir; - } - - /** - * Sets overwrite property. - */ - public void setOverwrite(boolean overwrite) { - this.overwrite = overwrite; - } - - /** - * Sets makepairs property. - */ - public void setMakePairs(boolean makePairs) { - this.makePairs = makePairs; - } - - /** - * Sets template property. - */ - public void setTemplate(String template) { - this.template = template; - } - - /** - * Sets superTemplate property. - */ - public void setSuperTemplate(String superTemplate) { - this.superTemplate = superTemplate; - } - - public void setQueryTemplate(String queryTemplate) { - this.queryTemplate = queryTemplate; - } - - public void setQuerySuperTemplate(String querySuperTemplate) { - this.querySuperTemplate = querySuperTemplate; - } - - /** - * Sets usepkgpath property. - */ - public void setUsePkgPath(boolean usePkgPath) { - this.usePkgPath = usePkgPath; - } - - /** - * Sets outputPattern property. - */ - public void setOutputPattern(String outputPattern) { - this.outputPattern = outputPattern; - } - - /** - * Sets createPropertyNames property. - */ - public void setCreatePropertyNames(boolean createPropertyNames) { - this.createPropertyNames = createPropertyNames; - } - - /** - * Opens a Writer to write generated output. Returned Writer is mapped to a - * filesystem file (although subclasses may override that). File location is - * determined from the current state of VelocityContext and the TemplateType - * passed as a parameter. Writer encoding is determined from the value of - * the "encoding" property. - */ - protected Writer openWriter(TemplateType templateType) throws Exception { - - File outFile = (templateType.isSuperclass()) ? fileForSuperclass() : fileForClass(); - if (outFile == null) { - return null; - } - - if (logger != null) { - String label = templateType.isSuperclass() ? "superclass" : "class"; - logger.info("Generating " + label + " file: " + outFile.getCanonicalPath()); - } - - // return writer with specified encoding - FileOutputStream out = new FileOutputStream(outFile); - - return (encoding != null) ? new OutputStreamWriter(out, encoding) : new OutputStreamWriter(out); - } - - /** - * Returns a target file where a generated superclass must be saved. If null - * is returned, class shouldn't be generated. - */ - protected File fileForSuperclass() throws Exception { - - String packageName = (String) context.get(Artifact.SUPER_PACKAGE_KEY); - String className = (String) context.get(Artifact.SUPER_CLASS_KEY); - - String filename = NamePatternMatcher.replaceWildcardInStringWithString(WILDCARD, outputPattern, className); - File dest = new File(mkpath(destDir, packageName), filename); - - // Ignore if the destination is newer than the map - // (internal timestamp), i.e. has been generated after the map was - // last saved AND the template is older than the destination file - if (dest.exists() && !isOld(dest)) { - - if (superTemplate == null) { - return null; - } - - File superTemplateFile = new File(superTemplate); - if (superTemplateFile.lastModified() < dest.lastModified()) { - return null; - } - } - - return dest; - } - - /** - * Returns a target file where a generated class must be saved. If null is - * returned, class shouldn't be generated. - */ - protected File fileForClass() throws Exception { - - String packageName = (String) context.get(Artifact.SUB_PACKAGE_KEY); - String className = (String) context.get(Artifact.SUB_CLASS_KEY); - - String filename = NamePatternMatcher.replaceWildcardInStringWithString(WILDCARD, outputPattern, className); - File dest = new File(mkpath(destDir, packageName), filename); - - if (dest.exists()) { - // no overwrite of subclasses - if (makePairs) { - return null; - } - - // skip if said so - if (!overwrite) { - return null; - } - - // Ignore if the destination is newer than the map - // (internal timestamp), i.e. has been generated after the map was - // last saved AND the template is older than the destination file - if (!isOld(dest)) { - - if (template == null) { - return null; - } - - File templateFile = new File(template); - if (templateFile.lastModified() < dest.lastModified()) { - return null; - } - } - } - - return dest; - } - - /** - * Returns true if file parameter is older than internal - * timestamp of this class generator. - */ - protected boolean isOld(File file) { - return file.lastModified() <= timestamp; - } - - /** - * Returns a File object corresponding to a directory where files that - * belong to pkgName package should reside. Creates any missing - * diectories below dest. - */ - protected File mkpath(File dest, String pkgName) throws Exception { - - if (!usePkgPath || pkgName == null) { - return dest; - } - - String path = pkgName.replace('.', File.separatorChar); - File fullPath = new File(dest, path); - if (!fullPath.isDirectory() && !fullPath.mkdirs()) { - throw new Exception("Error making path: " + fullPath); - } - - return fullPath; - } - - public void setTimestamp(long timestamp) { - this.timestamp = timestamp; - } - - /** - * Sets file encoding. If set to null, default system encoding will be used. - */ - public void setEncoding(String encoding) { - this.encoding = encoding; - } - - /** - * Sets "superPkg" property value. - */ - public void setSuperPkg(String superPkg) { - this.superPkg = superPkg; - } - - /** - * @param dataMap - * The dataMap to set. - */ - public void setDataMap(DataMap dataMap) { - this.dataMap = dataMap; - } - - /** - * Adds entities to the internal entity list. - */ - public void addEntities(Collection entities) { - if (artifactsGenerationMode == ArtifactsGenerationMode.ENTITY - || artifactsGenerationMode == ArtifactsGenerationMode.ALL) { - if (entities != null) { - for (ObjEntity entity : entities) { - artifacts.add(new EntityArtifact(entity)); - } - } - } - } - - public void addEmbeddables(Collection embeddables) { - if (artifactsGenerationMode == ArtifactsGenerationMode.ENTITY - || artifactsGenerationMode == ArtifactsGenerationMode.ALL) { - if (embeddables != null) { - for (Embeddable embeddable : embeddables) { - artifacts.add(new EmbeddableArtifact(embeddable)); - } - } - } - } - - public void addQueries(Collection queries) { - if (artifactsGenerationMode == ArtifactsGenerationMode.DATAMAP - || artifactsGenerationMode == ArtifactsGenerationMode.ALL) { - - // TODO: andrus 10.12.2010 - why not also check for empty query - // list?? Or - // create a better API for enabling DataMapArtifact - if (queries != null) { - artifacts.add(new DataMapArtifact(dataMap, queries)); - } - } - } - - /** - * Sets an optional shared VelocityContext. Useful with tools like VPP that - * can set custom values in the context, not known to Cayenne. - */ - public void setContext(VelocityContext context) { - this.context = context; - } - - /** - * Injects an optional logger that will be used to trace generated files at - * the info level. - */ - public void setLogger(Log logger) { - this.logger = logger; - } - - public void setEmbeddableTemplate(String embeddableTemplate) { - this.embeddableTemplate = embeddableTemplate; - } - - public void setEmbeddableSuperTemplate(String embeddableSuperTemplate) { - this.embeddableSuperTemplate = embeddableSuperTemplate; - } - - public void setArtifactsGenerationMode(String mode) { - if (ArtifactsGenerationMode.ENTITY.getLabel().equalsIgnoreCase(mode)) { - this.artifactsGenerationMode = ArtifactsGenerationMode.ENTITY; - } else if (ArtifactsGenerationMode.DATAMAP.getLabel().equalsIgnoreCase(mode)) { - this.artifactsGenerationMode = ArtifactsGenerationMode.DATAMAP; - } else { - this.artifactsGenerationMode = ArtifactsGenerationMode.ALL; - } - } -} + static final String TEMPLATES_DIR_NAME = "templates/v1_2/"; + + public static final String SINGLE_CLASS_TEMPLATE = TEMPLATES_DIR_NAME + "singleclass.vm"; + public static final String SUBCLASS_TEMPLATE = TEMPLATES_DIR_NAME + "subclass.vm"; + public static final String SUPERCLASS_TEMPLATE = TEMPLATES_DIR_NAME + "superclass.vm"; + + public static final String EMBEDDABLE_SINGLE_CLASS_TEMPLATE = TEMPLATES_DIR_NAME + "embeddable-singleclass.vm"; + public static final String EMBEDDABLE_SUBCLASS_TEMPLATE = TEMPLATES_DIR_NAME + "embeddable-subclass.vm"; + public static final String EMBEDDABLE_SUPERCLASS_TEMPLATE = TEMPLATES_DIR_NAME + "embeddable-superclass.vm"; + + public static final String DATAMAP_SINGLE_CLASS_TEMPLATE = TEMPLATES_DIR_NAME + "datamap-singleclass.vm"; + public static final String DATAMAP_SUBCLASS_TEMPLATE = TEMPLATES_DIR_NAME + "datamap-subclass.vm"; + public static final String DATAMAP_SUPERCLASS_TEMPLATE = TEMPLATES_DIR_NAME + "datamap-superclass.vm"; + + public static final String SUPERCLASS_PREFIX = "_"; + private static final String WILDCARD = "*"; + + protected Collection artifacts; + + protected String superPkg; + protected DataMap dataMap; + + protected ArtifactsGenerationMode artifactsGenerationMode; + protected boolean makePairs; + + protected Log logger; + protected File destDir; + protected boolean overwrite; + protected boolean usePkgPath; + + protected String template; + protected String superTemplate; + protected String embeddableTemplate; + protected String embeddableSuperTemplate; + protected String queryTemplate; + protected String querySuperTemplate; + protected long timestamp; + protected String outputPattern; + protected String encoding; + protected boolean createPropertyNames; + + // runtime ivars + protected VelocityContext context; + protected Map templateCache; + + public ClassGenerationAction() { + this.outputPattern = "*.java"; + this.timestamp = System.currentTimeMillis(); + this.usePkgPath = true; + this.makePairs = true; + this.context = new VelocityContext(); + this.templateCache = new HashMap<>(5); + + this.artifacts = new ArrayList(); + } + + protected String defaultTemplateName(TemplateType type) { + switch (type) { + case ENTITY_SINGLE_CLASS: + return ClassGenerationAction.SINGLE_CLASS_TEMPLATE; + case ENTITY_SUBCLASS: + return ClassGenerationAction.SUBCLASS_TEMPLATE; + case ENTITY_SUPERCLASS: + return ClassGenerationAction.SUPERCLASS_TEMPLATE; + case EMBEDDABLE_SUBCLASS: + return ClassGenerationAction.EMBEDDABLE_SUBCLASS_TEMPLATE; + case EMBEDDABLE_SUPERCLASS: + return ClassGenerationAction.EMBEDDABLE_SUPERCLASS_TEMPLATE; + case EMBEDDABLE_SINGLE_CLASS: + return ClassGenerationAction.EMBEDDABLE_SINGLE_CLASS_TEMPLATE; + case DATAMAP_SINGLE_CLASS: + return ClassGenerationAction.DATAMAP_SINGLE_CLASS_TEMPLATE; + case DATAMAP_SUPERCLASS: + return ClassGenerationAction.DATAMAP_SUPERCLASS_TEMPLATE; + case DATAMAP_SUBCLASS: + return ClassGenerationAction.DATAMAP_SUBCLASS_TEMPLATE; + default: + throw new IllegalArgumentException("Invalid template type: " + type); + } + } + + protected String customTemplateName(TemplateType type) { + switch (type) { + case ENTITY_SINGLE_CLASS: + return template; + case ENTITY_SUBCLASS: + return template; + case ENTITY_SUPERCLASS: + return superTemplate; + case EMBEDDABLE_SINGLE_CLASS: + return embeddableTemplate; + case EMBEDDABLE_SUBCLASS: + return embeddableTemplate; + case EMBEDDABLE_SUPERCLASS: + return embeddableSuperTemplate; + case DATAMAP_SINGLE_CLASS: + return queryTemplate; + case DATAMAP_SUPERCLASS: + return querySuperTemplate; + case DATAMAP_SUBCLASS: + return queryTemplate; + default: + throw new IllegalArgumentException("Invalid template type: " + type); + } + } + + /** + * Returns a String used to prefix class name to create a generated + * superclass. Default value is "_". + */ + protected String getSuperclassPrefix() { + return ClassGenerationAction.SUPERCLASS_PREFIX; + } + + /** + * VelocityContext initialization method called once per artifact. + */ + protected void resetContextForArtifact(Artifact artifact) { + StringUtils stringUtils = StringUtils.getInstance(); + + String qualifiedClassName = artifact.getQualifiedClassName(); + String packageName = stringUtils.stripClass(qualifiedClassName); + String className = stringUtils.stripPackageName(qualifiedClassName); + + String qualifiedBaseClassName = artifact.getQualifiedBaseClassName(); + String basePackageName = stringUtils.stripClass(qualifiedBaseClassName); + String baseClassName = stringUtils.stripPackageName(qualifiedBaseClassName); + + String superClassName = getSuperclassPrefix() + stringUtils.stripPackageName(qualifiedClassName); + + String superPackageName = this.superPkg; + if (superPackageName == null) { + superPackageName = packageName + ".auto"; + } + + context.put(Artifact.BASE_CLASS_KEY, baseClassName); + context.put(Artifact.BASE_PACKAGE_KEY, basePackageName); + + context.put(Artifact.SUB_CLASS_KEY, className); + context.put(Artifact.SUB_PACKAGE_KEY, packageName); + + context.put(Artifact.SUPER_CLASS_KEY, superClassName); + context.put(Artifact.SUPER_PACKAGE_KEY, superPackageName); + + context.put(Artifact.OBJECT_KEY, artifact.getObject()); + context.put(Artifact.STRING_UTILS_KEY, stringUtils); + + context.put(Artifact.CREATE_PROPERTY_NAMES, createPropertyNames); + } + + /** + * VelocityContext initialization method called once per each artifact and + * template type combination. + */ + protected void resetContextForArtifactTemplate(Artifact artifact, TemplateType templateType) { + context.put(Artifact.IMPORT_UTILS_KEY, new ImportUtils()); + artifact.postInitContext(context); + } + + /** + * Executes class generation once per each artifact. + */ + public void execute() throws Exception { + + validateAttributes(); + + try { + for (Artifact artifact : artifacts) { + execute(artifact); + } + } finally { + // must reset engine at the end of class generator run to avoid + // memory + // leaks and stale templates + this.templateCache.clear(); + } + } + + /** + * Executes class generation for a single artifact. + */ + protected void execute(Artifact artifact) throws Exception { + + resetContextForArtifact(artifact); + + ArtifactGenerationMode artifactMode = makePairs ? ArtifactGenerationMode.GENERATION_GAP + : ArtifactGenerationMode.SINGLE_CLASS; + + TemplateType[] templateTypes = artifact.getTemplateTypes(artifactMode); + for (TemplateType type : templateTypes) { + + try (Writer out = openWriter(type);) { + if (out != null) { + + resetContextForArtifactTemplate(artifact, type); + getTemplate(type).merge(context, out); + } + } + } + } + + protected Template getTemplate(TemplateType type) throws Exception { + + String templateName = customTemplateName(type); + if (templateName == null) { + templateName = defaultTemplateName(type); + } + + // Velocity < 1.5 has some memory problems, so we will create a + // VelocityEngine + // every time, and store templates in an internal cache, to avoid + // uncontrolled + // memory leaks... Presumably 1.5 fixes it. + + Template template = templateCache.get(templateName); + + if (template == null) { + + Properties props = new Properties(); + + // null logger that will prevent velocity.log from being generated + props.put(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, NullLogSystem.class.getName()); + props.put("resource.loader", "cayenne"); + props.put("cayenne.resource.loader.class", ClassGeneratorResourceLoader.class.getName()); + props.put("cayenne.resource.loader.cache", "false"); + + VelocityEngine velocityEngine = new VelocityEngine(); + velocityEngine.init(props); + + template = velocityEngine.getTemplate(templateName); + templateCache.put(templateName, template); + } + + return template; + } + + /** + * Validates the state of this class generator. Throws + * CayenneRuntimeException if it is in an inconsistent state. Called + * internally from "execute". + */ + protected void validateAttributes() { + if (destDir == null) { + throw new CayenneRuntimeException("'destDir' attribute is missing."); + } + + if (!destDir.isDirectory()) { + throw new CayenneRuntimeException("'destDir' is not a directory."); + } + + if (!destDir.canWrite()) { + throw new CayenneRuntimeException("Do not have write permissions for " + destDir); + } + } + + /** + * Sets the destDir. + */ + public void setDestDir(File destDir) { + this.destDir = destDir; + } + + /** + * Sets overwrite property. + */ + public void setOverwrite(boolean overwrite) { + this.overwrite = overwrite; + } + + /** + * Sets makepairs property. + */ + public void setMakePairs(boolean makePairs) { + this.makePairs = makePairs; + } + + /** + * Sets template property. + */ + public void setTemplate(String template) { + this.template = template; + } + + /** + * Sets superTemplate property. + */ + public void setSuperTemplate(String superTemplate) { + this.superTemplate = superTemplate; + } + + public void setQueryTemplate(String queryTemplate) { + this.queryTemplate = queryTemplate; + } + + public void setQuerySuperTemplate(String querySuperTemplate) { + this.querySuperTemplate = querySuperTemplate; + } + + /** + * Sets usepkgpath property. + */ + public void setUsePkgPath(boolean usePkgPath) { + this.usePkgPath = usePkgPath; + } + + /** + * Sets outputPattern property. + */ + public void setOutputPattern(String outputPattern) { + this.outputPattern = outputPattern; + } + + /** + * Sets createPropertyNames property. + */ + public void setCreatePropertyNames(boolean createPropertyNames) { + this.createPropertyNames = createPropertyNames; + } + + /** + * Opens a Writer to write generated output. Returned Writer is mapped to a + * filesystem file (although subclasses may override that). File location is + * determined from the current state of VelocityContext and the TemplateType + * passed as a parameter. Writer encoding is determined from the value of + * the "encoding" property. + */ + protected Writer openWriter(TemplateType templateType) throws Exception { + + File outFile = (templateType.isSuperclass()) ? fileForSuperclass() : fileForClass(); + if (outFile == null) { + return null; + } + + if (logger != null) { + String label = templateType.isSuperclass() ? "superclass" : "class"; + logger.info("Generating " + label + " file: " + outFile.getCanonicalPath()); + } + + // return writer with specified encoding + FileOutputStream out = new FileOutputStream(outFile); + + return (encoding != null) ? new OutputStreamWriter(out, encoding) : new OutputStreamWriter(out); + } + + /** + * Returns a target file where a generated superclass must be saved. If null + * is returned, class shouldn't be generated. + */ + protected File fileForSuperclass() throws Exception { + + String packageName = (String) context.get(Artifact.SUPER_PACKAGE_KEY); + String className = (String) context.get(Artifact.SUPER_CLASS_KEY); + + String filename = NamePatternMatcher.replaceWildcardInStringWithString(WILDCARD, outputPattern, className); + File dest = new File(mkpath(destDir, packageName), filename); + + // Ignore if the destination is newer than the map + // (internal timestamp), i.e. has been generated after the map was + // last saved AND the template is older than the destination file + if (dest.exists() && !isOld(dest)) { + + if (superTemplate == null) { + return null; + } + + File superTemplateFile = new File(superTemplate); + if (superTemplateFile.lastModified() < dest.lastModified()) { + return null; + } + } + + return dest; + } + + /** + * Returns a target file where a generated class must be saved. If null is + * returned, class shouldn't be generated. + */ + protected File fileForClass() throws Exception { + + String packageName = (String) context.get(Artifact.SUB_PACKAGE_KEY); + String className = (String) context.get(Artifact.SUB_CLASS_KEY); + + String filename = NamePatternMatcher.replaceWildcardInStringWithString(WILDCARD, outputPattern, className); + File dest = new File(mkpath(destDir, packageName), filename); + + if (dest.exists()) { + // no overwrite of subclasses + if (makePairs) { + return null; + } + + // skip if said so + if (!overwrite) { + return null; + } + + // Ignore if the destination is newer than the map + // (internal timestamp), i.e. has been generated after the map was + // last saved AND the template is older than the destination file + if (!isOld(dest)) { + + if (template == null) { + return null; + } + + File templateFile = new File(template); + if (templateFile.lastModified() < dest.lastModified()) { + return null; + } + } + } + + return dest; + } + + /** + * Returns true if file parameter is older than internal + * timestamp of this class generator. + */ + protected boolean isOld(File file) { + return file.lastModified() <= timestamp; + } + + /** + * Returns a File object corresponding to a directory where files that + * belong to pkgName package should reside. Creates any missing + * diectories below dest. + */ + protected File mkpath(File dest, String pkgName) throws Exception { + + if (!usePkgPath || pkgName == null) { + return dest; + } + + String path = pkgName.replace('.', File.separatorChar); + File fullPath = new File(dest, path); + if (!fullPath.isDirectory() && !fullPath.mkdirs()) { + throw new Exception("Error making path: " + fullPath); + } + + return fullPath; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + + /** + * Sets file encoding. If set to null, default system encoding will be used. + */ + public void setEncoding(String encoding) { + this.encoding = encoding; + } + + /** + * Sets "superPkg" property value. + */ + public void setSuperPkg(String superPkg) { + this.superPkg = superPkg; + } + + /** + * @param dataMap + * The dataMap to set. + */ + public void setDataMap(DataMap dataMap) { + this.dataMap = dataMap; + } + + /** + * Adds entities to the internal entity list. + */ + public void addEntities(Collection entities) { + if (artifactsGenerationMode == ArtifactsGenerationMode.ENTITY + || artifactsGenerationMode == ArtifactsGenerationMode.ALL) { + if (entities != null) { + for (ObjEntity entity : entities) { + artifacts.add(new EntityArtifact(entity)); + } + } + } + } + + public void addEmbeddables(Collection embeddables) { + if (artifactsGenerationMode == ArtifactsGenerationMode.ENTITY + || artifactsGenerationMode == ArtifactsGenerationMode.ALL) { + if (embeddables != null) { + for (Embeddable embeddable : embeddables) { + artifacts.add(new EmbeddableArtifact(embeddable)); + } + } + } + } + + public void addQueries(Collection queries) { + if (artifactsGenerationMode == ArtifactsGenerationMode.DATAMAP + || artifactsGenerationMode == ArtifactsGenerationMode.ALL) { + + // TODO: andrus 10.12.2010 - why not also check for empty query + // list?? Or + // create a better API for enabling DataMapArtifact + if (queries != null) { + artifacts.add(new DataMapArtifact(dataMap, queries)); + } + } + } + + /** + * Sets an optional shared VelocityContext. Useful with tools like VPP that + * can set custom values in the context, not known to Cayenne. + */ + public void setContext(VelocityContext context) { + this.context = context; + } + + /** + * Injects an optional logger that will be used to trace generated files at + * the info level. + */ + public void setLogger(Log logger) { + this.logger = logger; + } + + public void setEmbeddableTemplate(String embeddableTemplate) { + this.embeddableTemplate = embeddableTemplate; + } + + public void setEmbeddableSuperTemplate(String embeddableSuperTemplate) { + this.embeddableSuperTemplate = embeddableSuperTemplate; + } + + public void setArtifactsGenerationMode(String mode) { + if (ArtifactsGenerationMode.ENTITY.getLabel().equalsIgnoreCase(mode)) { + this.artifactsGenerationMode = ArtifactsGenerationMode.ENTITY; + } else if (ArtifactsGenerationMode.DATAMAP.getLabel().equalsIgnoreCase(mode)) { + this.artifactsGenerationMode = ArtifactsGenerationMode.DATAMAP; + } else { + this.artifactsGenerationMode = ArtifactsGenerationMode.ALL; + } + } +} \ No newline at end of file diff --git a/cayenne-tools/src/main/java/org/apache/cayenne/gen/ClientClassGenerationAction.java b/cayenne-tools/src/main/java/org/apache/cayenne/gen/ClientClassGenerationAction.java index 0b9a5553bd..0a556eda3f 100644 --- a/cayenne-tools/src/main/java/org/apache/cayenne/gen/ClientClassGenerationAction.java +++ b/cayenne-tools/src/main/java/org/apache/cayenne/gen/ClientClassGenerationAction.java @@ -20,6 +20,7 @@ import java.util.Collection; +import org.apache.cayenne.map.template.TemplateType; import org.apache.cayenne.map.ObjEntity; import org.apache.cayenne.map.QueryDescriptor; @@ -30,11 +31,11 @@ public class ClientClassGenerationAction extends ClassGenerationAction { public static final String SUBCLASS_TEMPLATE = TEMPLATES_DIR_NAME + "client-subclass.vm"; public static final String SUPERCLASS_TEMPLATE = TEMPLATES_DIR_NAME + "client-superclass.vm"; - + public static final String DMAP_SINGLE_CLASS_TEMPLATE = TEMPLATES_DIR_NAME + "client-datamap-singleclass.vm"; public static final String DMAP_SUBCLASS_TEMPLATE = TEMPLATES_DIR_NAME + "client-datamap-subclass.vm"; public static final String DMAP_SUPERCLASS_TEMPLATE = TEMPLATES_DIR_NAME + "client-datamap-superclass.vm"; - + public static final String CLIENT_SUPERCLASS_PREFIX = "_Client"; @Override @@ -48,7 +49,7 @@ protected String defaultTemplateName(TemplateType type) { return ClassGenerationAction.EMBEDDABLE_SUBCLASS_TEMPLATE; case EMBEDDABLE_SUPERCLASS: return ClassGenerationAction.EMBEDDABLE_SUPERCLASS_TEMPLATE; - + case DATAMAP_SUPERCLASS: return ClientClassGenerationAction.DMAP_SUPERCLASS_TEMPLATE; case DATAMAP_SUBCLASS: @@ -66,11 +67,11 @@ public void addEntities(Collection entities) { } } } - + @Override public void addQueries(Collection queries) { if (queries != null) { artifacts.add(new ClientDataMapArtifact(dataMap, queries)); } } -} +} \ No newline at end of file diff --git a/cayenne-tools/src/main/java/org/apache/cayenne/gen/DataMapArtifact.java b/cayenne-tools/src/main/java/org/apache/cayenne/gen/DataMapArtifact.java index 838f1c693f..83f10477a1 100644 --- a/cayenne-tools/src/main/java/org/apache/cayenne/gen/DataMapArtifact.java +++ b/cayenne-tools/src/main/java/org/apache/cayenne/gen/DataMapArtifact.java @@ -22,6 +22,7 @@ import java.util.Collection; import java.util.LinkedList; +import org.apache.cayenne.map.template.TemplateType; import org.apache.cayenne.map.DataMap; import org.apache.cayenne.map.QueryDescriptor; import org.apache.cayenne.map.naming.NameConverter; diff --git a/cayenne-tools/src/main/java/org/apache/cayenne/gen/EmbeddableArtifact.java b/cayenne-tools/src/main/java/org/apache/cayenne/gen/EmbeddableArtifact.java index 8e6ea43b7a..e6b676449d 100644 --- a/cayenne-tools/src/main/java/org/apache/cayenne/gen/EmbeddableArtifact.java +++ b/cayenne-tools/src/main/java/org/apache/cayenne/gen/EmbeddableArtifact.java @@ -18,6 +18,7 @@ ****************************************************************/ package org.apache.cayenne.gen; +import org.apache.cayenne.map.template.TemplateType; import org.apache.cayenne.map.Embeddable; import org.apache.velocity.VelocityContext; diff --git a/cayenne-tools/src/main/java/org/apache/cayenne/gen/EntityArtifact.java b/cayenne-tools/src/main/java/org/apache/cayenne/gen/EntityArtifact.java index 394304fc60..87015d2599 100644 --- a/cayenne-tools/src/main/java/org/apache/cayenne/gen/EntityArtifact.java +++ b/cayenne-tools/src/main/java/org/apache/cayenne/gen/EntityArtifact.java @@ -19,6 +19,7 @@ package org.apache.cayenne.gen; import org.apache.cayenne.CayenneDataObject; +import org.apache.cayenne.map.template.TemplateType; import org.apache.cayenne.map.ObjEntity; import org.apache.velocity.VelocityContext; diff --git a/cayenne-tools/src/main/java/org/apache/cayenne/tools/CayenneGeneratorTask.java b/cayenne-tools/src/main/java/org/apache/cayenne/tools/CayenneGeneratorTask.java index 6ed29cbaaa..49ecaa1fbe 100644 --- a/cayenne-tools/src/main/java/org/apache/cayenne/tools/CayenneGeneratorTask.java +++ b/cayenne-tools/src/main/java/org/apache/cayenne/tools/CayenneGeneratorTask.java @@ -20,19 +20,37 @@ import foundrylogic.vpp.VPPConfig; import org.apache.cayenne.access.loader.NamePatternMatcher; -import org.apache.cayenne.gen.ArtifactsGenerationMode; +import org.apache.cayenne.map.template.ArtifactsGenerationMode; +import org.apache.cayenne.map.template.ClassTemplate; +import org.apache.cayenne.map.template.TemplateType; +import org.apache.cayenne.configuration.ConfigurationNameMapper; +import org.apache.cayenne.configuration.server.ServerModule; +import org.apache.cayenne.di.DIBootstrap; +import org.apache.cayenne.di.Injector; import org.apache.cayenne.gen.ClassGenerationAction; +import org.apache.cayenne.map.template.ClassGenerationDescriptor; import org.apache.cayenne.gen.ClientClassGenerationAction; import org.apache.cayenne.map.DataMap; +import org.apache.cayenne.resource.Resource; +import org.apache.cayenne.resource.URLResource; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.types.Path; import org.apache.velocity.VelocityContext; import java.io.File; +import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; +import java.net.URLDecoder; +import java.util.HashMap; +import java.util.Map; + +import static org.apache.cayenne.map.template.TemplateType.*; +import static org.apache.cayenne.map.template.TemplateType.DATAMAP_SUBCLASS; +import static org.apache.cayenne.map.template.TemplateType.DATAMAP_SUPERCLASS; /** * An Ant task to perform class generation based on CayenneDataMap. - * + * * @since 3.0 */ public class CayenneGeneratorTask extends CayenneTask { @@ -59,6 +77,8 @@ public class CayenneGeneratorTask extends CayenneTask { protected String querysupertemplate; protected boolean usepkgpath; protected boolean createpropertynames; + protected boolean isClassGenerationDefined; + public CayenneGeneratorTask() { this.makepairs = true; @@ -72,29 +92,6 @@ protected VelocityContext getVppContext() { return vppConfig.getVelocityContext(); } - protected ClassGenerationAction createGeneratorAction() { - ClassGenerationAction action = client ? new ClientClassGenerationAction() : new ClassGenerationAction(); - - action.setContext(getVppContext()); - action.setDestDir(destDir); - action.setEncoding(encoding); - action.setMakePairs(makepairs); - action.setArtifactsGenerationMode(mode); - action.setOutputPattern(outputPattern); - action.setOverwrite(overwrite); - action.setSuperPkg(superpkg); - action.setSuperTemplate(supertemplate); - action.setTemplate(template); - action.setEmbeddableSuperTemplate(embeddablesupertemplate); - action.setEmbeddableTemplate(embeddabletemplate); - action.setQueryTemplate(querytemplate); - action.setQuerySuperTemplate(querysupertemplate); - action.setUsePkgPath(usepkgpath); - action.setCreatePropertyNames(createpropertynames); - - return action; - } - /** * Executes the task. It will be called by ant framework. */ @@ -116,7 +113,7 @@ public void execute() throws BuildException { DataMap dataMap = loadAction.getMainDataMap(); - ClassGenerationAction generatorAction = createGeneratorAction(); + ClassGenerationAction generatorAction = createGeneratorAction(dataMap); generatorAction.setLogger(logger); generatorAction.setTimestamp(map.lastModified()); generatorAction.setDataMap(dataMap); @@ -124,8 +121,7 @@ public void execute() throws BuildException { generatorAction.addEmbeddables(filterAction.getFilteredEmbeddables(dataMap)); generatorAction.addQueries(dataMap.getQueryDescriptors()); generatorAction.execute(); - } - catch (Exception e) { + } catch (Exception e) { throw new BuildException(e); } } @@ -142,7 +138,7 @@ protected void validateAttributes() throws BuildException { /** * Sets the map. - * + * * @param map The map to set */ public void setMap(File map) { @@ -151,7 +147,7 @@ public void setMap(File map) { /** * Sets the additional DataMaps. - * + * * @param additionalMapsPath The additional DataMaps to set */ public void setAdditionalMaps(Path additionalMapsPath) { @@ -188,6 +184,7 @@ public void setMakepairs(boolean makepairs) { * Sets template property. */ public void setTemplate(String template) { + isClassGenerationDefined = true; this.template = template; } @@ -195,6 +192,7 @@ public void setTemplate(String template) { * Sets supertemplate property. */ public void setSupertemplate(String supertemplate) { + isClassGenerationDefined = true; this.supertemplate = supertemplate; } @@ -202,6 +200,7 @@ public void setSupertemplate(String supertemplate) { * Sets querytemplate property. */ public void setQueryTemplate(String querytemplate) { + isClassGenerationDefined = true; this.querytemplate = querytemplate; } @@ -209,6 +208,7 @@ public void setQueryTemplate(String querytemplate) { * Sets querysupertemplate property. */ public void setQuerySupertemplate(String querysupertemplate) { + isClassGenerationDefined = true; this.querysupertemplate = querysupertemplate; } @@ -301,4 +301,201 @@ private void initializeVppConfig() { vppConfig = VPPConfig.getDefaultConfig(getProject()); } } + + protected ClassGenerationAction createGeneratorAction(DataMap dataMap) + throws UnsupportedEncodingException, MalformedURLException { + + ClassGenerationAction action; + if (client) { + action = new ClientClassGenerationAction(); + } else { + action = new ClassGenerationAction(); + } + + if (isClassGenerationDefined) { + createGenerator(action); + } else { + if (dataMap.getClassGenerationDescriptor() != null) { + createGeneratorFromMap(action, dataMap.getClassGenerationDescriptor()); + } + } + + action.setDestDir(destDir); + action.setEncoding(encoding); + action.setMakePairs(makepairs); + action.setOutputPattern(outputPattern); + action.setOverwrite(overwrite); + action.setSuperPkg(superpkg); + action.setUsePkgPath(usepkgpath); + action.setCreatePropertyNames(createpropertynames); + + return action; + } + + protected void createGenerator(ClassGenerationAction action) { + action.setArtifactsGenerationMode(mode); + action.setSuperTemplate(supertemplate); + action.setTemplate(template); + action.setEmbeddableSuperTemplate(embeddablesupertemplate); + action.setEmbeddableTemplate(embeddabletemplate); + } + + abstract static class GeneratorByTemplate { + + public static Map GENERATORS = new HashMap<>(); + + public static void setTemplate_(ClassGenerationAction action, ClassTemplate template, File map) + throws UnsupportedEncodingException, MalformedURLException { + + GeneratorByTemplate generator = GENERATORS.get(template.getType()); + if (generator == null) { + throw new IllegalArgumentException("Invalid template type: " + template.getType()); + } + Injector injector = DIBootstrap.createInjector(new ServerModule()); + ConfigurationNameMapper nameMapper = injector.getInstance(ConfigurationNameMapper.class); + String templateLocation = nameMapper.configurationLocation(ClassTemplate.class, template.getName()); + Resource templateResource = new URLResource(map.toURI().toURL()).getRelativeResource(templateLocation); + template.setConfigurationSource(templateResource); + generator.setTemplate(action, template); + } + + static { + GENERATORS.put(ENTITY_SINGLE_CLASS, new GeneratorByTemplate() { + @Override + void setTemplate(ClassGenerationAction action, ClassTemplate template) throws UnsupportedEncodingException { + if (!isTemplateDefined) { + String path = URLDecoder.decode(template.getConfigurationSource().getURL().getPath(), "UTF-8"); + action.setTemplate(path); + isTemplateDefined = true; + } else { + throw new IllegalStateException("Entity template defined more than one time in datamap. " + + "Delete redundant ones or use mvn plugin"); + } + } + }); + + GENERATORS.put(ENTITY_SUPERCLASS, new GeneratorByTemplate() { + @Override + void setTemplate(ClassGenerationAction action, ClassTemplate template) throws UnsupportedEncodingException { + if (!isTemplateDefined) { + String path = URLDecoder.decode(template.getConfigurationSource().getURL().getPath(), "UTF-8"); + action.setSuperTemplate(path); + isTemplateDefined = true; + } else { + throw new IllegalStateException("Entity super template defined more than one time in datamap. " + + "Delete redundant ones or use mvn plugin"); + } + } + }); + + GENERATORS.put(ENTITY_SUBCLASS, new GeneratorByTemplate() { + @Override + void setTemplate(ClassGenerationAction action, ClassTemplate template) { + if (!isTemplateDefined) { + isTemplateDefined = true; + } else { + throw new IllegalStateException("Entity sub template defined more than one time in datamap. " + + "Delete redundant ones or use mvn plugin"); + } + } + }); + + GENERATORS.put(EMBEDDABLE_SINGLE_CLASS, new GeneratorByTemplate() { + @Override + void setTemplate(ClassGenerationAction action, ClassTemplate template) throws UnsupportedEncodingException { + if (!isTemplateDefined) { + String path = URLDecoder.decode(template.getConfigurationSource().getURL().getPath(), "UTF-8"); + action.setEmbeddableTemplate(path); + isTemplateDefined = true; + } else { + throw new IllegalStateException("Embeddable template defined more than one time in datamap. " + + "Delete redundant ones or use mvn plugin"); + } + } + }); + + GENERATORS.put(EMBEDDABLE_SUPERCLASS, new GeneratorByTemplate() { + @Override + void setTemplate(ClassGenerationAction action, ClassTemplate template) throws UnsupportedEncodingException { + if (!isTemplateDefined) { + String path = URLDecoder.decode(template.getConfigurationSource().getURL().getPath(), "UTF-8"); + action.setEmbeddableSuperTemplate(path); + isTemplateDefined = true; + } else { + throw new IllegalStateException("Embeddable super template defined more than one time in datamap. " + + "Delete redundant ones or use mvn plugin"); + } + } + }); + + GENERATORS.put(EMBEDDABLE_SUBCLASS, new GeneratorByTemplate() { + @Override + void setTemplate(ClassGenerationAction action, ClassTemplate template) { + if (!isTemplateDefined) { + isTemplateDefined = true; + } else { + throw new IllegalStateException("Embeddable sub template defined more than one time in datamap. " + + "Delete redundant ones or use mvn plugin"); + } + } + }); + + GENERATORS.put(DATAMAP_SINGLE_CLASS, new GeneratorByTemplate() { + @Override + void setTemplate(ClassGenerationAction action, ClassTemplate template) throws UnsupportedEncodingException { + if (!isTemplateDefined) { + String path = URLDecoder.decode(template.getConfigurationSource().getURL().getPath(), "UTF-8"); + action.setQueryTemplate(path); + isTemplateDefined = true; + } else { + throw new IllegalStateException("Query template defined more than one time in datamap. " + + "Delete redundant ones or use mvn plugin"); + } + } + }); + + GENERATORS.put(DATAMAP_SUPERCLASS, new GeneratorByTemplate() { + @Override + void setTemplate(ClassGenerationAction action, ClassTemplate template) throws UnsupportedEncodingException { + if (!isTemplateDefined) { + String path = URLDecoder.decode(template.getConfigurationSource().getURL().getPath(), "UTF-8"); + action.setQuerySuperTemplate(path); + isTemplateDefined = true; + } else { + throw new IllegalStateException("Query super template defined more than one time in datamap. " + + "Delete redundant ones or use mvn plugin"); + } + } + }); + + GENERATORS.put(DATAMAP_SUBCLASS, new GeneratorByTemplate() { + @Override + void setTemplate(ClassGenerationAction action, ClassTemplate template) { + if (!isTemplateDefined) { + isTemplateDefined = true; + } else { + throw new IllegalStateException("Query sub template defined more than one time in datamap. " + + "Delete redundant ones or use mvn plugin"); + } + } + }); + } + + boolean isTemplateDefined = false; + + abstract void setTemplate(ClassGenerationAction action, ClassTemplate template) throws UnsupportedEncodingException; + + } + + protected void createGeneratorFromMap(ClassGenerationAction action, ClassGenerationDescriptor descriptor) + throws UnsupportedEncodingException, MalformedURLException { + if (descriptor.getArtifactsGenerationMode() != null) { + action.setArtifactsGenerationMode(descriptor.getArtifactsGenerationMode().getLabel()); + } else { + action.setArtifactsGenerationMode(mode); + } + for (ClassTemplate template : descriptor.getTemplates().values()) { + GeneratorByTemplate.setTemplate_(action, template, map); + } + } } diff --git a/cayenne-tools/src/main/java/org/apache/cayenne/tools/DbImporterTask.java b/cayenne-tools/src/main/java/org/apache/cayenne/tools/DbImporterTask.java index 119ebf41e0..282a5bdadb 100644 --- a/cayenne-tools/src/main/java/org/apache/cayenne/tools/DbImporterTask.java +++ b/cayenne-tools/src/main/java/org/apache/cayenne/tools/DbImporterTask.java @@ -101,50 +101,48 @@ public void execute() { } finally { injector.shutdown(); } - } else { - if (dataMapFile.exists()) { - try { - URL url = dataMapFile.toURI().toURL(); - URLResource resource = new URLResource(url); - - XMLDataMapLoader xmlDataMapLoader = new XMLDataMapLoader(); - DataMap dataMap = xmlDataMapLoader.load(resource); - if (dataMap.getReverseEngineering() != null) { - Injector injector = DIBootstrap.createInjector(new ToolsModule(logger), new DbImportModule()); - try { - ConfigurationNameMapper nameMapper = injector.getInstance(ConfigurationNameMapper.class); - String reverseEngineeringLocation = nameMapper.configurationLocation(ReverseEngineering.class, dataMap.getReverseEngineering().getName()); - Resource reverseEngineeringResource = new URLResource(dataMapFile.toURI().toURL()).getRelativeResource(reverseEngineeringLocation); - - DefaultReverseEngineeringLoader reverseEngineeringLoader = new DefaultReverseEngineeringLoader(); - ReverseEngineering reverseEngineering = reverseEngineeringLoader.load(reverseEngineeringResource.getURL().openStream()); - reverseEngineering.setName(dataMap.getReverseEngineering().getName()); - reverseEngineering.setConfigurationSource(reverseEngineeringResource); - dataMap.setReverseEngineering(reverseEngineering); - - FiltersConfigBuilder filtersConfigBuilder = new FiltersConfigBuilder(dataMap.getReverseEngineering()); - config.getDbLoaderConfig().setFiltersConfig(filtersConfigBuilder.filtersConfig()); - validateDbImportConfiguration(config, injector); - injector.getInstance(DbImportAction.class).execute(config); - } catch (Exception ex) { - Throwable th = Util.unwindException(ex); - - String message = "Error importing database schema"; - - if (th.getLocalizedMessage() != null) { - message += ": " + th.getLocalizedMessage(); - } - - log(message, Project.MSG_ERR); - throw new BuildException(message, th); - } finally { - injector.shutdown(); + } else if (dataMapFile.exists()) { + try { + URL url = dataMapFile.toURI().toURL(); + URLResource resource = new URLResource(url); + + XMLDataMapLoader xmlDataMapLoader = new XMLDataMapLoader(); + DataMap dataMap = xmlDataMapLoader.load(resource); + if (dataMap.getReverseEngineering() != null) { + Injector injector = DIBootstrap.createInjector(new ToolsModule(logger), new DbImportModule()); + try { + ConfigurationNameMapper nameMapper = injector.getInstance(ConfigurationNameMapper.class); + String reverseEngineeringLocation = nameMapper.configurationLocation(ReverseEngineering.class, dataMap.getReverseEngineering().getName()); + Resource reverseEngineeringResource = new URLResource(dataMapFile.toURI().toURL()).getRelativeResource(reverseEngineeringLocation); + + DefaultReverseEngineeringLoader reverseEngineeringLoader = new DefaultReverseEngineeringLoader(); + ReverseEngineering reverseEngineering = reverseEngineeringLoader.load(reverseEngineeringResource.getURL().openStream()); + reverseEngineering.setName(dataMap.getReverseEngineering().getName()); + reverseEngineering.setConfigurationSource(reverseEngineeringResource); + dataMap.setReverseEngineering(reverseEngineering); + + FiltersConfigBuilder filtersConfigBuilder = new FiltersConfigBuilder(dataMap.getReverseEngineering()); + config.getDbLoaderConfig().setFiltersConfig(filtersConfigBuilder.filtersConfig()); + validateDbImportConfiguration(config, injector); + injector.getInstance(DbImportAction.class).execute(config); + } catch (Exception ex) { + Throwable th = Util.unwindException(ex); + + String message = "Error importing database schema"; + + if (th.getLocalizedMessage() != null) { + message += ": " + th.getLocalizedMessage(); } + + log(message, Project.MSG_ERR); + throw new BuildException(message, th); + } finally { + injector.shutdown(); } - } catch (MalformedURLException e) { - log(e.getMessage(), Project.MSG_ERR); - throw new BuildException(e.getMessage(), e); } + } catch (MalformedURLException e) { + log(e.getMessage(), Project.MSG_ERR); + throw new BuildException(e.getMessage(), e); } } } diff --git a/cayenne-tools/src/test/java/org/apache/cayenne/gen/ClassGenerationActionTest.java b/cayenne-tools/src/test/java/org/apache/cayenne/gen/ClassGenerationActionTest.java index 9379e215e2..2222c0435f 100644 --- a/cayenne-tools/src/test/java/org/apache/cayenne/gen/ClassGenerationActionTest.java +++ b/cayenne-tools/src/test/java/org/apache/cayenne/gen/ClassGenerationActionTest.java @@ -18,6 +18,7 @@ ****************************************************************/ package org.apache.cayenne.gen; +import org.apache.cayenne.map.template.TemplateType; import org.apache.cayenne.map.CallbackDescriptor; import org.apache.cayenne.map.DataMap; import org.apache.cayenne.map.ObjAttribute; @@ -40,216 +41,216 @@ public class ClassGenerationActionTest { - protected ClassGenerationAction action; - protected Collection writers; + protected ClassGenerationAction action; + protected Collection writers; - @Before - public void setUp() throws Exception { - this.writers = new ArrayList(3); - this.action = new ClassGenerationAction() { + @Before + public void setUp() throws Exception { + this.writers = new ArrayList(3); + this.action = new ClassGenerationAction() { - @Override - protected Writer openWriter(TemplateType templateType) throws Exception { - StringWriter writer = new StringWriter(); - writers.add(writer); - return writer; - } - }; - } + @Override + protected Writer openWriter(TemplateType templateType) throws Exception { + StringWriter writer = new StringWriter(); + writers.add(writer); + return writer; + } + }; + } - @After - public void tearDown() throws Exception { - action = null; - writers = null; - } + @After + public void tearDown() throws Exception { + action = null; + writers = null; + } - @Test - public void testExecuteArtifactPairsImports() throws Exception { + @Test + public void testExecuteArtifactPairsImports() throws Exception { - ObjEntity testEntity1 = new ObjEntity("TE1"); - testEntity1.setClassName("org.example.TestClass1"); + ObjEntity testEntity1 = new ObjEntity("TE1"); + testEntity1.setClassName("org.example.TestClass1"); - action.setMakePairs(true); - action.setSuperPkg("org.example.auto"); + action.setMakePairs(true); + action.setSuperPkg("org.example.auto"); - List generated = execute(new EntityArtifact(testEntity1)); - assertNotNull(generated); - assertEquals(2, generated.size()); + List generated = execute(new EntityArtifact(testEntity1)); + assertNotNull(generated); + assertEquals(2, generated.size()); - String superclass = generated.get(0); - assertTrue(superclass, superclass.contains("package org.example.auto;")); - assertTrue(superclass, superclass.contains("import org.apache.cayenne.CayenneDataObject;")); + String superclass = generated.get(0); + assertTrue(superclass, superclass.contains("package org.example.auto;")); + assertTrue(superclass, superclass.contains("import org.apache.cayenne.CayenneDataObject;")); - String subclass = generated.get(1); - assertTrue(subclass, subclass.contains("package org.example;")); - assertTrue(subclass, subclass.contains("import org.example.auto._TestClass1;")); - } + String subclass = generated.get(1); + assertTrue(subclass, subclass.contains("package org.example;")); + assertTrue(subclass, subclass.contains("import org.example.auto._TestClass1;")); + } - @Test - public void testExecuteArtifactPairsMapRelationships() throws Exception { + @Test + public void testExecuteArtifactPairsMapRelationships() throws Exception { - ObjEntity testEntity1 = new ObjEntity("TE1"); - testEntity1.setClassName("org.example.TestClass1"); + ObjEntity testEntity1 = new ObjEntity("TE1"); + testEntity1.setClassName("org.example.TestClass1"); - final ObjEntity testEntity2 = new ObjEntity("TE1"); - testEntity2.setClassName("org.example.TestClass2"); + final ObjEntity testEntity2 = new ObjEntity("TE1"); + testEntity2.setClassName("org.example.TestClass2"); - ObjRelationship relationship = new ObjRelationship("xMap") { + ObjRelationship relationship = new ObjRelationship("xMap") { - private static final long serialVersionUID = 8042147877503405974L; + private static final long serialVersionUID = 8042147877503405974L; - @Override - public boolean isToMany() { - return true; - } + @Override + public boolean isToMany() { + return true; + } - @Override - public ObjEntity getTargetEntity() { - return testEntity2; - } - }; - relationship.setCollectionType("java.util.Map"); - testEntity1.addRelationship(relationship); + @Override + public ObjEntity getTargetEntity() { + return testEntity2; + } + }; + relationship.setCollectionType("java.util.Map"); + testEntity1.addRelationship(relationship); - action.setMakePairs(true); + action.setMakePairs(true); - List generated = execute(new EntityArtifact(testEntity1)); - assertNotNull(generated); - assertEquals(2, generated.size()); + List generated = execute(new EntityArtifact(testEntity1)); + assertNotNull(generated); + assertEquals(2, generated.size()); - String superclass = generated.get(0); - assertTrue(superclass, superclass.contains("import java.util.Map;")); - } + String superclass = generated.get(0); + assertTrue(superclass, superclass.contains("import java.util.Map;")); + } - @Test - public void testExecuteArtifactPairsAttribute() throws Exception { + @Test + public void testExecuteArtifactPairsAttribute() throws Exception { - ObjEntity testEntity1 = new ObjEntity("TE1"); - testEntity1.setClassName("org.example.TestClass1"); + ObjEntity testEntity1 = new ObjEntity("TE1"); + testEntity1.setClassName("org.example.TestClass1"); - ObjAttribute attr = new ObjAttribute(); - attr.setName("ID"); - attr.setType("int"); + ObjAttribute attr = new ObjAttribute(); + attr.setName("ID"); + attr.setType("int"); - ObjAttribute attr1 = new ObjAttribute(); - attr1.setName("name"); - attr1.setType("char"); + ObjAttribute attr1 = new ObjAttribute(); + attr1.setName("name"); + attr1.setType("char"); - testEntity1.addAttribute(attr); - testEntity1.addAttribute(attr1); + testEntity1.addAttribute(attr); + testEntity1.addAttribute(attr1); - action.setMakePairs(true); + action.setMakePairs(true); - List generated = execute(new EntityArtifact(testEntity1)); - assertNotNull(generated); - assertEquals(2, generated.size()); - String superclass = generated.get(0); + List generated = execute(new EntityArtifact(testEntity1)); + assertNotNull(generated); + assertEquals(2, generated.size()); + String superclass = generated.get(0); - assertTrue(superclass, superclass.contains("public void setID(int ID)")); - assertTrue(superclass, superclass.contains("writeProperty(\"ID\", ID);")); + assertTrue(superclass, superclass.contains("public void setID(int ID)")); + assertTrue(superclass, superclass.contains("writeProperty(\"ID\", ID);")); - assertTrue(superclass, superclass.contains("public int getID()")); - assertTrue(superclass, superclass.contains("Object value = readProperty(\"ID\");")); - assertTrue(superclass, superclass.contains("return (value != null) ? (Integer) value : 0;")); + assertTrue(superclass, superclass.contains("public int getID()")); + assertTrue(superclass, superclass.contains("Object value = readProperty(\"ID\");")); + assertTrue(superclass, superclass.contains("return (value != null) ? (Integer) value : 0;")); - assertTrue(superclass, superclass.contains("public void setName(char name)")); - assertTrue(superclass, superclass.contains("writeProperty(\"name\", name);")); + assertTrue(superclass, superclass.contains("public void setName(char name)")); + assertTrue(superclass, superclass.contains("writeProperty(\"name\", name);")); - assertTrue(superclass, superclass.contains("public char getName()")); - assertTrue(superclass, superclass.contains("Object value = readProperty(\"name\");")); - assertTrue(superclass, superclass.contains("return (value != null) ? (Character) value : 0;")); + assertTrue(superclass, superclass.contains("public char getName()")); + assertTrue(superclass, superclass.contains("Object value = readProperty(\"name\");")); + assertTrue(superclass, superclass.contains("return (value != null) ? (Character) value : 0;")); - } + } - @Test - public void testExecuteDataMapQueryNames() throws Exception { - runDataMapTest(false); - } + @Test + public void testExecuteDataMapQueryNames() throws Exception { + runDataMapTest(false); + } - @Test - public void testExecuteClientDataMapQueryNames() throws Exception { - runDataMapTest(true); - } + @Test + public void testExecuteClientDataMapQueryNames() throws Exception { + runDataMapTest(true); + } - private void runDataMapTest(boolean client) throws Exception { - QueryDescriptor descriptor = QueryDescriptor.selectQueryDescriptor(); + private void runDataMapTest(boolean client) throws Exception { + QueryDescriptor descriptor = QueryDescriptor.selectQueryDescriptor(); descriptor.setName("TestQuery"); - DataMap map = new DataMap(); - map.addQueryDescriptor(descriptor); - map.setName("testmap"); - List generated; - if (client) { - map.setDefaultClientPackage("testpackage"); - generated = execute(new ClientDataMapArtifact(map, map.getQueryDescriptors())); - } else { - map.setDefaultPackage("testpackage"); - generated = execute(new DataMapArtifact(map, map.getQueryDescriptors())); - } - assertEquals(2, generated.size()); - assertTrue(generated.get(0).contains("public static final String TEST_QUERY_QUERYNAME = \"TestQuery\"")); - } - - @Test - public void testCallbackMethodGeneration() throws Exception { - assertCallbacks(false); - } - - @Test - public void testClientCallbackMethodGeneration() throws Exception { - assertCallbacks(true); - } - - private void assertCallbacks(boolean isClient) throws Exception { - ObjEntity testEntity1 = new ObjEntity("TE1"); - testEntity1.setClassName("org.example.TestClass1"); - int i = 0; - for (CallbackDescriptor cb : testEntity1.getCallbackMap().getCallbacks()) { - cb.addCallbackMethod("cb" + i++); - } - - if (isClient) { - - action = new ClientClassGenerationAction() { - @Override - protected Writer openWriter(TemplateType templateType) throws Exception { - StringWriter writer = new StringWriter(); - writers.add(writer); - return writer; - } - - }; - - } - - action.setMakePairs(true); - - List generated = execute(new EntityArtifact(testEntity1)); - assertNotNull(generated); - assertEquals(2, generated.size()); - - String superclass = generated.get(0); - - assertTrue(superclass, superclass.contains("public abstract class _TestClass1")); - - for (int j = 0; j < i; j++) { - assertTrue(superclass, superclass.contains("protected abstract void cb" + j + "();")); - } - - String subclass = generated.get(1); - for (int j = 0; j < i; j++) { - assertTrue(subclass, subclass.contains("protected void cb" + j + "() {")); - } - } - - protected List execute(Artifact artifact) throws Exception { - - action.execute(artifact); - - List strings = new ArrayList(writers.size()); - for (StringWriter writer : writers) { - strings.add(writer.toString()); - } - return strings; - } + DataMap map = new DataMap(); + map.addQueryDescriptor(descriptor); + map.setName("testmap"); + List generated; + if (client) { + map.setDefaultClientPackage("testpackage"); + generated = execute(new ClientDataMapArtifact(map, map.getQueryDescriptors())); + } else { + map.setDefaultPackage("testpackage"); + generated = execute(new DataMapArtifact(map, map.getQueryDescriptors())); + } + assertEquals(2, generated.size()); + assertTrue(generated.get(0).contains("public static final String TEST_QUERY_QUERYNAME = \"TestQuery\"")); + } + + @Test + public void testCallbackMethodGeneration() throws Exception { + assertCallbacks(false); + } + + @Test + public void testClientCallbackMethodGeneration() throws Exception { + assertCallbacks(true); + } + + private void assertCallbacks(boolean isClient) throws Exception { + ObjEntity testEntity1 = new ObjEntity("TE1"); + testEntity1.setClassName("org.example.TestClass1"); + int i = 0; + for (CallbackDescriptor cb : testEntity1.getCallbackMap().getCallbacks()) { + cb.addCallbackMethod("cb" + i++); + } + + if (isClient) { + + action = new ClientClassGenerationAction() { + @Override + protected Writer openWriter(TemplateType templateType) throws Exception { + StringWriter writer = new StringWriter(); + writers.add(writer); + return writer; + } + + }; + + } + + action.setMakePairs(true); + + List generated = execute(new EntityArtifact(testEntity1)); + assertNotNull(generated); + assertEquals(2, generated.size()); + + String superclass = generated.get(0); + + assertTrue(superclass, superclass.contains("public abstract class _TestClass1")); + + for (int j = 0; j < i; j++) { + assertTrue(superclass, superclass.contains("protected abstract void cb" + j + "();")); + } + + String subclass = generated.get(1); + for (int j = 0; j < i; j++) { + assertTrue(subclass, subclass.contains("protected void cb" + j + "() {")); + } + } + + protected List execute(Artifact artifact) throws Exception { + + action.execute(artifact); + + List strings = new ArrayList(writers.size()); + for (StringWriter writer : writers) { + strings.add(writer.toString()); + } + return strings; + } } diff --git a/cayenne-tools/src/test/java/org/apache/cayenne/gen/ClassGenerationCase.java b/cayenne-tools/src/test/java/org/apache/cayenne/gen/ClassGenerationCase.java index 44b709d7e9..f07ee90e15 100644 --- a/cayenne-tools/src/test/java/org/apache/cayenne/gen/ClassGenerationCase.java +++ b/cayenne-tools/src/test/java/org/apache/cayenne/gen/ClassGenerationCase.java @@ -19,6 +19,7 @@ package org.apache.cayenne.gen; +import org.apache.cayenne.configuration.gen.ClassGeneratorResourceLoader; import org.apache.velocity.Template; import org.apache.velocity.app.VelocityEngine; import org.apache.velocity.context.Context; diff --git a/cayenne-tools/src/test/java/org/apache/cayenne/tools/CayenneGeneratorTaskCrossMapRelationshipsTest.java b/cayenne-tools/src/test/java/org/apache/cayenne/tools/CayenneGeneratorTaskCrossMapRelationshipsTest.java index 40877b8bd5..a32decae56 100644 --- a/cayenne-tools/src/test/java/org/apache/cayenne/tools/CayenneGeneratorTaskCrossMapRelationshipsTest.java +++ b/cayenne-tools/src/test/java/org/apache/cayenne/tools/CayenneGeneratorTaskCrossMapRelationshipsTest.java @@ -81,6 +81,7 @@ public void testCrossDataMapRelationships() throws Exception { task.setDestDir(destDir); task.setSuperpkg("org.apache.cayenne.testdo.cgen2.auto"); task.setUsepkgpath(true); + task.isClassGenerationDefined = true; // run task task.execute(); diff --git a/cayenne-tools/src/test/java/org/apache/cayenne/tools/CayenneGeneratorTaskTest.java b/cayenne-tools/src/test/java/org/apache/cayenne/tools/CayenneGeneratorTaskTest.java index f6f616b812..f293462fb2 100644 --- a/cayenne-tools/src/test/java/org/apache/cayenne/tools/CayenneGeneratorTaskTest.java +++ b/cayenne-tools/src/test/java/org/apache/cayenne/tools/CayenneGeneratorTaskTest.java @@ -67,6 +67,7 @@ public void setUp() { task.setProject(project); task.setTaskName("Test"); task.setLocation(Location.UNKNOWN_LOCATION); + task.isClassGenerationDefined = true; } /** diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/configuration/event/TemplateEvent.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/configuration/event/TemplateEvent.java new file mode 100644 index 0000000000..4e5936adcc --- /dev/null +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/configuration/event/TemplateEvent.java @@ -0,0 +1,67 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.cayenne.configuration.event; + +import org.apache.cayenne.map.template.ClassTemplate; +import org.apache.cayenne.map.event.MapEvent;; + +/** + * @since 4.0 + */ +public class TemplateEvent extends MapEvent { + protected ClassTemplate template; + + /** Creates a Template change event. */ + public TemplateEvent(Object src, ClassTemplate template) { + super(src); + setTemplate(template); + } + + /** Creates a Template event of a specified type. */ + public TemplateEvent(Object src, ClassTemplate template, int id) { + this(src, template); + setId(id); + } + + /** Creates a Template name change event.*/ + public TemplateEvent(Object src, ClassTemplate template, String oldName) { + this(src, template); + setOldName(oldName); + } + + /** Returns template object associated with this event. */ + public ClassTemplate getTemplate() { + return template; + } + + /** + * Sets the template. + * + * @param template The template to set + */ + public void setTemplate(ClassTemplate template) { + this.template = template; + } + + @Override + public String getNewName() { + return (template != null) ? template.getName() : null; + } +} diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/configuration/event/TemplateListener.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/configuration/event/TemplateListener.java new file mode 100644 index 0000000000..d90336e2cf --- /dev/null +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/configuration/event/TemplateListener.java @@ -0,0 +1,40 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.cayenne.configuration.event; + +import java.util.EventListener; + +/** + * @since 4.0 + */ +public interface TemplateListener extends EventListener { + /** + * Template changed. + */ + void templateChanged(TemplateEvent e); + /** + * New template has been created/added. + */ + void templateAdded(TemplateEvent e); + /** + * Template has been removed. + */ + void templateRemoved(TemplateEvent e); +} diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/CayenneModelerFrame.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/CayenneModelerFrame.java index 0b78d9e7ed..92051b4538 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/CayenneModelerFrame.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/CayenneModelerFrame.java @@ -100,6 +100,9 @@ import org.apache.cayenne.modeler.event.QueryDisplayEvent; import org.apache.cayenne.modeler.event.QueryDisplayListener; import org.apache.cayenne.modeler.event.RecentFileListListener; +import org.apache.cayenne.modeler.event.TemplateDisplayEvent; +import org.apache.cayenne.modeler.event.TemplateDisplayListener; + import org.apache.cayenne.modeler.pref.ComponentGeometry; import org.apache.cayenne.modeler.util.ModelerUtil; import org.apache.cayenne.modeler.util.RecentFileMenu; @@ -112,7 +115,7 @@ public class CayenneModelerFrame extends JFrame implements DataNodeDisplayListener, DataMapDisplayListener, ObjEntityDisplayListener, DbEntityDisplayListener, QueryDisplayListener, ProcedureDisplayListener, MultipleObjectsDisplayListener, - EmbeddableDisplayListener { + EmbeddableDisplayListener, TemplateDisplayListener { protected EditorView view; protected RecentFileMenu recentFileMenu; @@ -462,6 +465,10 @@ public void currentProcedureChanged(ProcedureDisplayEvent e) { actionManager.procedureSelected(); } + public void currentTemplateChanged(TemplateDisplayEvent e) { + actionManager.templateSelected(); + } + public void currentObjectsChanged( MultipleObjectsDisplayEvent e, Application application) { diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/CodeTemplateManager.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/CodeTemplateManager.java index 0f8131c335..03e511d3fc 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/CodeTemplateManager.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/CodeTemplateManager.java @@ -26,8 +26,12 @@ import java.util.prefs.BackingStoreException; import java.util.prefs.Preferences; +import org.apache.cayenne.map.template.ClassGenerationDescriptor; +import org.apache.cayenne.map.template.ClassTemplate; +import org.apache.cayenne.configuration.DataChannelDescriptor; import org.apache.cayenne.gen.ClassGenerationAction; import org.apache.cayenne.gen.ClientClassGenerationAction; +import org.apache.cayenne.map.DataMap; import org.apache.cayenne.modeler.pref.FSPath; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -47,6 +51,7 @@ public class CodeTemplateManager { protected List standardSubclassTemplates; protected List standardSuperclassTemplates; protected Map customTemplates; + protected Map modelTemplates; protected Map standardTemplates; private static Log logger = LogFactory.getLog(CodeTemplateManager.class); @@ -55,6 +60,10 @@ public Preferences getTemplatePreferences(Application application) { return application.getPreferencesNode(this.getClass(), NODE_NAME); } + private DataChannelDescriptor getDataChannel(Application application) { + return application.getFrameController().projectController.getCurrentDataChanel(); + } + public CodeTemplateManager(Application application) { standardSuperclassTemplates = new ArrayList(3); @@ -66,6 +75,7 @@ public CodeTemplateManager(Application application) { standardSubclassTemplates.add(STANDARD_CLIENT_SUBCLASS); updateCustomTemplates(getTemplatePreferences(application)); + updateFromModel(getDataChannel(application)); standardTemplates = new HashMap<>(); standardTemplates.put(STANDARD_SERVER_SUPERCLASS, ClassGenerationAction.SUPERCLASS_TEMPLATE); @@ -92,6 +102,19 @@ public void updateCustomTemplates(Preferences preference) { } } + public void updateFromModel(DataChannelDescriptor descriptor) { + modelTemplates = new HashMap<>(5); + + for (DataMap dataMap : descriptor.getDataMaps()) { + ClassGenerationDescriptor classGenerator = dataMap.getClassGenerationDescriptor(); + for (ClassTemplate template : classGenerator.getTemplates().values()) { + if (template.getConfigurationSource() != null) { + modelTemplates.put(template.getName(), template.getConfigurationSource().getURL().getPath()); + } + } + } + } + // TODO: andrus, 12/5/2007 - this should also take a "pairs" parameter to // correctly // assign standard templates @@ -109,6 +132,10 @@ public Map getCustomTemplates() { return customTemplates; } + public Map getModelTemplates() { + return modelTemplates; + } + public List getStandardSubclassTemplates() { return standardSubclassTemplates; } diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ProjectController.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ProjectController.java index 1872fb9cdd..5e3fb119c9 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ProjectController.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ProjectController.java @@ -20,6 +20,7 @@ package org.apache.cayenne.modeler; import org.apache.cayenne.CayenneRuntimeException; +import org.apache.cayenne.map.template.ClassTemplate; import org.apache.cayenne.configuration.ConfigurationNode; import org.apache.cayenne.configuration.DataChannelDescriptor; import org.apache.cayenne.configuration.DataNodeDescriptor; @@ -35,6 +36,8 @@ import org.apache.cayenne.configuration.event.ProcedureParameterListener; import org.apache.cayenne.configuration.event.QueryEvent; import org.apache.cayenne.configuration.event.QueryListener; +import org.apache.cayenne.configuration.event.TemplateEvent; +import org.apache.cayenne.configuration.event.TemplateListener; import org.apache.cayenne.map.DataMap; import org.apache.cayenne.map.DbAttribute; import org.apache.cayenne.map.DbEntity; @@ -102,6 +105,8 @@ import org.apache.cayenne.modeler.event.QueryDisplayEvent; import org.apache.cayenne.modeler.event.QueryDisplayListener; import org.apache.cayenne.modeler.event.RelationshipDisplayEvent; +import org.apache.cayenne.modeler.event.TemplateDisplayListener; +import org.apache.cayenne.modeler.event.TemplateDisplayEvent; import org.apache.cayenne.modeler.pref.DataMapDefaults; import org.apache.cayenne.modeler.pref.DataNodeDefaults; import org.apache.cayenne.modeler.pref.ProjectStatePreferences; @@ -156,6 +161,8 @@ public class ControllerState { private ObjRelationship[] objRels; private DbRelationship[] dbRels; + private ClassTemplate template; + private Procedure procedure; private ProcedureParameter[] procedureParameters; private QueryDescriptor query; @@ -219,6 +226,8 @@ public boolean isEquivalent(ControllerState val) { return query == val.query; } else if (event instanceof EmbeddableDisplayEvent && val.event instanceof EmbeddableDisplayEvent) { return embeddable == val.embeddable; + } else if (event.getClass() == TemplateDisplayEvent.class && event.getClass() == val.event.getClass()) { + return template == val.template; } else if (event.getClass() == DataMapDisplayEvent.class && event.getClass() == val.event.getClass()) { return map == val.map; } else if (event.getClass() == DataNodeDisplayEvent.class && event.getClass() == val.event.getClass()) { @@ -408,6 +417,7 @@ public void projectOpened() { addProcedureDisplayListener(frame); addMultipleObjectsDisplayListener(frame); addEmbeddableDisplayListener(frame); + addTemplateDisplayListener(frame); } public void reset() { @@ -489,6 +499,10 @@ private void removeFromHistory(EventObject e) { if (((DomainEvent) e).getDomain() == ((DomainDisplayEvent) csEvent).getDomain()) { removeList.add(cs); } + } else if (e instanceof TemplateEvent && csEvent instanceof TemplateDisplayEvent) { + if (((TemplateEvent) e).getTemplate() == ((TemplateDisplayEvent) csEvent).getTemplate()) { + removeList.add(cs); + } } } @@ -521,6 +535,8 @@ public DbEntity getCurrentDbEntity() { return currentState.dbEntity; } + public ClassTemplate getCurrentTemplate() { return currentState.template; } + /** * @return Array of selected ObjAttributes */ @@ -628,6 +644,14 @@ public void removeProjectOnSaveListener(ProjectOnSaveListener listener) { listenerList.remove(ProjectOnSaveListener.class, listener); } + public void addTemplateListener(TemplateListener listener) { + listenerList.add(TemplateListener.class, listener); + } + + public void addTemplateDisplayListener(TemplateDisplayListener listener) { + listenerList.add(TemplateDisplayListener.class, listener); + } + public void addObjEntityListener(ObjEntityListener listener) { listenerList.add(ObjEntityListener.class, listener); } @@ -733,7 +757,7 @@ public void fireDomainDisplayEvent(DomainDisplayEvent e) { if (!changed) { changed = currentState.node != null || currentState.map != null || currentState.dbEntity != null || currentState.objEntity != null || currentState.procedure != null || currentState.query != null - || currentState.embeddable != null; + || currentState.embeddable != null || currentState.template != null; } if (!e.isRefired()) { @@ -790,7 +814,8 @@ public void fireDataNodeDisplayEvent(DataNodeDisplayEvent e) { if (!changed) { changed = currentState.map != null || currentState.dbEntity != null || currentState.objEntity != null - || currentState.procedure != null || currentState.query != null || currentState.embeddable != null; + || currentState.procedure != null || currentState.query != null || currentState.embeddable != null + || currentState.template != null; } if (!e.isRefired()) { @@ -846,7 +871,7 @@ public void fireDataMapDisplayEvent(DataMapDisplayEvent e) { boolean changed = e.getDataMap() != currentState.map; if (!changed) { changed = currentState.dbEntity != null || currentState.objEntity != null || currentState.procedure != null - || currentState.query != null || currentState.embeddable != null; + || currentState.query != null || currentState.embeddable != null || currentState.template != null; } if (!e.isRefired()) { @@ -933,6 +958,35 @@ public void fireObjEntityEvent(EntityEvent e) { } } + /** + * Informs all listeners of the TemplateEvent. Does not send the event to its + * originator. + */ + public void fireTemplateEvent(TemplateEvent e) { + setDirty(true); + + if (e.getId() == MapEvent.REMOVE) { + removeFromHistory(e); + } + + for (EventListener listener : listenerList.getListeners(TemplateListener.class)) { + TemplateListener temp = (TemplateListener) listener; + switch (e.getId()) { + case MapEvent.ADD: + temp.templateAdded(e); + break; + case MapEvent.CHANGE: + temp.templateChanged(e); + break; + case MapEvent.REMOVE: + temp.templateRemoved(e); + break; + default: + throw new IllegalArgumentException("Invalid TemplateEvent type: " + e.getId()); + } + } + } + /** * Informs all listeners of the EntityEvent. Does not send the event to its * originator. @@ -1152,6 +1206,10 @@ public void fireNavigationEvent(EventObject e) { QueryDisplayEvent qde = (QueryDisplayEvent) de; qde.setQueryChanged(true); fireQueryDisplayEvent(qde); + } else if (de instanceof TemplateDisplayEvent) { + TemplateDisplayEvent tde = (TemplateDisplayEvent) de; + tde.setTemplateChanged(true); + fireTemplateDisplayEvent(tde); } else if (de instanceof DataMapDisplayEvent) { DataMapDisplayEvent dmde = (DataMapDisplayEvent) de; dmde.setDataMapChanged(true); @@ -1244,6 +1302,30 @@ public void fireQueryDisplayEvent(QueryDisplayEvent e) { } } + public void fireTemplateDisplayEvent(TemplateDisplayEvent e) { + boolean changed = e.getTemplate() != currentState.template; + + if (!e.isRefired()) { + e.setTemplateChanged(changed); + + if (changed) { + clearState(); + currentState.domain = e.getDomain(); + currentState.map = e.getDataMap(); + currentState.template = e.getTemplate(); + } + } + + if (changed) { + saveState(e); + } + + for (EventListener eventListener : listenerList.getListeners(TemplateDisplayListener.class)) { + TemplateDisplayListener listener = (TemplateDisplayListener) eventListener; + listener.currentTemplateChanged(e); + } + } + public void fireProcedureDisplayEvent(ProcedureDisplayEvent e) { boolean changed = e.getProcedure() != currentState.procedure; @@ -1690,6 +1772,8 @@ public Object getCurrentObject() { return getCurrentQuery(); } else if (getCurrentProcedure() != null) { return getCurrentProcedure(); + } else if (getCurrentTemplate() != null) { + return getCurrentTemplate(); } else if (getCurrentDataMap() != null) { return getCurrentDataMap(); } else if (getCurrentDataNode() != null) { diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ProjectFileChangeTracker.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ProjectFileChangeTracker.java index bbb8e742d4..74887edb12 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ProjectFileChangeTracker.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ProjectFileChangeTracker.java @@ -28,6 +28,7 @@ import org.apache.cayenne.configuration.DataChannelDescriptor; import org.apache.cayenne.map.DataMap; +import org.apache.cayenne.map.template.ClassTemplate; import org.apache.cayenne.modeler.action.OpenProjectAction; import org.apache.cayenne.modeler.action.SaveAction; import org.apache.cayenne.modeler.dialog.FileDeletedDialog; @@ -84,6 +85,12 @@ public void reconfigure() { while (it.hasNext()) { DataMap dm = it.next(); addFile(dm.getConfigurationSource().getURL().getPath()); + + for (ClassTemplate template: dm.getClassGenerationDescriptor().getTemplates().values()) { + if (template.getConfigurationSource() != null) { + addFile(template.getConfigurationSource().getURL().getPath()); + } + } } } diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ProjectTreeView.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ProjectTreeView.java index f281c80d37..914100558c 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ProjectTreeView.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ProjectTreeView.java @@ -32,12 +32,15 @@ import org.apache.cayenne.configuration.event.ProcedureListener; import org.apache.cayenne.configuration.event.QueryEvent; import org.apache.cayenne.configuration.event.QueryListener; +import org.apache.cayenne.configuration.event.TemplateEvent; +import org.apache.cayenne.configuration.event.TemplateListener; import org.apache.cayenne.map.DataMap; import org.apache.cayenne.map.DbEntity; import org.apache.cayenne.map.Embeddable; import org.apache.cayenne.map.Entity; import org.apache.cayenne.map.ObjEntity; import org.apache.cayenne.map.Procedure; +import org.apache.cayenne.map.template.ClassTemplate; import org.apache.cayenne.map.event.DbEntityListener; import org.apache.cayenne.map.event.EmbeddableEvent; import org.apache.cayenne.map.event.EmbeddableListener; @@ -73,6 +76,7 @@ import org.apache.cayenne.modeler.event.ProcedureDisplayListener; import org.apache.cayenne.modeler.event.QueryDisplayEvent; import org.apache.cayenne.modeler.event.QueryDisplayListener; +import org.apache.cayenne.modeler.event.TemplateDisplayEvent; import org.apache.cayenne.modeler.util.CayenneAction; import org.apache.cayenne.modeler.util.CellRenderers; import org.apache.cayenne.modeler.util.Comparators; @@ -112,7 +116,7 @@ public class ProjectTreeView extends JTree implements DomainDisplayListener, DataNodeListener, ObjEntityListener, ObjEntityDisplayListener, DbEntityListener, DbEntityDisplayListener, QueryListener, QueryDisplayListener, ProcedureListener, ProcedureDisplayListener, MultipleObjectsDisplayListener, - EmbeddableDisplayListener, EmbeddableListener { + EmbeddableDisplayListener, EmbeddableListener, TemplateListener { private static final Log logObj = LogFactory.getLog(ProjectTreeView.class); @@ -220,6 +224,7 @@ public void treeWillCollapse(TreeExpansionEvent e) throws ExpandVetoException { mediator.addDbEntityDisplayListener(this); mediator.addEmbeddableDisplayListener(this); mediator.addEmbeddableListener(this); + mediator.addTemplateListener(this); mediator.addProcedureListener(this); mediator.addProcedureDisplayListener(this); mediator.addQueryListener(this); @@ -953,6 +958,15 @@ else if (obj instanceof QueryDescriptor) { mediator.fireQueryDisplayEvent(e); } + else if (obj instanceof ClassTemplate) { + TemplateDisplayEvent e = new TemplateDisplayEvent( + this, + (ClassTemplate) obj, + (DataMap) data[data.length - 2], + (DataChannelDescriptor) mediator.getProject().getRootNode()); + mediator.fireTemplateDisplayEvent(e); + } + this.scrollPathToVisible(path); } @@ -1032,7 +1046,7 @@ private JPopupMenu createJPopupMenu() { /** * Creates and returns an menu item associated with the key. * - * @param key action key + * @param actionType action key */ private JMenuItem buildMenu(Class actionType) { CayenneAction action = (CayenneAction) mediator @@ -1042,6 +1056,21 @@ private JMenuItem buildMenu(Class actionType) { return action.buildMenu(); } + @Override + public void templateChanged(TemplateEvent e) { + + } + + @Override + public void templateAdded(TemplateEvent e) { + + } + + @Override + public void templateRemoved(TemplateEvent e) { + + } + /** * Class to handle right-click and show popup for selected tree row */ diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/ActionManager.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/ActionManager.java index 16b53edcc3..2f40686ffb 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/ActionManager.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/ActionManager.java @@ -62,6 +62,8 @@ public interface ActionManager { void embeddableSelected(); + void templateSelected(); + /** * Invoked when several objects were selected in ProjectTree at time */ diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/CopyAction.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/CopyAction.java index ce9a82f31c..ae7aee839f 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/CopyAction.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/CopyAction.java @@ -25,6 +25,7 @@ import javax.swing.KeyStroke; +import org.apache.cayenne.map.template.ClassTemplate; import org.apache.cayenne.configuration.ConfigurationNode; import org.apache.cayenne.map.DataMap; import org.apache.cayenne.map.DbAttribute; @@ -134,7 +135,8 @@ public boolean enableForPath(ConfigurationNode object) { || object instanceof ObjRelationship || object instanceof ObjCallbackMethod || object instanceof Procedure - || object instanceof ProcedureParameter) { + || object instanceof ProcedureParameter + || object instanceof ClassTemplate) { return true; } diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/CreateTemplateAction.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/CreateTemplateAction.java new file mode 100644 index 0000000000..c026c0d90c --- /dev/null +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/CreateTemplateAction.java @@ -0,0 +1,85 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.cayenne.modeler.action; + +import org.apache.cayenne.map.template.ClassGenerationDescriptor; +import org.apache.cayenne.map.template.ClassTemplate; +import org.apache.cayenne.configuration.DataChannelDescriptor; +import org.apache.cayenne.configuration.event.TemplateEvent; +import org.apache.cayenne.map.DataMap; +import org.apache.cayenne.map.event.MapEvent; +import org.apache.cayenne.map.naming.DefaultUniqueNameGenerator; +import org.apache.cayenne.map.naming.NameCheckers; +import org.apache.cayenne.modeler.Application; +import org.apache.cayenne.modeler.ProjectController; +import org.apache.cayenne.modeler.util.CayenneAction; + +import java.awt.event.ActionEvent; + +/** + * @since 4.0 + */ +public class CreateTemplateAction extends CayenneAction{ + public static String getActionName() { + return "Create Template"; + } + + /** + * Constructor for CreateTemplateAction + */ + public CreateTemplateAction(Application application) { + super(getActionName(), application); + } + + @Override + public void performAction(ActionEvent e) { + ProjectController mediator = getProjectController(); + + DataChannelDescriptor dataChannelDescriptor = mediator.getCurrentDataChanel(); + String templateName = DefaultUniqueNameGenerator.generate(NameCheckers.template, dataChannelDescriptor); + + ClassTemplate template = new ClassTemplate(templateName); + DataMap dataMap = mediator.getCurrentDataMap(); + + createTemplate(dataMap, template); + } + + public void createTemplate(DataMap dataMap, ClassTemplate template) { + ProjectController mediator = getProjectController(); + if (dataMap != null) { + ClassGenerationDescriptor classGenerationDescriptor = dataMap.getClassGenerationDescriptor(); + if (classGenerationDescriptor.getTemplates() != null) { + template.setDataMap(dataMap); + classGenerationDescriptor.getTemplates().put(template.getName(), template); + fireTemplateEvent(this, mediator, dataMap, template); + } + } + } + + /** + * Fires events when a template was added + */ + static void fireTemplateEvent( + Object src, + ProjectController mediator, + DataMap dataMap, + ClassTemplate template) { + mediator.fireTemplateEvent(new TemplateEvent(src, template, MapEvent.ADD)); + } +} diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/DefaultActionManager.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/DefaultActionManager.java index 80bd3557bb..cd98204441 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/DefaultActionManager.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/DefaultActionManager.java @@ -52,6 +52,7 @@ public class DefaultActionManager implements ActionManager { private Collection EMBEDDABLE_ACTIONS; private Collection PROCEDURE_ACTIONS; private Collection MULTIPLE_OBJECTS_ACTIONS; + private Collection TEMPLATE_ACTIONS; protected Map actionMap; @@ -82,6 +83,7 @@ public DefaultActionManager(@Inject Application application) { registerAction(new CreateRelationshipAction(application)); registerAction(new RemoveRelationshipAction(application)); registerAction(new RemoveAttributeRelationshipAction(application)); + registerAction(new CreateTemplateAction(application)); // start callback-related actions registerAction(new CreateCallbackMethodAction(application)).setAlwaysOn(true); registerAction(new RemoveCallbackMethodAction(application)); @@ -205,6 +207,8 @@ private void initActions() { DbEntityCounterpartAction.class.getName(), ShowGraphEntityAction.class.getName())); + + EMBEDDABLE_ACTIONS = new HashSet(DATA_MAP_ACTIONS); EMBEDDABLE_ACTIONS.addAll(Arrays.asList(CreateAttributeAction.class.getName())); @@ -214,6 +218,10 @@ private void initActions() { PROCEDURE_ACTIONS.addAll(Arrays.asList(CreateProcedureParameterAction.class .getName())); + TEMPLATE_ACTIONS = new HashSet<>(DATA_MAP_ACTIONS); + + TEMPLATE_ACTIONS.addAll(Arrays.asList(CreateTemplateAction.class.getName())); + MULTIPLE_OBJECTS_ACTIONS = new HashSet(PROJECT_ACTIONS); MULTIPLE_OBJECTS_ACTIONS.addAll(Arrays.asList( @@ -309,6 +317,11 @@ public void embeddableSelected() { updateActions("Embeddable"); } + public void templateSelected() { + processActionsState(TEMPLATE_ACTIONS); + updateActions("Template"); + } + /** * Invoked when several objects were selected in ProjectTree at time */ diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/PasteAction.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/PasteAction.java index e1885b93a5..459eb5c3c6 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/PasteAction.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/PasteAction.java @@ -18,6 +18,7 @@ ****************************************************************/ package org.apache.cayenne.modeler.action; +import org.apache.cayenne.map.template.ClassTemplate; import org.apache.cayenne.configuration.ConfigurationNode; import org.apache.cayenne.configuration.DataChannelDescriptor; import org.apache.cayenne.configuration.DataNodeDescriptor; @@ -214,6 +215,12 @@ public void paste( for (Procedure procedure : dataMap.getProcedures()) { procedure.setName(DefaultUniqueNameGenerator.generate(NameCheckers.procedure, COPY_PATTERN, dataMap, procedure.getName())); } + + for (ClassTemplate template: dataMap.getClassGenerationDescriptor().getTemplates().values()) { + DataChannelDescriptor descriptor = getProjectController().getCurrentDataChanel(); + template.setName(DefaultUniqueNameGenerator.generate(NameCheckers.template, COPY_PATTERN, descriptor, template.getName())); + } + for (QueryDescriptor query : dataMap.getQueryDescriptors()) { query.setName(DefaultUniqueNameGenerator.generate(NameCheckers.query, COPY_PATTERN, dataMap, query.getName())); } @@ -307,6 +314,19 @@ else if (content instanceof Procedure) { dataMap, procedure); } + else if (content instanceof ClassTemplate) { + //paste Template to DataMap + ClassTemplate template = (ClassTemplate) content; + DataChannelDescriptor dataChannelDescriptor = getProjectController().getCurrentDataChanel(); + template.setName(DefaultUniqueNameGenerator.generate(NameCheckers.template, COPY_PATTERN, dataChannelDescriptor, template.getName())); + + dataMap.addTemplate(template); + CreateTemplateAction.fireTemplateEvent( + this, + mediator, + dataMap, + template); + } } else if (where instanceof DbEntity) { final DbEntity dbEntity = (DbEntity) where; @@ -476,7 +496,9 @@ private boolean getState() { (currentObject instanceof Procedure && (content instanceof ProcedureParameter || isTreeLeaf(content)) || - (currentObject instanceof Query && isTreeLeaf(content))); + (currentObject instanceof Query && isTreeLeaf(content))) + + || currentObject instanceof ClassTemplate; } catch (Exception ex) { return false; @@ -491,7 +513,8 @@ private boolean isTreeLeaf(Object content) { || content instanceof ObjEntity || content instanceof Embeddable || content instanceof Procedure - || content instanceof Query; + || content instanceof Query + || content instanceof ClassTemplate; } public void flavorsChanged(FlavorEvent e) { diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/RemoveAction.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/RemoveAction.java index 3e3f13d194..c0a3c5dee1 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/RemoveAction.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/RemoveAction.java @@ -19,14 +19,11 @@ package org.apache.cayenne.modeler.action; +import org.apache.cayenne.map.template.ClassTemplate; import org.apache.cayenne.configuration.ConfigurationNode; import org.apache.cayenne.configuration.DataChannelDescriptor; import org.apache.cayenne.configuration.DataNodeDescriptor; -import org.apache.cayenne.configuration.event.DataMapEvent; -import org.apache.cayenne.configuration.event.DataNodeEvent; -import org.apache.cayenne.configuration.event.ProcedureEvent; -import org.apache.cayenne.configuration.event.ProcedureParameterEvent; -import org.apache.cayenne.configuration.event.QueryEvent; +import org.apache.cayenne.configuration.event.*; import org.apache.cayenne.map.Attribute; import org.apache.cayenne.map.CallbackMap; import org.apache.cayenne.map.DataMap; @@ -42,12 +39,7 @@ import org.apache.cayenne.map.Procedure; import org.apache.cayenne.map.ProcedureParameter; import org.apache.cayenne.map.Relationship; -import org.apache.cayenne.map.event.AttributeEvent; -import org.apache.cayenne.map.event.EmbeddableAttributeEvent; -import org.apache.cayenne.map.event.EmbeddableEvent; -import org.apache.cayenne.map.event.EntityEvent; -import org.apache.cayenne.map.event.MapEvent; -import org.apache.cayenne.map.event.RelationshipEvent; +import org.apache.cayenne.map.event.*; import org.apache.cayenne.modeler.Application; import org.apache.cayenne.modeler.ProjectController; import org.apache.cayenne.modeler.dialog.ConfirmRemoveDialog; @@ -191,6 +183,19 @@ else if (mediator.getCurrentProcedure() != null) { } } + else if (mediator.getCurrentTemplate() != null) { + if (dialog + .shouldDelete("template", mediator.getCurrentTemplate().getName())) { + + application.getUndoManager().addEdit( + new RemoveUndoableEdit(mediator.getCurrentDataMap(), mediator + .getCurrentTemplate())); + + removeTemplate(mediator.getCurrentDataMap(), mediator + .getCurrentTemplate()); + + } + } else if (mediator.getCurrentEmbeddable() != null) { if (dialog.shouldDelete("embeddable", mediator .getCurrentEmbeddable() @@ -455,6 +460,11 @@ public void removeDataMap(DataMap map) { URL reverseEngineeringURL = map.getReverseEngineering().getConfigurationSource().getURL(); unusedResources.add(reverseEngineeringURL); } + for (ClassTemplate template: map.getClassGenerationDescriptor().getTemplates().values()) { + if (template.getConfigurationSource() != null) { + unusedResources.add(template.getConfigurationSource().getURL()); + } + } } Iterator iterator = domain.getNodeDescriptors().iterator(); @@ -493,6 +503,24 @@ public void removeDbEntity(DataMap map, DbEntity ent) { mediator.fireDbEntityEvent(e); } + /** + * Removes current Template from its DataMap and fires "remove" TemplateEvent. + */ + public void removeTemplate(DataMap map, ClassTemplate template) { + ProjectController mediator = getProjectController(); + + TemplateEvent e = new TemplateEvent(Application.getFrame(), template, MapEvent.REMOVE); + e.setDomain((DataChannelDescriptor) mediator.getProject().getRootNode()); + + if (template.getConfigurationSource() != null) { + Collection unusedResources = getCurrentProject().getUnusedResources(); + unusedResources.add(template.getConfigurationSource().getURL()); + } + + map.removeTemplate(template.getName()); + mediator.fireTemplateEvent(e); + } + /** * Removes current Query from its DataMap and fires "remove" QueryEvent. */ @@ -615,6 +643,9 @@ else if (object instanceof Embeddable) { else if (object instanceof EmbeddableAttribute) { return true; } + else if(object instanceof ClassTemplate) { + return true; + } else { return false; } @@ -674,6 +705,10 @@ else if (object instanceof Embeddable) { (Embeddable) object); removeEmbeddable(((Embeddable) object).getDataMap(), (Embeddable) object); } + else if (object instanceof ClassTemplate) { + undo = new RemoveUndoableEdit(getProjectController().getCurrentDataMap(), (ClassTemplate) object); + removeTemplate(getProjectController().getCurrentDataMap(), (ClassTemplate) object); + } return undo; } diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/codegen/CodeGeneratorController.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/codegen/CodeGeneratorController.java index 751c3ca346..5ab274fdba 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/codegen/CodeGeneratorController.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/codegen/CodeGeneratorController.java @@ -117,7 +117,7 @@ else if (size == 1) { } label = label.concat("; "); - + int sizeEmb = getSelectedEmbeddablesSize(); if (sizeEmb == 0) { @@ -129,7 +129,7 @@ else if (sizeEmb == 1) { else { label =label + sizeEmb + " embeddables selected"; } - + view.getClassesCount().setText(label); } diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/codegen/CustomModeController.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/codegen/CustomModeController.java index 88781f6f2a..6ab220ceae 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/codegen/CustomModeController.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/codegen/CustomModeController.java @@ -188,16 +188,21 @@ private StandardPanelComponent createDataMapLineBy(DataMap dataMap, DataMapDefau protected void updateTemplates() { this.templateManager = getApplication().getCodeTemplateManager(); + List modelTemplates = new ArrayList(templateManager.getModelTemplates().keySet()); + Collections.sort(modelTemplates); + List customTemplates = new ArrayList(templateManager.getCustomTemplates().keySet()); Collections.sort(customTemplates); List superTemplates = new ArrayList(templateManager.getStandardSuperclassTemplates()); Collections.sort(superTemplates); superTemplates.addAll(customTemplates); + superTemplates.addAll(modelTemplates); List subTemplates = new ArrayList(templateManager.getStandardSubclassTemplates()); Collections.sort(subTemplates); subTemplates.addAll(customTemplates); + subTemplates.addAll(modelTemplates); this.view.getSubclassTemplate().setModel(new DefaultComboBoxModel(subTemplates.toArray())); this.view.getSuperclassTemplate().setModel(new DefaultComboBoxModel(superTemplates.toArray())); diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/codegen/GeneratorController.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/codegen/GeneratorController.java index 0df2a1d5b3..bcb279c2ec 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/codegen/GeneratorController.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/codegen/GeneratorController.java @@ -19,7 +19,7 @@ package org.apache.cayenne.modeler.dialog.codegen; -import org.apache.cayenne.gen.ArtifactsGenerationMode; +import org.apache.cayenne.map.template.ArtifactsGenerationMode; import org.apache.cayenne.gen.ClassGenerationAction; import org.apache.cayenne.map.DataMap; import org.apache.cayenne.map.Embeddable; @@ -57,7 +57,6 @@ /** * A mode-specific part of the code generation dialog. - * */ public abstract class GeneratorController extends CayenneController { diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/codegen/StandardModeController.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/codegen/StandardModeController.java index e700779c84..aa82a17bc7 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/codegen/StandardModeController.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/codegen/StandardModeController.java @@ -19,7 +19,7 @@ package org.apache.cayenne.modeler.dialog.codegen; -import org.apache.cayenne.gen.ArtifactsGenerationMode; +import org.apache.cayenne.map.template.ArtifactsGenerationMode; import org.apache.cayenne.gen.ClassGenerationAction; import org.apache.cayenne.map.DataMap; import org.apache.cayenne.modeler.pref.DataMapDefaults; diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/ReverseEngineeringView.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/ReverseEngineeringView.java index 3b3fad90bb..12bb5cf127 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/ReverseEngineeringView.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/ReverseEngineeringView.java @@ -1,20 +1,20 @@ /***************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.cayenne.modeler.dialog.db; @@ -108,6 +108,7 @@ private void initView() { this.configButton = new JButton("..."); this.configButton.setToolTipText("configure local DataSource"); this.syncButton = new JButton(); + this.syncButton.setContentAreaFilled(false); this.icon = ModelerUtil.buildIcon("icon-refresh.png"); this.syncButton.setIcon(icon); this.syncButton.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/template/TemplateController.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/template/TemplateController.java new file mode 100644 index 0000000000..b73e001280 --- /dev/null +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/template/TemplateController.java @@ -0,0 +1,103 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.cayenne.modeler.dialog.template; + +import org.apache.cayenne.map.template.ClassGenerationDescriptor; +import org.apache.cayenne.map.template.ClassTemplate; +import org.apache.cayenne.configuration.DataChannelDescriptor; +import org.apache.cayenne.map.DataMap; +import org.apache.cayenne.map.naming.DefaultUniqueNameGenerator; +import org.apache.cayenne.map.naming.NameCheckers; +import org.apache.cayenne.modeler.Application; +import org.apache.cayenne.modeler.ProjectController; +import org.apache.cayenne.modeler.action.ActionManager; +import org.apache.cayenne.modeler.action.CreateTemplateAction; +import org.apache.cayenne.modeler.action.RemoveAction; +import org.apache.cayenne.modeler.undo.CreateTemplateUndoableEdit; +import org.apache.cayenne.modeler.util.CayenneController; +import org.apache.cayenne.swing.BindingBuilder; + +import javax.swing.*; +import java.awt.*; + +/** + * @since 4.0 + */ +public class TemplateController extends CayenneController { + protected ProjectController projectController; + protected TemplateView view; + protected boolean canceled; + + public TemplateController(ProjectController controller, + TemplateView view) { + super(controller); + this.projectController = controller; + this.view = view; + + initBindings(); + } + + protected void initBindings() { + BindingBuilder builder = new BindingBuilder(getApplication().getBindingFactory(), this); + + builder.bindToAction(view.getAddTemplateButton(), "addTemplateAction()"); + builder.bindToAction(view.getDeleteTemplateButton(), "deleteTemplateAction()"); + } + + public void addTemplateAction() { + ActionManager actionManager = Application.getInstance().getActionManager(); + CreateTemplateAction action = actionManager + .getAction(CreateTemplateAction.class); + + DataChannelDescriptor dataChannelDescriptor = projectController.getCurrentDataChanel(); + String templateName = DefaultUniqueNameGenerator.generate(NameCheckers.template, dataChannelDescriptor); + + ClassTemplate template = new ClassTemplate(templateName); + DataMap dataMap = projectController.getCurrentDataMap(); + template.setDataMap(dataMap); + + action.createTemplate(dataMap, template); + application.getUndoManager().addEdit(new CreateTemplateUndoableEdit(dataMap, template)); + } + + public void deleteTemplateAction() { + DataMap dataMap = projectController.getCurrentDataMap(); + JTable templateList = view.getTemplateListEditor().getView().getTemplateList(); + String templateName = (String) templateList. + getValueAt(templateList.getSelectedRow(), 0); + if (templateName != null) { + DataMap currentDataMap = projectController.getCurrentDataMap(); + if (currentDataMap != null) { + ClassGenerationDescriptor classGenerationDescriptor = currentDataMap.getClassGenerationDescriptor(); + if (classGenerationDescriptor.getTemplates() != null) { + ClassTemplate template = classGenerationDescriptor.getTemplates().get(templateName); + + ActionManager actionManager = Application.getInstance().getActionManager(); + RemoveAction action = actionManager.getAction(RemoveAction.class); + action.removeTemplate(currentDataMap, template); + } + } + } + } + + public Component getView() { + return view; + } + +} diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/template/TemplateEditor.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/template/TemplateEditor.java new file mode 100644 index 0000000000..e8494ed7b0 --- /dev/null +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/template/TemplateEditor.java @@ -0,0 +1,43 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.cayenne.modeler.dialog.template; + +import org.apache.cayenne.modeler.ProjectController; +import org.apache.cayenne.modeler.util.CayenneController; + +/** + * @since 4.0 + */ +public class TemplateEditor extends CayenneController { + protected TemplateEditorView templateEditorView; + public TemplateEditor(ProjectController controller) { + super(controller); + + this.templateEditorView = new TemplateEditorView(); + } + + @Override + public TemplateEditorView getView() { + return templateEditorView; + } + + public void setView(String templateText) { + templateEditorView.getEditorPane().setText(templateText); + } +} diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/template/TemplateEditorView.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/template/TemplateEditorView.java new file mode 100644 index 0000000000..3a9a34db5e --- /dev/null +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/template/TemplateEditorView.java @@ -0,0 +1,58 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.cayenne.modeler.dialog.template; + +import javax.swing.*; +import java.awt.*; + +/** + * @since 4.0 + */ +public class TemplateEditorView extends JPanel { + protected JEditorPane editorPane; + protected JScrollPane scrollPane; + protected JLabel label; + + public TemplateEditorView() { + this.editorPane = new JEditorPane(); + this.scrollPane = new JScrollPane(editorPane); + this.label = new JLabel(); + this.label.setVisible(false); + + scrollPane = new JScrollPane(editorPane); + scrollPane.setPreferredSize(new Dimension(210, 300)); + } + + public JEditorPane getEditorPane() { + return editorPane; + } + + public JScrollPane getScrollPane() { + return scrollPane; + } + + public JLabel getLabel() { + return label; + } + + public void setLabel(JLabel label) { + this.label = label; + } +} diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/template/TemplateListEditor.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/template/TemplateListEditor.java new file mode 100644 index 0000000000..f067db0d7b --- /dev/null +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/template/TemplateListEditor.java @@ -0,0 +1,41 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.cayenne.modeler.dialog.template; + +import org.apache.cayenne.modeler.ProjectController; +import org.apache.cayenne.modeler.util.CayenneController; + +/** + * @since 4.0 + */ +public class TemplateListEditor extends CayenneController { + protected TemplateViewModel templateViewModel; + + public TemplateListEditor(ProjectController controller) { + super(controller); + + this.templateViewModel = new TemplateViewModel(); + this.templateViewModel.setVisible(true); + } + + @Override + public TemplateViewModel getView() { + return templateViewModel; + } +} diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/template/TemplateScrollPane.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/template/TemplateScrollPane.java new file mode 100644 index 0000000000..989875286b --- /dev/null +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/template/TemplateScrollPane.java @@ -0,0 +1,34 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.cayenne.modeler.dialog.template; + +import javax.swing.*; + +/** + * @since 4.0 + */ +public class TemplateScrollPane extends JScrollPane { + protected TemplateView templateView; + + public TemplateScrollPane(TemplateView templateView) { + super(templateView); + this.templateView = templateView; + } +} diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/template/TemplateView.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/template/TemplateView.java new file mode 100644 index 0000000000..2c87c75ac1 --- /dev/null +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/template/TemplateView.java @@ -0,0 +1,513 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.cayenne.modeler.dialog.template; + +import org.apache.cayenne.map.template.ClassGenerationDescriptor; +import org.apache.cayenne.map.template.ClassTemplate; +import org.apache.cayenne.configuration.DataChannelDescriptor; +import org.apache.cayenne.map.template.TemplateType; +import org.apache.cayenne.configuration.event.DataMapEvent; +import org.apache.cayenne.configuration.event.DataMapListener; +import org.apache.cayenne.map.DataMap; +import org.apache.cayenne.configuration.event.TemplateEvent; +import org.apache.cayenne.configuration.event.TemplateListener; +import org.apache.cayenne.map.naming.NameCheckers; +import org.apache.cayenne.modeler.ProjectController; +import org.apache.cayenne.modeler.event.DataMapDisplayEvent; +import org.apache.cayenne.modeler.event.DataMapDisplayListener; +import org.apache.cayenne.modeler.event.TemplateDisplayEvent; +import org.apache.cayenne.modeler.util.ModelerUtil; + +import javax.swing.*; +import javax.swing.event.*; +import javax.swing.table.DefaultTableModel; +import javax.swing.table.TableModel; +import javax.swing.table.TableRowSorter; +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import java.util.List; + +/** + * @since 4.0 + */ +public class TemplateView extends JPanel { + protected ProjectController controller; + protected TemplateController templateController; + protected JPanel templatePanel; + + protected JButton addTemplateButton; + protected JButton deleteTemplateButton; + + protected JComboBox templateType; + protected DefaultComboBoxModel comboBoxModel; + + protected JSeparator separator; + protected JSplitPane splitPane; + protected JLabel templateListLabel; + protected JLabel templateLabel; + protected Icon icon; + + protected TemplateEditor templateEditor; + protected DocumentListener documentListener; + + protected DefaultTableModel tableModel; + protected ListSelectionListener templateTextRefresh; + protected TableModelListener templateNameRefresh; + + protected int lastSelectedRow; + + protected Map templateViewMap; + + public TemplateListEditor getTemplateListEditor() { + return templateListEditor; + } + + protected TemplateListEditor templateListEditor; + + public TemplateView(ProjectController controller) { + this.controller = controller; + + this.templateViewMap = new HashMap<>(3); + initView(); + initController(); + } + + private DefaultTableModel getTemplateNames(DataMap dataMap) { + tableModel = new DefaultTableModel(); + tableModel.addColumn("Templates"); + if (dataMap != null) { + ClassGenerationDescriptor classGenerationDescriptor = dataMap.getClassGenerationDescriptor(); + if (classGenerationDescriptor != null) { + for (ClassTemplate template : classGenerationDescriptor.getTemplates().values()) { + Vector vector = new Vector<>(); + vector.add(template.getName()); + tableModel.addRow(vector); + } + } + } + return tableModel; + } + + private DefaultComboBoxModel getTemplateTypes() { + comboBoxModel = new DefaultComboBoxModel<>(); + comboBoxModel.addElement(null); + comboBoxModel.addElement(TemplateType.ENTITY_SUPERCLASS); + comboBoxModel.addElement(TemplateType.ENTITY_SINGLE_CLASS); + comboBoxModel.addElement(TemplateType.ENTITY_SUBCLASS); + comboBoxModel.addElement(TemplateType.EMBEDDABLE_SUPERCLASS); + comboBoxModel.addElement(TemplateType.EMBEDDABLE_SINGLE_CLASS); + comboBoxModel.addElement(TemplateType.EMBEDDABLE_SUBCLASS); + comboBoxModel.addElement(TemplateType.DATAMAP_SINGLE_CLASS); + comboBoxModel.addElement(TemplateType.DATAMAP_SUBCLASS); + comboBoxModel.addElement(TemplateType.DATAMAP_SUPERCLASS); + return comboBoxModel; + } + + private void initView() { + this.templatePanel = new JPanel(); + this.splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true); + + this.addTemplateButton = new JButton(); + this.addTemplateButton.setContentAreaFilled(false); + this.icon = ModelerUtil.buildIcon("icon-plus.gif"); + this.addTemplateButton.setIcon(icon); + this.addTemplateButton.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); + + this.deleteTemplateButton = new JButton(); + this.deleteTemplateButton.setContentAreaFilled(false); + this.icon = ModelerUtil.buildIcon("icon-trash.gif"); + this.deleteTemplateButton.setIcon(icon); + this.deleteTemplateButton.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); + + this.templateType = new JComboBox(); + this.templateListLabel = new JLabel("Templates"); + this.templateLabel = new JLabel("Editor"); + this.templateLabel.setBorder(BorderFactory.createEmptyBorder(0, 5, 5, 5)); + this.templateListLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 10, 5)); + this.templateEditor = new TemplateEditor(controller); + this.templateListEditor = new TemplateListEditor(controller); + + JPanel templateHeaderComponent = new JPanel(new FlowLayout(FlowLayout.LEFT)); + templateHeaderComponent.add(templateLabel); + templateHeaderComponent.add(addTemplateButton); + + JPanel leftComponent = new JPanel(); + leftComponent.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0)); + leftComponent.setLayout(new BorderLayout()); + leftComponent.add(templateHeaderComponent, BorderLayout.NORTH); + leftComponent.add(templateListEditor.getView().getScrollPane(), BorderLayout.CENTER); + + templateType.setModel(getTemplateTypes()); + JPanel typeSelector = new JPanel(new FlowLayout(FlowLayout.LEFT)); + typeSelector.add(templateType); + + JPanel rightComponent = new JPanel(); + rightComponent.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5)); + rightComponent.setLayout(new BorderLayout()); + rightComponent.add(typeSelector, BorderLayout.NORTH); + rightComponent.add(templateEditor.getView().getScrollPane(), BorderLayout.CENTER); + + splitPane.setLeftComponent(leftComponent); + splitPane.setRightComponent(rightComponent); + splitPane.setResizeWeight(0.2); + + JPanel splitWithErrorsPanel = new JPanel(); + splitWithErrorsPanel.setLayout(new BorderLayout()); + splitWithErrorsPanel.add(splitPane, BorderLayout.CENTER); + templateEditor.getView().getLabel().setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0)); + splitWithErrorsPanel.add(templateEditor.getView().getLabel(), BorderLayout.SOUTH); + + setLayout(new BorderLayout()); + add(splitWithErrorsPanel, BorderLayout.CENTER); + } + + public void initController() { + templateType.addItemListener(new ItemListener() { + + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + updateType(); + } + } + }); + + templateTextRefresh = new ListSelectionListener() { + public void valueChanged(ListSelectionEvent e) { + if (!e.getValueIsAdjusting()) { + if (!templateEditor.getView().getEditorPane().isVisible()) { + templateEditor.getView().getEditorPane().setVisible(true); + } + if (!templateType.isVisible()) { + templateType.setVisible(true); + } + JTable table = templateListEditor.getView().getTemplateList(); + + if (table.getSelectedRow() >= 0) { + String templateName = (String) table.getValueAt(table.getSelectedRow(), 0); + ClassTemplate template = controller.getCurrentDataMap().getClassGenerationDescriptor().getTemplates().get(templateName); + + TemplateDisplayEvent event = new TemplateDisplayEvent( + this, + template, + controller.getCurrentDataMap(), + (DataChannelDescriptor) controller.getProject().getRootNode()); + controller.fireTemplateDisplayEvent(event); + } + updateTemplate(); + } + } + }; + + ListSelectionModel cellSelectionModel = templateListEditor.getView().getTemplateList().getSelectionModel(); + cellSelectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + cellSelectionModel.addListSelectionListener(templateTextRefresh); + + controller.addTemplateListener(new TemplateListener() { + @Override + public void templateChanged(TemplateEvent e) {} + + @Override + public void templateAdded(TemplateEvent e) { + ClassTemplate template = e.getTemplate(); + + JTable templateList = templateListEditor.getView().getTemplateList(); + DataMap dataMap = template.getDataMap(); + + DefaultTableModel templateModel = getTemplateNames(dataMap); + + if (dataMap == controller.getCurrentDataMap()) { + templateList.setModel(templateModel); + } + int row = getRowFromModel(template.getName()); + + TableRowSorter sorter = createRowSorter(templateList.getModel()); + sorter.sort(); + sorter.setSortsOnUpdates(true); + templateList.setRowSorter(sorter); + templateViewMap.put(dataMap.getName(), templateModel); + + if (dataMap == controller.getCurrentDataMap()) { + row = sorter.convertRowIndexToView(row); + templateList.setRowSelectionInterval(row, row); + } + templateList.getModel().addTableModelListener(templateNameRefresh); + } + + @Override + public void templateRemoved(TemplateEvent e) { + JTable templateList = templateListEditor.getView().getTemplateList(); + + ClassTemplate template = e.getTemplate(); + DataMap dataMap = template.getDataMap(); + + DefaultTableModel templateModel = getTemplateNames(dataMap); + templateViewMap.put(dataMap.getName(), templateModel); + + int viewRow = 0; + if (dataMap == controller.getCurrentDataMap()) { + int row = getRowFromModel(template.getName()); + + TableRowSorter sorter = (TableRowSorter) templateList.getRowSorter(); + viewRow = sorter.convertRowIndexToView(row); + + templateList.setModel(templateModel); + + TableRowSorter newSorter = createRowSorter(templateList.getModel()); + newSorter.sort(); + newSorter.setSortsOnUpdates(true); + templateList.setRowSorter(newSorter); + + if (dataMap == controller.getCurrentDataMap()) { + if (templateList.getRowCount() > viewRow) { + templateList.setRowSelectionInterval(viewRow, viewRow); + } else { + if (templateList.getRowCount() != 0) { + templateList.setRowSelectionInterval(viewRow - 1, viewRow - 1); + } else { + templateEditor.getView().getEditorPane().setVisible(false); + templateType.setVisible(false); + + DataMapDisplayEvent event = new DataMapDisplayEvent( + this, + controller.getCurrentDataMap(), + controller.getCurrentDataChanel()); + controller.fireDataMapDisplayEvent(event); + } + } + } + } + templateList.getModel().addTableModelListener(templateNameRefresh); + } + }); + + templateNameRefresh = new TableModelListener() { + @Override + public void tableChanged(TableModelEvent e) { + JTable templateList = templateListEditor.getView().getTemplateList(); + + String updatedName = (String) templateList.getValueAt(templateList.getSelectedRow(), 0); + DataMap dataMap = controller.getCurrentDataMap(); + DataChannelDescriptor dataChannelDescriptor = controller.getCurrentDataChanel(); + boolean isNameInUse = NameCheckers.template.isNameInUse(dataChannelDescriptor, updatedName); + + int row; + if (!isNameInUse) { + String templateName = controller.getCurrentTemplate().getName(); + Map templates = dataMap.getClassGenerationDescriptor().getTemplates(); + ClassTemplate removedTemplate = templates.remove(templateName); + removedTemplate.setName(updatedName); + templates.put(updatedName, removedTemplate); + + DefaultTableModel templateModel = getTemplateNames(dataMap); + templateList.setModel(templateModel); + templateViewMap.put(dataMap.getName(), templateModel); + row = getRowFromModel(updatedName); + } else { + DefaultTableModel templateModel = getTemplateNames(dataMap); + templateList.setModel(templateModel); + templateViewMap.put(dataMap.getName(), templateModel); + row = getRowFromModel(controller.getCurrentTemplate().getName()); + } + + TableRowSorter newSorter = createRowSorter(templateList.getModel()); + newSorter.sort(); + newSorter.setSortsOnUpdates(true); + templateList.setRowSorter(newSorter); + + row = newSorter.convertRowIndexToView(row); + templateList.setRowSelectionInterval(row, row); + + templateList.getModel().addTableModelListener(templateNameRefresh); + } + }; + + documentListener = new DocumentListener() { + + @Override + public void insertUpdate(DocumentEvent e) { + JTable templateList = templateListEditor.getView().getTemplateList(); + + if (e.getType() == DocumentEvent.EventType.INSERT) { + if (templateList.getSelectedRow() >= 0) { + String name = (String) templateList.getValueAt(templateList.getSelectedRow(), 0); + + DataMap dataMap = controller.getCurrentDataMap(); + ClassTemplate classTemplate = dataMap.getClassGenerationDescriptor().getTemplates().get(name); + + classTemplate.setText(templateEditor.getView().getEditorPane().getText()); + + controller.fireTemplateEvent(new TemplateEvent(this, classTemplate, 1)); + } + } + } + + @Override + public void removeUpdate(DocumentEvent e) { + JTable templateList = templateListEditor.getView().getTemplateList(); + + if (e.getType() == DocumentEvent.EventType.REMOVE) { + if (templateList.getSelectedRow() >= 0) { + String name = (String) templateList.getValueAt(templateList.getSelectedRow(), 0); + + DataMap dataMap = controller.getCurrentDataMap(); + ClassTemplate classTemplate = dataMap.getClassGenerationDescriptor().getTemplates().get(name); + + classTemplate.setText(templateEditor.getView().getEditorPane().getText()); + + controller.fireTemplateEvent(new TemplateEvent(this, classTemplate, 1)); + } + } + } + + @Override + public void changedUpdate(DocumentEvent e) { + } + }; + + templateEditor.getView().getEditorPane().getDocument().addDocumentListener(documentListener); + + controller.addDataMapDisplayListener(new DataMapDisplayListener() { + public void currentDataMapChanged(DataMapDisplayEvent e) { + if (!templateViewMap.containsKey(e.getDataMap().getName())) { + TemplateViewModel model = templateListEditor.getView(); + JTable templateList = model.getTemplateList(); + model.getTemplateList().setModel(getTemplateNames(controller.getCurrentDataMap())); + + templateList.getModel().addTableModelListener(templateNameRefresh); + templateList.setAutoCreateRowSorter(true); + + TableRowSorter sorter = createRowSorter(templateList.getModel()); + sorter.sort(); + sorter.setSortsOnUpdates(true); + templateList.setRowSorter(sorter); + + templateViewMap.put(e.getDataMap().getName(), (DefaultTableModel) templateList.getModel()); + } else { + DefaultTableModel model = templateViewMap.get(e.getDataMap().getName()); + templateListEditor.getView().setTableModel(model); + JTable templateList = templateListEditor.getView().getTemplateList(); + + TableRowSorter newSorter = createRowSorter(templateList.getModel()); + newSorter.sort(); + newSorter.setSortsOnUpdates(true); + templateList.setRowSorter(newSorter); + } + TemplateViewModel model = templateListEditor.getView(); + if (model != null) { + templateEditor.getView().getEditorPane().setVisible(false); + templateType.setVisible(false); + + lastSelectedRow = -1; + templateEditor.getView().getEditorPane().setText(""); + } + } + }); + + controller.addDataMapListener(new DataMapListener() { + @Override + public void dataMapChanged(DataMapEvent e) { + } + + @Override + public void dataMapAdded(DataMapEvent e) { + } + + @Override + public void dataMapRemoved(DataMapEvent e) { + templateViewMap.remove(e.getDataMap().getName()); + } + }); + + this.templateController = new TemplateController(controller, this); + } + + private int getRowFromModel(String templateName) { + JTable templateList = templateListEditor.getView().getTemplateList(); + int row = 0; + int i = 0; + DefaultTableModel model = (DefaultTableModel) templateList.getModel(); + for (Object templates : model.getDataVector()) { + Vector vector = (Vector) templates; + if (vector.get(0).equals(templateName)) { + row = i; + break; + } + ++i; + } + return row; + } + + private TableRowSorter createRowSorter(TableModel model) { + TableRowSorter sorter = new TableRowSorter<>(model); + + List sortKeys = new ArrayList<>(1); + sortKeys.add(new RowSorter.SortKey(0, SortOrder.ASCENDING)); + sorter.setSortKeys(sortKeys); + + return sorter; + } + + public JButton getAddTemplateButton() { + return addTemplateButton; + } + + private void updateTemplate() { + DataMap dataMap = controller.getCurrentDataMap(); + JTable templateList = templateListEditor.getView().getTemplateList(); + + lastSelectedRow = templateList.getSelectedRow(); + + if (lastSelectedRow >= 0) { + ClassGenerationDescriptor classGenerationDescriptor = dataMap.getClassGenerationDescriptor(); + if (classGenerationDescriptor.getTemplates().size() > 0) { + String templateName = (String) templateList.getValueAt(lastSelectedRow, 0); + ClassTemplate template = classGenerationDescriptor.getTemplates().get(templateName); + if (template != null) { + templateEditor.setView(template.getText()); + templateType.setSelectedItem(template.getType()); + } else { + templateEditor.getView().getEditorPane().setText(""); + templateType.setSelectedIndex(-1); + } + } + } else { + templateEditor.getView().getEditorPane().setText(""); + templateType.setSelectedIndex(-1); + } + } + + private void updateType() { + DataMap dataMap = controller.getCurrentDataMap(); + JTable templateList = templateListEditor.getView().getTemplateList(); + + ClassGenerationDescriptor classGenerationDescriptor = dataMap.getClassGenerationDescriptor(); + if (templateList.getRowCount() >= 0) { + String templateName = (String) templateList.getValueAt(lastSelectedRow, 0); + if (templateName != null) { + TemplateType type = (TemplateType) templateType.getSelectedItem(); + classGenerationDescriptor.getTemplates().get(templateName).setType(type); + } + } + } + + public JButton getDeleteTemplateButton() { + return deleteTemplateButton; + } +} diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/template/TemplateViewModel.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/template/TemplateViewModel.java new file mode 100644 index 0000000000..b564f7697e --- /dev/null +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/template/TemplateViewModel.java @@ -0,0 +1,57 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.cayenne.modeler.dialog.template; + +import javax.swing.*; +import javax.swing.table.DefaultTableModel; +import java.awt.*; + +/** + * @since 4.0 + */ +public class TemplateViewModel extends JPanel { + protected JTable templateList; + protected JScrollPane scrollPane; + + public TemplateViewModel() { + this.templateList = new JTable(); + this.templateList.setBounds(10, 0, 457, 103); + this.templateList.setShowGrid(false); + this.templateList.getTableHeader().setEnabled(false); + + scrollPane = new JScrollPane(templateList); + scrollPane.setPreferredSize(new Dimension(210, 300)); + } + + public JScrollPane getScrollPane() { + return scrollPane; + } + + public JTable getTemplateList() { + return templateList; + } + + public void setTableModel(DefaultTableModel model) { + templateList.setModel(model); + } + + public void setTable(JTable table) { + templateList = table; + } +} diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/DataMapTabbedView.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/DataMapTabbedView.java index 76b842d3a6..4e383b612e 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/DataMapTabbedView.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/DataMapTabbedView.java @@ -28,6 +28,8 @@ import org.apache.cayenne.modeler.dialog.db.ReverseEngineeringView; import java.awt.event.ActionEvent; +import org.apache.cayenne.modeler.dialog.template.TemplateScrollPane; +import org.apache.cayenne.modeler.dialog.template.TemplateView; /** @@ -37,6 +39,7 @@ public class DataMapTabbedView extends JTabbedPane implements ChangeListener { ProjectController mediator; private ReverseEngineeringScrollPane reverseEngineeringScrollPane; + private TemplateScrollPane templateScrollPane; /** * constructor @@ -66,10 +69,19 @@ private void initView() { reverseEngineeringScrollPane = new ReverseEngineeringScrollPane(reverseEngineeringView); addTab("Reverse Engineering", reverseEngineeringScrollPane); addChangeListener(this); + + TemplateView templateView = new TemplateView(mediator); + templateScrollPane = new TemplateScrollPane(templateView); + addTab("Template", templateScrollPane); } @Override public void stateChanged(ChangeEvent changeEvent) { + if (getSelectedComponent().equals(templateScrollPane)) { + ActionEvent actionEvent = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, ""); + actionEvent.setSource(templateScrollPane); + } + if (getSelectedComponent().equals(reverseEngineeringScrollPane)) { ActionEvent actionEvent = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, ""); actionEvent.setSource(reverseEngineeringScrollPane); diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/EditorView.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/EditorView.java index 09ee599da8..0c8a48149b 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/EditorView.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/EditorView.java @@ -46,6 +46,8 @@ import org.apache.cayenne.modeler.event.ProcedureDisplayListener; import org.apache.cayenne.modeler.event.QueryDisplayEvent; import org.apache.cayenne.modeler.event.QueryDisplayListener; +import org.apache.cayenne.modeler.event.TemplateDisplayEvent; +import org.apache.cayenne.modeler.event.TemplateDisplayListener; import org.apache.cayenne.modeler.pref.ComponentGeometry; import org.apache.commons.logging.LogFactory; @@ -72,7 +74,7 @@ public class EditorView extends JPanel implements ObjEntityDisplayListener, DbEntityDisplayListener, DomainDisplayListener, DataMapDisplayListener, DataNodeDisplayListener, ProcedureDisplayListener, QueryDisplayListener, - MultipleObjectsDisplayListener, EmbeddableDisplayListener { + MultipleObjectsDisplayListener, EmbeddableDisplayListener, TemplateDisplayListener { private static final String EMPTY_VIEW = "Empty"; private static final String DOMAIN_VIEW = "Domain"; @@ -276,6 +278,7 @@ private void initController() { eventController.addQueryDisplayListener(this); eventController.addMultipleObjectsDisplayListener(this); eventController.addEmbeddableDisplayListener(this); + eventController.addTemplateDisplayListener(this); /** * Moving this to try-catch block per CAY-940. Exception will be stack-traced @@ -368,4 +371,8 @@ public void currentEmbeddableChanged(EmbeddableDisplayEvent e) { else detailLayout.show(detailPanel, EMBEDDABLE_VIEW); } -} \ No newline at end of file + + public void currentTemplateChanged(TemplateDisplayEvent e) { + + } +} diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/event/TemplateDisplayEvent.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/event/TemplateDisplayEvent.java new file mode 100644 index 0000000000..3b0c515b16 --- /dev/null +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/event/TemplateDisplayEvent.java @@ -0,0 +1,56 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.cayenne.modeler.event; + +import org.apache.cayenne.map.template.ClassTemplate; +import org.apache.cayenne.configuration.DataChannelDescriptor; +import org.apache.cayenne.map.DataMap; + +/** + * @since 4.0 + */ +public class TemplateDisplayEvent extends DataMapDisplayEvent { + protected ClassTemplate template; + protected boolean templateChanged = true; + + /** + * Creates a new TemplateDisplayEvent + */ + public TemplateDisplayEvent(Object src, ClassTemplate template, DataMap map, DataChannelDescriptor domain) { + super(src, map, domain); + this.template = template; + } + + public ClassTemplate getTemplate() { + return template; + } + + public void setTemplate(ClassTemplate template) { + this.template = template; + } + + public boolean isTemplateChanged() { + return templateChanged; + } + + public void setTemplateChanged(boolean b) { + templateChanged = b; + } + +} diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/event/TemplateDisplayListener.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/event/TemplateDisplayListener.java new file mode 100644 index 0000000000..d4324464e9 --- /dev/null +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/event/TemplateDisplayListener.java @@ -0,0 +1,28 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.cayenne.modeler.event; + +import java.util.EventListener; + +/** + * @since 4.0 + */ +public interface TemplateDisplayListener extends EventListener { + void currentTemplateChanged(TemplateDisplayEvent e); +} diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/undo/CreateTemplateUndoableEdit.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/undo/CreateTemplateUndoableEdit.java new file mode 100644 index 0000000000..1720781479 --- /dev/null +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/undo/CreateTemplateUndoableEdit.java @@ -0,0 +1,62 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.cayenne.modeler.undo; + +import org.apache.cayenne.map.template.ClassTemplate; +import org.apache.cayenne.map.DataMap; +import org.apache.cayenne.modeler.action.CreateTemplateAction; +import org.apache.cayenne.modeler.action.RemoveAction; + +import javax.swing.undo.CannotRedoException; +import javax.swing.undo.CannotUndoException; + +/** + * @since 4.0 + */ +public class CreateTemplateUndoableEdit extends CayenneUndoableEdit { + @Override + public boolean canRedo() { + return true; + } + + @Override + public String getPresentationName() { + return "Create Template"; + } + + private DataMap map; + private ClassTemplate template; + + public CreateTemplateUndoableEdit(DataMap map, ClassTemplate template) { + this.map = map; + this.template = template; + } + + @Override + public void redo() throws CannotRedoException { + CreateTemplateAction action = actionManager.getAction(CreateTemplateAction.class); + action.createTemplate(map, template); + } + + @Override + public void undo() throws CannotUndoException { + RemoveAction action = actionManager.getAction(RemoveAction.class); + action.removeTemplate(map, template); + } +} diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/undo/PasteUndoableEdit.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/undo/PasteUndoableEdit.java index 6b3b59f3d7..df614b7ca5 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/undo/PasteUndoableEdit.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/undo/PasteUndoableEdit.java @@ -21,6 +21,7 @@ import javax.swing.undo.CannotRedoException; import javax.swing.undo.CannotUndoException; +import org.apache.cayenne.map.template.ClassTemplate; import org.apache.cayenne.configuration.DataChannelDescriptor; import org.apache.cayenne.configuration.DataNodeDescriptor; import org.apache.cayenne.map.DataMap; @@ -116,6 +117,9 @@ else if (content instanceof QueryDescriptor) { else if (content instanceof Procedure) { rAction.removeProcedure(map, (Procedure) content); } + else if (content instanceof ClassTemplate) { + rAction.removeTemplate(map, (ClassTemplate) content); + } } else if (where instanceof DbEntity) { if (content instanceof DbEntity) { @@ -175,6 +179,9 @@ else if (where instanceof Procedure) { }); } } + else if (content instanceof ClassTemplate) { + rAction.removeTemplate(map, (ClassTemplate) content); + } else if (content instanceof Embeddable) { rAction.removeEmbeddable(map, (Embeddable) content); } diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/undo/RemoveUndoableEdit.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/undo/RemoveUndoableEdit.java index d5dde14a41..0669e07488 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/undo/RemoveUndoableEdit.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/undo/RemoveUndoableEdit.java @@ -28,6 +28,7 @@ import javax.swing.undo.CannotRedoException; import javax.swing.undo.CannotUndoException; +import org.apache.cayenne.map.template.ClassTemplate; import org.apache.cayenne.configuration.DataChannelDescriptor; import org.apache.cayenne.configuration.DataNodeDescriptor; import org.apache.cayenne.configuration.event.DataNodeEvent; @@ -50,6 +51,7 @@ import org.apache.cayenne.modeler.action.CreateQueryAction; import org.apache.cayenne.modeler.action.CreateRelationshipAction; import org.apache.cayenne.modeler.action.RemoveAction; +import org.apache.cayenne.modeler.action.CreateTemplateAction; import org.apache.cayenne.map.QueryDescriptor; public class RemoveUndoableEdit extends CayenneUndoableEdit { @@ -65,11 +67,13 @@ public class RemoveUndoableEdit extends CayenneUndoableEdit { private Embeddable embeddable; + private ClassTemplate template; + private Map> dbRelationshipMap = new HashMap>(); private Map> objRelationshipMap = new HashMap>(); private static enum REMOVE_MODE { - OBJECT_ENTITY, DB_ENTITY, QUERY, PROCEDURE, MAP_FROM_NODE, MAP_FROM_DOMAIN, NODE, DOMAIN, EMBEDDABLE + OBJECT_ENTITY, DB_ENTITY, QUERY, PROCEDURE, MAP_FROM_NODE, MAP_FROM_DOMAIN, NODE, DOMAIN, EMBEDDABLE, TEMPLATE }; private REMOVE_MODE mode; @@ -89,14 +93,12 @@ public RemoveUndoableEdit(Application application, DataNodeDescriptor node, public RemoveUndoableEdit(Application application, DataMap map) { this.domain = (DataChannelDescriptor) application.getProject().getRootNode(); - ; this.map = map; this.mode = REMOVE_MODE.MAP_FROM_DOMAIN; } public RemoveUndoableEdit(Application application, DataNodeDescriptor node) { this.domain = (DataChannelDescriptor) application.getProject().getRootNode(); - ; this.dataNode = node; this.mode = REMOVE_MODE.NODE; } @@ -169,6 +171,12 @@ public RemoveUndoableEdit(DataMap map, Embeddable embeddable) { this.mode = REMOVE_MODE.EMBEDDABLE; } + public RemoveUndoableEdit(DataMap map, ClassTemplate template) { + this.map = map; + this.template = template; + this.mode = REMOVE_MODE.TEMPLATE; + } + @Override public String getPresentationName() { switch (this.mode) { @@ -190,6 +198,8 @@ public String getPresentationName() { return "Remove DataDomain"; case EMBEDDABLE: return "Remove Embeddable"; + case TEMPLATE: + return "Remove Template"; default: return "Remove"; @@ -223,6 +233,10 @@ public void redo() throws CannotRedoException { break; case EMBEDDABLE: action.removeEmbeddable(map, embeddable); + break; + case TEMPLATE: + action.removeTemplate(map, template); + break; } } @@ -233,6 +247,13 @@ public void undo() throws CannotUndoException { .getAction(CreateRelationshipAction.class); switch (this.mode) { + case TEMPLATE: { + CreateTemplateAction action = actionManager + .getAction(CreateTemplateAction.class); + action.createTemplate(map, template); + + break; + } case OBJECT_ENTITY: { for (Entry> entry : objRelationshipMap .entrySet()) { diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/util/state/DisplayEventTypes.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/util/state/DisplayEventTypes.java index 44bac2cb08..ddf314122b 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/util/state/DisplayEventTypes.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/util/state/DisplayEventTypes.java @@ -157,6 +157,13 @@ public String toString() { } }, + TemplateDisplayEvent { + @Override + DisplayEventType createDisplayEventType(ProjectController controller) { + return new TemplateDisplayEventType(controller); + } + }, + ProcedureParameterDisplayEvent { @Override DisplayEventType createDisplayEventType(ProjectController controller) { diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/util/state/TemplateDisplayEventType.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/util/state/TemplateDisplayEventType.java new file mode 100644 index 0000000000..ab78eb5757 --- /dev/null +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/util/state/TemplateDisplayEventType.java @@ -0,0 +1,19 @@ +package org.apache.cayenne.modeler.util.state; + +import org.apache.cayenne.modeler.ProjectController; + +/** + * @since 4.0 + */ +public class TemplateDisplayEventType extends DisplayEventType { + + public TemplateDisplayEventType(ProjectController controller) { + super(controller); + } + + @Override + public void fireLastDisplayEvent() {} + + @Override + public void saveLastDisplayEvent() {} +} diff --git a/plugins/maven-cayenne-plugin/src/main/java/org/apache/cayenne/tools/CayenneGeneratorMojo.java b/plugins/maven-cayenne-plugin/src/main/java/org/apache/cayenne/tools/CayenneGeneratorMojo.java index 87a3156490..66285690af 100644 --- a/plugins/maven-cayenne-plugin/src/main/java/org/apache/cayenne/tools/CayenneGeneratorMojo.java +++ b/plugins/maven-cayenne-plugin/src/main/java/org/apache/cayenne/tools/CayenneGeneratorMojo.java @@ -20,9 +20,20 @@ package org.apache.cayenne.tools; import org.apache.cayenne.access.loader.NamePatternMatcher; +import org.apache.cayenne.map.template.ClassGenerationDescriptor; +import org.apache.cayenne.map.template.ClassTemplate; +import org.apache.cayenne.map.template.TemplateType; +import static org.apache.cayenne.map.template.TemplateType.*; + +import org.apache.cayenne.configuration.ConfigurationNameMapper; +import org.apache.cayenne.configuration.server.ServerModule; +import org.apache.cayenne.di.DIBootstrap; +import org.apache.cayenne.di.Injector; import org.apache.cayenne.gen.ClassGenerationAction; import org.apache.cayenne.gen.ClientClassGenerationAction; import org.apache.cayenne.map.DataMap; +import org.apache.cayenne.resource.Resource; +import org.apache.cayenne.resource.URLResource; import org.apache.commons.logging.Log; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; @@ -30,166 +41,200 @@ import java.io.File; import java.io.FilenameFilter; +import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; +import java.net.URLDecoder; +import java.util.HashMap; +import java.util.Map; /** * Maven mojo to perform class generation from data map. This class is an Maven * adapter to DefaultClassGenerator class. - * - * @since 3.0 - * + * * @phase generate-sources * @goal cgen + * @since 3.0 */ public class CayenneGeneratorMojo extends AbstractMojo { public static final File[] NO_FILES = new File[0]; /** - * Path to additional DataMap XML files to use for class generation. - * - * @parameter additionalMaps="additionalMaps" - */ - private File additionalMaps; - - /** - * Whether we are generating classes for the client tier in a Remote Object - * Persistence application. Default is false. - * - * @parameter client="client" default-value="false" - */ - private boolean client; - - /** - * Destination directory for Java classes (ignoring their package names). - * - * @parameter destDir="destDir" default-value="${project.build.sourceDirectory}" - */ - private File destDir; - - /** - * Specify generated file encoding if different from the default on current - * platform. Target encoding must be supported by the JVM running Maven - * build. Standard encodings supported by Java on all platforms are - * US-ASCII, ISO-8859-1, UTF-8, UTF-16BE, UTF-16LE, UTF-16. See Sun Java - * Docs for java.nio.charset.Charset for more information. - * - * @parameter encoding="encoding" - */ - private String encoding; - - /** - * Entities (expressed as a perl5 regex) to exclude from template - * generation. (Default is to include all entities in the DataMap). - * - * @parameter excludeEntities="excludeEntities" - */ - private String excludeEntities; - - /** - * Entities (expressed as a perl5 regex) to include in template generation. - * (Default is to include all entities in the DataMap). - * - * @parameter includeEntities="includeEntities" - */ - private String includeEntities; - - /** - * If set to true, will generate subclass/superclass pairs, - * with all generated code included in superclass (default is - * true). - * - * @parameter makePairs="makePairs" default-value="true" - */ - private boolean makePairs; - - /** - * DataMap XML file to use as a base for class generation. - * - * @parameter map="map" - * @required - */ - private File map; - - /** - * Specifies generator iteration target. "entity" performs one - * iteration for each selected entity. "datamap" performs one - * iteration per datamap (This is always one iteration since cgen currently - * supports specifying one-and-only-one datamap). (Default is - * "entity") - * - * @parameter mode="mode" default-value="entity" - */ - private String mode; - - /** - * Name of file for generated output. (Default is "*.java") - * - * @parameter outputPattern="outputPattern" default-value="*.java" - */ - private String outputPattern; - - /** - * If set to true, will overwrite older versions of generated - * classes. Ignored unless makepairs is set to false. - * - * @parameter overwrite="overwrite" default-value="false" - */ - private boolean overwrite; - - /** - * Java package name of generated superclasses. Ignored unless - * makepairs set to true. If omitted, each - * superclass will be assigned the same package as subclass. Note that - * having superclass in a different package would only make sense when - * usepkgpath is set to true. Otherwise classes - * from different packages will end up in the same directory. - * - * @parameter superPkg="superPkg" - */ - private String superPkg; - - /** - * Location of Velocity template file for Entity superclass generation. - * Ignored unless makepairs set to true. If - * omitted, default template is used. - * - * @parameter superTemplate="superTemplate" - */ - private String superTemplate; - - /** - * Location of Velocity template file for Entity class generation. If - * omitted, default template is used. - * - * @parameter template="template" - */ - private String template; - - /** - * Location of Velocity template file for Embeddable superclass generation. - * Ignored unless makepairs set to true. If - * omitted, default template is used. - * - * @parameter embeddableSuperTemplate="embeddableSuperTemplate" - */ - private String embeddableSuperTemplate; - - /** - * Location of Velocity template file for Embeddable class generation. If - * omitted, default template is used. - * - * @parameter embeddableTemplate="embeddableTemplate" - */ - private String embeddableTemplate; - - /** - * If set to true (default), a directory tree will be generated - * in "destDir" corresponding to the class package structure, if set to - * false, classes will be generated in "destDir" - * ignoring their package. - * - * @parameter usePkgPath="usePkgPath" default-value="true" - */ - private boolean usePkgPath; + * Path to additional DataMap XML files to use for class generation. + * + * @parameter additionalMaps="additionalMaps" + */ + private File additionalMaps; + + /** + * Whether we are generating classes for the client tier in a Remote Object + * Persistence application. Default is false. + * + * @parameter client="client" default-value="false" + */ + private boolean client; + + /** + * Destination directory for Java classes (ignoring their package names). + * + * @parameter destDir="destDir" default-value="${project.build.sourceDirectory}" + */ + private File destDir; + + /** + * Specify generated file encoding if different from the default on current + * platform. Target encoding must be supported by the JVM running Maven + * build. Standard encodings supported by Java on all platforms are + * US-ASCII, ISO-8859-1, UTF-8, UTF-16BE, UTF-16LE, UTF-16. See Sun Java + * Docs for java.nio.charset.Charset for more information. + * + * @parameter encoding="encoding" + */ + private String encoding; + + /** + * Entities (expressed as a perl5 regex) to exclude from template + * generation. (Default is to include all entities in the DataMap). + * + * @parameter excludeEntities="excludeEntities" + */ + private String excludeEntities; + + public void setExcludeEntities(String excludeEntities) { + isClassGenerationDefined = true; + this.excludeEntities = excludeEntities; + } + + /** + * Entities (expressed as a perl5 regex) to include in template generation. + * (Default is to include all entities in the DataMap). + * + * @parameter includeEntities="includeEntities" + */ + private String includeEntities; + + public void setIncludeEntities(String includeEntities) { + isClassGenerationDefined = true; + this.includeEntities = includeEntities; + } + + /** + * If set to true, will generate subclass/superclass pairs, + * with all generated code included in superclass (default is + * true). + * + * @parameter makePairs="makePairs" default-value="true" + */ + private boolean makePairs; + + /** + * DataMap XML file to use as a base for class generation. + * + * @parameter map="map" + * @required + */ + private File map; + + /** + * Specifies generator iteration target. "entity" performs one + * iteration for each selected entity. "datamap" performs one + * iteration per datamap (This is always one iteration since cgen currently + * supports specifying one-and-only-one datamap). (Default is + * "entity") + * + * @parameter mode="mode" default-value="entity" + */ + private String mode; + + /** + * Name of file for generated output. (Default is "*.java") + * + * @parameter outputPattern="outputPattern" default-value="*.java" + */ + private String outputPattern; + + /** + * If set to true, will overwrite older versions of generated + * classes. Ignored unless makepairs is set to false. + * + * @parameter overwrite="overwrite" default-value="false" + */ + private boolean overwrite; + + /** + * Java package name of generated superclasses. Ignored unless + * makepairs set to true. If omitted, each + * superclass will be assigned the same package as subclass. Note that + * having superclass in a different package would only make sense when + * usepkgpath is set to true. Otherwise classes + * from different packages will end up in the same directory. + * + * @parameter superPkg="superPkg" + */ + private String superPkg; + + /** + * Location of Velocity template file for Entity superclass generation. + * Ignored unless makepairs set to true. If + * omitted, default template is used. + * + * @parameter superTemplate="superTemplate" + */ + private String superTemplate; + + public void setSuperTemplate(String superTemplate) { + isClassGenerationDefined = true; + this.superTemplate = superTemplate; + } + + /** + * Location of Velocity template file for Entity class generation. If + * omitted, default template is used. + * + * @parameter template="template" + */ + private String template; + + public void setTemplate(String template) { + isClassGenerationDefined = true; + this.template = template; + } + + /** + * Location of Velocity template file for Embeddable superclass generation. + * Ignored unless makepairs set to true. If + * omitted, default template is used. + * + * @parameter embeddableSuperTemplate="embeddableSuperTemplate" + */ + private String embeddableSuperTemplate; + + public void setEmbeddableSuperTemplate(String embeddableSuperTemplate) { + isClassGenerationDefined = true; + this.embeddableSuperTemplate = embeddableSuperTemplate; + } + + /** + * Location of Velocity template file for Embeddable class generation. If + * omitted, default template is used. + * + * @parameter embeddableTemplate="embeddableTemplate" + */ + private String embeddableTemplate; + + public void setEmbeddableTemplate(String embeddableTemplate) { + isClassGenerationDefined = true; + this.embeddableTemplate = embeddableTemplate; + } + + /** + * If set to true (default), a directory tree will be generated + * in "destDir" corresponding to the class package structure, if set to + * false, classes will be generated in "destDir" + * ignoring their package. + * + * @parameter usePkgPath="usePkgPath" default-value="true" + */ + private boolean usePkgPath; /** * If set to true, will generate String Property names. @@ -199,57 +244,73 @@ public class CayenneGeneratorMojo extends AbstractMojo { */ private boolean createPropertyNames; - public void execute() throws MojoExecutionException, MojoFailureException { - // Create the destination directory if necessary. - // TODO: (KJM 11/2/06) The destDir really should be added as a - // compilation resource for maven. - if (!destDir.exists()) { - destDir.mkdirs(); - } - - Log logger = new MavenLogger(this); - CayenneGeneratorMapLoaderAction loaderAction = new CayenneGeneratorMapLoaderAction(); - loaderAction.setMainDataMapFile(map); - - CayenneGeneratorEntityFilterAction filterAction = new CayenneGeneratorEntityFilterAction(); - filterAction.setClient(client); - filterAction.setNameFilter(NamePatternMatcher.build(logger, includeEntities, excludeEntities)); - - try { - loaderAction.setAdditionalDataMapFiles(convertAdditionalDataMaps()); - - DataMap dataMap = loaderAction.getMainDataMap(); - - ClassGenerationAction generator = createGenerator(); - generator.setLogger(logger); - generator.setTimestamp(map.lastModified()); - generator.setDataMap(dataMap); - generator.addEntities(filterAction.getFilteredEntities(dataMap)); - // ksenia khailenko 15.10.2010 - // TODO add the "includeEmbeddables" and "excludeEmbeddables" - // attributes - generator.addEmbeddables(dataMap.getEmbeddables()); - // TODO add the "includeQueries" and "excludeQueries" attributes - generator.addQueries(dataMap.getQueryDescriptors()); - generator.execute(); - } catch (Exception e) { - throw new MojoExecutionException("Error generating classes: ", e); - } - } - - /** - * Loads and returns DataMap based on map attribute. - */ - protected File[] convertAdditionalDataMaps() throws Exception { - - if (additionalMaps == null) { - return NO_FILES; - } - - if (!additionalMaps.isDirectory()) { - throw new MojoFailureException( - "'additionalMaps' must be a directory."); - } + /** + * Flag which defines from where to take the configuration of cgen. + * If we define the config of cgen in pom.xml + * we should set it to true or it will be setted to true automatically + * if we will define some configuration parameters in pom.xml + * Else it remains default(false) and for cgen + * we use the configuration defined in signed dataMap + * + * @parameter isClassGenerationDefined="isClassGenerationDefined" default-value="false" + */ + private boolean isClassGenerationDefined; + + public void setIsClassGenerationDefined(boolean isClassGenerationDefined) { + this.isClassGenerationDefined = isClassGenerationDefined; + } + + public void execute() throws MojoExecutionException, MojoFailureException { + // Create the destination directory if necessary. + // TODO: (KJM 11/2/06) The destDir really should be added as a + // compilation resource for maven. + if (!destDir.exists()) { + destDir.mkdirs(); + } + + Log logger = new MavenLogger(this); + CayenneGeneratorMapLoaderAction loaderAction = new CayenneGeneratorMapLoaderAction(); + loaderAction.setMainDataMapFile(map); + + CayenneGeneratorEntityFilterAction filterAction = new CayenneGeneratorEntityFilterAction(); + filterAction.setClient(client); + filterAction.setNameFilter(NamePatternMatcher.build(logger, includeEntities, excludeEntities)); + + try { + loaderAction.setAdditionalDataMapFiles(convertAdditionalDataMaps()); + + DataMap dataMap = loaderAction.getMainDataMap(); + + ClassGenerationAction generator = createGenerator(dataMap); + generator.setLogger(logger); + generator.setTimestamp(map.lastModified()); + generator.setDataMap(dataMap); + generator.addEntities(filterAction.getFilteredEntities(dataMap)); + // ksenia khailenko 15.10.2010 + // TODO add the "includeEmbeddables" and "excludeEmbeddables" + // attributes + generator.addEmbeddables(dataMap.getEmbeddables()); + // TODO add the "includeQueries" and "excludeQueries" attributes + generator.addQueries(dataMap.getQueryDescriptors()); + generator.execute(); + } catch (Exception e) { + throw new MojoExecutionException("Error generating classes: ", e); + } + } + + /** + * Loads and returns DataMap based on map attribute. + */ + protected File[] convertAdditionalDataMaps() throws Exception { + + if (additionalMaps == null) { + return NO_FILES; + } + + if (!additionalMaps.isDirectory()) { + throw new MojoFailureException( + "'additionalMaps' must be a directory."); + } FilenameFilter mapFilter = new FilenameFilter() { @Override @@ -259,35 +320,208 @@ public boolean accept(File dir, String name) { } }; return additionalMaps.listFiles(mapFilter); - } - - /** - * Factory method to create internal class generator. Called from - * constructor. - */ - protected ClassGenerationAction createGenerator() { - - ClassGenerationAction action; - if (client) { - action = new ClientClassGenerationAction(); - } else { - action = new ClassGenerationAction(); - } - - action.setDestDir(destDir); - action.setEncoding(encoding); - action.setMakePairs(makePairs); - action.setArtifactsGenerationMode(mode); - action.setOutputPattern(outputPattern); - action.setOverwrite(overwrite); - action.setSuperPkg(superPkg); - action.setSuperTemplate(superTemplate); - action.setTemplate(template); - action.setEmbeddableSuperTemplate(embeddableSuperTemplate); - action.setEmbeddableTemplate(embeddableTemplate); - action.setUsePkgPath(usePkgPath); + } + + /** + * Factory method to create internal class generator. Called from + * constructor. + */ + protected ClassGenerationAction createGenerator(DataMap dataMap) + throws UnsupportedEncodingException, MalformedURLException { + + ClassGenerationAction action; + if (client) { + action = new ClientClassGenerationAction(); + } else { + action = new ClassGenerationAction(); + } + + if (isClassGenerationDefined) { + createGenerator(action); + } else { + if (dataMap.getClassGenerationDescriptor() != null) { + createGeneratorFromMap(action, dataMap.getClassGenerationDescriptor()); + } + } + + action.setDestDir(destDir); + action.setEncoding(encoding); + action.setMakePairs(makePairs); + action.setOutputPattern(outputPattern); + action.setOverwrite(overwrite); + action.setSuperPkg(superPkg); + action.setUsePkgPath(usePkgPath); action.setCreatePropertyNames(createPropertyNames); - return action; - } + return action; + } + + protected void createGenerator(ClassGenerationAction action) { + action.setArtifactsGenerationMode(mode); + action.setSuperTemplate(superTemplate); + action.setTemplate(template); + action.setEmbeddableSuperTemplate(embeddableSuperTemplate); + action.setEmbeddableTemplate(embeddableTemplate); + } + + abstract static class GeneratorByTemplate { + + public static Map GENERATORS = new HashMap<>(); + + public static void setTemplate_(ClassGenerationAction action, ClassTemplate template, File map) + throws UnsupportedEncodingException, MalformedURLException { + + GeneratorByTemplate generator = GENERATORS.get(template.getType()); + if (generator == null) { + throw new IllegalArgumentException("Invalid template type: " + template.getType()); + } + Injector injector = DIBootstrap.createInjector(new ServerModule()); + ConfigurationNameMapper nameMapper = injector.getInstance(ConfigurationNameMapper.class); + String templateLocation = nameMapper.configurationLocation(ClassTemplate.class, template.getName()); + Resource templateResource = new URLResource(map.toURI().toURL()).getRelativeResource(templateLocation); + template.setConfigurationSource(templateResource); + generator.setTemplate(action, template); + + } + + static { + GENERATORS.put(ENTITY_SINGLE_CLASS, new GeneratorByTemplate() { + @Override + void setTemplate(ClassGenerationAction action, ClassTemplate template) throws UnsupportedEncodingException { + if (!isTemplateDefined) { + String path = URLDecoder.decode(template.getConfigurationSource().getURL().getPath(), "UTF-8"); + action.setTemplate(path); + isTemplateDefined = true; + } else { + throw new IllegalStateException("Entity template defined more than one time in datamap. " + + "Delete redundant ones or use mvn plugin"); + } + } + }); + + GENERATORS.put(ENTITY_SUPERCLASS, new GeneratorByTemplate() { + @Override + void setTemplate(ClassGenerationAction action, ClassTemplate template) throws UnsupportedEncodingException { + if (!isTemplateDefined) { + String path = URLDecoder.decode(template.getConfigurationSource().getURL().getPath(), "UTF-8"); + action.setSuperTemplate(path); + isTemplateDefined = true; + } else { + throw new IllegalStateException("Entity super template defined more than one time in datamap. " + + "Delete redundant ones or use mvn plugin"); + } + } + }); + + GENERATORS.put(ENTITY_SUBCLASS, new GeneratorByTemplate() { + @Override + void setTemplate(ClassGenerationAction action, ClassTemplate template) { + if (!isTemplateDefined) { + isTemplateDefined = true; + } else { + throw new IllegalStateException("Entity sub template defined more than one time in datamap. " + + "Delete redundant ones or use mvn plugin"); + } + } + }); + + GENERATORS.put(EMBEDDABLE_SINGLE_CLASS, new GeneratorByTemplate() { + @Override + void setTemplate(ClassGenerationAction action, ClassTemplate template) throws UnsupportedEncodingException { + if (!isTemplateDefined) { + String path = URLDecoder.decode(template.getConfigurationSource().getURL().getPath(), "UTF-8"); + action.setEmbeddableTemplate(path); + isTemplateDefined = true; + } else { + throw new IllegalStateException("Embeddable template defined more than one time in datamap. " + + "Delete redundant ones or use mvn plugin"); + } + } + }); + + GENERATORS.put(EMBEDDABLE_SUPERCLASS, new GeneratorByTemplate() { + @Override + void setTemplate(ClassGenerationAction action, ClassTemplate template) throws UnsupportedEncodingException { + if (!isTemplateDefined) { + String path = URLDecoder.decode(template.getConfigurationSource().getURL().getPath(), "UTF-8"); + action.setEmbeddableSuperTemplate(path); + isTemplateDefined = true; + } else { + throw new IllegalStateException("Embeddable super template defined more than one time in datamap. " + + "Delete redundant ones or use mvn plugin"); + } + } + }); + + GENERATORS.put(EMBEDDABLE_SUBCLASS, new GeneratorByTemplate() { + @Override + void setTemplate(ClassGenerationAction action, ClassTemplate template) { + if (!isTemplateDefined) { + isTemplateDefined = true; + } else { + throw new IllegalStateException("Embeddable sub template defined more than one time in datamap. " + + "Delete redundant ones or use mvn plugin"); + } + } + }); + + GENERATORS.put(DATAMAP_SINGLE_CLASS, new GeneratorByTemplate() { + @Override + void setTemplate(ClassGenerationAction action, ClassTemplate template) throws UnsupportedEncodingException { + if (!isTemplateDefined) { + String path = URLDecoder.decode(template.getConfigurationSource().getURL().getPath(), "UTF-8"); + action.setQueryTemplate(path); + isTemplateDefined = true; + } else { + throw new IllegalStateException("Query template defined more than one time in datamap. " + + "Delete redundant ones or use mvn plugin"); + } + } + }); + + GENERATORS.put(DATAMAP_SUPERCLASS, new GeneratorByTemplate() { + @Override + void setTemplate(ClassGenerationAction action, ClassTemplate template) throws UnsupportedEncodingException { + if (!isTemplateDefined) { + String path = URLDecoder.decode(template.getConfigurationSource().getURL().getPath(), "UTF-8"); + action.setQuerySuperTemplate(path); + isTemplateDefined = true; + } else { + throw new IllegalStateException("Query super template defined more than one time in datamap. " + + "Delete redundant ones or use mvn plugin"); + } + } + }); + + GENERATORS.put(DATAMAP_SUBCLASS, new GeneratorByTemplate() { + @Override + void setTemplate(ClassGenerationAction action, ClassTemplate template) { + if (!isTemplateDefined) { + isTemplateDefined = true; + } else { + throw new IllegalStateException("Query sub template defined more than one time in datamap. " + + "Delete redundant ones or use mvn plugin"); + } + } + }); + } + + boolean isTemplateDefined = false; + + abstract void setTemplate(ClassGenerationAction action, ClassTemplate template) + throws UnsupportedEncodingException; + + } + + protected void createGeneratorFromMap(ClassGenerationAction action, + ClassGenerationDescriptor descriptor) throws UnsupportedEncodingException, MalformedURLException { + if (descriptor.getArtifactsGenerationMode() != null) { + action.setArtifactsGenerationMode(descriptor.getArtifactsGenerationMode().getLabel()); + } else { + action.setArtifactsGenerationMode(mode); + } + for (ClassTemplate template : descriptor.getTemplates().values()) { + GeneratorByTemplate.setTemplate_(action, template, map); + } + } } diff --git a/plugins/maven-cayenne-plugin/src/main/java/org/apache/cayenne/tools/DbImporterMojo.java b/plugins/maven-cayenne-plugin/src/main/java/org/apache/cayenne/tools/DbImporterMojo.java index f3198d8134..fd79f19748 100644 --- a/plugins/maven-cayenne-plugin/src/main/java/org/apache/cayenne/tools/DbImporterMojo.java +++ b/plugins/maven-cayenne-plugin/src/main/java/org/apache/cayenne/tools/DbImporterMojo.java @@ -294,48 +294,46 @@ public void execute() throws MojoExecutionException, MojoFailureException { getLog().error(message); throw new MojoExecutionException(message, th); } - } else { - if (dataMapFile.exists()) { - try { - URL url = dataMapFile.toURI().toURL(); - URLResource resource = new URLResource(url); - - XMLDataMapLoader xmlDataMapLoader = new XMLDataMapLoader(); - DataMap dataMap = xmlDataMapLoader.load(resource); - if (dataMap.getReverseEngineering() != null) { - try { - Injector injector = DIBootstrap.createInjector(new ToolsModule(logger), new DbImportModule()); - ConfigurationNameMapper nameMapper = injector.getInstance(ConfigurationNameMapper.class); - String reverseEngineeringLocation = nameMapper.configurationLocation(ReverseEngineering.class, dataMap.getReverseEngineering().getName()); - Resource reverseEngineeringResource = new URLResource(dataMapFile.toURI().toURL()).getRelativeResource(reverseEngineeringLocation); - - DefaultReverseEngineeringLoader reverseEngineeringLoader = new DefaultReverseEngineeringLoader(); - ReverseEngineering reverseEngineering = reverseEngineeringLoader.load(reverseEngineeringResource.getURL().openStream()); - reverseEngineering.setName(dataMap.getReverseEngineering().getName()); - reverseEngineering.setConfigurationSource(reverseEngineeringResource); - dataMap.setReverseEngineering(reverseEngineering); - - FiltersConfigBuilder filtersConfigBuilder = new FiltersConfigBuilder(dataMap.getReverseEngineering()); - config.getDbLoaderConfig().setFiltersConfig(filtersConfigBuilder.filtersConfig()); - validateDbImportConfiguration(config, injector); - injector.getInstance(DbImportAction.class).execute(config); - } catch (Exception ex) { - Throwable th = Util.unwindException(ex); - - String message = "Error importing database schema"; - - if (th.getLocalizedMessage() != null) { - message += ": " + th.getLocalizedMessage(); - } - - getLog().error(message); - throw new MojoExecutionException(message, th); + } else if (dataMapFile.exists()) { + try { + URL url = dataMapFile.toURI().toURL(); + URLResource resource = new URLResource(url); + + XMLDataMapLoader xmlDataMapLoader = new XMLDataMapLoader(); + DataMap dataMap = xmlDataMapLoader.load(resource); + if (dataMap.getReverseEngineering() != null) { + try { + Injector injector = DIBootstrap.createInjector(new ToolsModule(logger), new DbImportModule()); + ConfigurationNameMapper nameMapper = injector.getInstance(ConfigurationNameMapper.class); + String reverseEngineeringLocation = nameMapper.configurationLocation(ReverseEngineering.class, dataMap.getReverseEngineering().getName()); + Resource reverseEngineeringResource = new URLResource(dataMapFile.toURI().toURL()).getRelativeResource(reverseEngineeringLocation); + + DefaultReverseEngineeringLoader reverseEngineeringLoader = new DefaultReverseEngineeringLoader(); + ReverseEngineering reverseEngineering = reverseEngineeringLoader.load(reverseEngineeringResource.getURL().openStream()); + reverseEngineering.setName(dataMap.getReverseEngineering().getName()); + reverseEngineering.setConfigurationSource(reverseEngineeringResource); + dataMap.setReverseEngineering(reverseEngineering); + + FiltersConfigBuilder filtersConfigBuilder = new FiltersConfigBuilder(dataMap.getReverseEngineering()); + config.getDbLoaderConfig().setFiltersConfig(filtersConfigBuilder.filtersConfig()); + validateDbImportConfiguration(config, injector); + injector.getInstance(DbImportAction.class).execute(config); + } catch (Exception ex) { + Throwable th = Util.unwindException(ex); + + String message = "Error importing database schema"; + + if (th.getLocalizedMessage() != null) { + message += ": " + th.getLocalizedMessage(); } + + getLog().error(message); + throw new MojoExecutionException(message, th); } - } catch (MalformedURLException e) { - getLog().error(e); - throw new MojoExecutionException(e.getMessage(), e); } + } catch (MalformedURLException e) { + getLog().error(e); + throw new MojoExecutionException(e.getMessage(), e); } } } diff --git a/plugins/maven-cayenne-plugin/src/test/java/org/apache/cayenne/tools/CayenneGeneratorMojoTest.java b/plugins/maven-cayenne-plugin/src/test/java/org/apache/cayenne/tools/CayenneGeneratorMojoTest.java index 10e843be51..1bb69c1ce3 100644 --- a/plugins/maven-cayenne-plugin/src/test/java/org/apache/cayenne/tools/CayenneGeneratorMojoTest.java +++ b/plugins/maven-cayenne-plugin/src/test/java/org/apache/cayenne/tools/CayenneGeneratorMojoTest.java @@ -75,4 +75,26 @@ public void testCgenExecution() throws Exception { assertTrue(content.contains("public void removeFromAdditionalRel(TestRelEntity obj)")); } + + public void testCgenExecutionUsingTemplateFromDataMap() throws Exception { + File pom = getTestFile("src/test/resources/cgen/project-to-test/pom1.xml"); + assertNotNull(pom); + assertTrue(pom.exists()); + + CayenneGeneratorMojo myMojo = (CayenneGeneratorMojo) lookupMojo("cgen", + pom); + assertNotNull(myMojo); + + myMojo.execute(); + + File testEntity = new File( + "target/cayenneGeneratedClasses/pack/TestEntity1.txt"); + + File embeddable = new File( + "target/cayenneGeneratedClasses/pack/Embeddable1.txt"); + + assertTrue(testEntity.exists()); + + assertTrue(embeddable.exists()); + } } diff --git a/plugins/maven-cayenne-plugin/src/test/resources/cgen/project-to-test/pom1.xml b/plugins/maven-cayenne-plugin/src/test/resources/cgen/project-to-test/pom1.xml new file mode 100644 index 0000000000..4c84e8039c --- /dev/null +++ b/plugins/maven-cayenne-plugin/src/test/resources/cgen/project-to-test/pom1.xml @@ -0,0 +1,50 @@ + + + + + + Test CayenneGeneratorMojo + + + + junit + junit + test + + + + + + + maven-cayenne-plugin + + src/test/resources/cgen/testDataMapWithTemplates.map.xml + target/cayenneGeneratedClasses + *.txt + true + superPkg + + + + + + diff --git a/plugins/maven-cayenne-plugin/src/test/resources/cgen/singleclass.vm b/plugins/maven-cayenne-plugin/src/test/resources/cgen/singleclass.vm new file mode 100644 index 0000000000..540a8ad175 --- /dev/null +++ b/plugins/maven-cayenne-plugin/src/test/resources/cgen/singleclass.vm @@ -0,0 +1,145 @@ +## Licensed to the Apache Software Foundation (ASF) under one +## or more contributor license agreements. See the NOTICE file +## distributed with this work for additional information +## regarding copyright ownership. The ASF licenses this file +## to you 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. +## +##Terminology: +## Base class - super superclass of entity, ie, org.apache.cayenne.CayenneDataObject or MyBaseClass +## Super class - superclass of entity, ie, org.apache.cayenne.art.auto._Artist +## Sub class - class of entity, ie, org.apache.cayenne.art.Artist +## +## Classes available in template +## object (duplicated as 'objEntity') - the ObjEntity class: See org.apache.cayenne.map.ObjectEntity +## stringUtils - class for string "helper" functions: See org.apache.cayenne.gen.StringUtils +## entityUtils - class for entity "helper" functions: See org.apache.cayenne.gen.EntityUtils +## importUtils - class for import statement management: See org.apache.cayenne.gen.ImportUtils +## superClassName +## superPackageName +## subClassName +## subPackageName +## baseClassName +## basePackageName +## +## +${importUtils.setPackage($subPackageName)}## +${importUtils.addReservedType("${subPackageName}.${subClassName}")}## +${importUtils.addType("${basePackageName}.${baseClassName}")}## +#if((${object.DeclaredAttributes} && !${object.DeclaredAttributes.isEmpty()}) || (${object.DeclaredRelationships} && !${object.DeclaredRelationships.isEmpty()})) +${importUtils.addType('org.apache.cayenne.exp.Property')}## +#end +#foreach( $attr in ${object.DeclaredAttributes} ) +$importUtils.addType(${attr.Type})## +#end +#foreach( $rel in ${object.DeclaredRelationships} ) +$importUtils.addType(${rel.TargetEntity.ClassName})## +#if(${rel.CollectionType}) +$importUtils.addType(${rel.CollectionType})## +#end +#end +${importUtils.generate()} + +public#if("true" == "${object.getIsAbstract()}") abstract#end class ${subClassName} extends ${baseClassName} { + + private static final long serialVersionUID = 1L; + +## Create property names +#if( $createPropertyNames ) +#foreach( $attr in ${object.DeclaredAttributes} ) + public static final String ${stringUtils.capitalizedAsConstant($attr.Name)}_PROPERTY = "${attr.Name}"; +#end +#foreach( $rel in ${object.DeclaredRelationships} ) + public static final String ${stringUtils.capitalizedAsConstant($rel.Name)}_PROPERTY = "${rel.Name}"; +#end + +#end +#if( $object.DbEntity ) +#foreach( $idAttr in ${object.DbEntity.PrimaryKeys} ) + public static final String ${stringUtils.capitalizedAsConstant($idAttr.Name)}_PK_COLUMN = "${idAttr.Name}"; +#end +#end + +## Create Properties +#foreach( $attr in ${object.DeclaredAttributes} ) + public static final Property<$importUtils.formatJavaType(${attr.Type}, false)> ${stringUtils.capitalizedAsConstant($attr.Name)} = new Property<$importUtils.formatJavaType(${attr.Type}, false)>("${attr.Name}"); +#end +#foreach( $rel in ${object.DeclaredRelationships} ) +#if( $rel.ToMany ) +#if ( ${rel.CollectionType} == "java.util.Map") + #set( $type = "$importUtils.formatJavaType($rel.CollectionType)<$importUtils.formatJavaType($entityUtils.getMapKeyType($rel)), $importUtils.formatJavaType($rel.TargetEntity.ClassName)>" ) + public static final Property<$type> ${stringUtils.capitalizedAsConstant($rel.Name)} = new Property<$type>("${rel.Name}"); +#else + #set( $type = "$importUtils.formatJavaType($rel.CollectionType)<$importUtils.formatJavaType($rel.TargetEntity.ClassName)>" ) + public static final Property<$type> ${stringUtils.capitalizedAsConstant($rel.Name)} = new Property<$type>("${rel.Name}"); +#end +#else + public static final Property<$importUtils.formatJavaType(${rel.TargetEntity.ClassName})> ${stringUtils.capitalizedAsConstant($rel.Name)} = new Property<$importUtils.formatJavaType(${rel.TargetEntity.ClassName})>("${rel.Name}"); +#end +#end + +## Create attribute set/get methods +#foreach( $attr in ${object.DeclaredAttributes} ) +#if ("true" != "${object.isReadOnly()}") + public void set${stringUtils.capitalized($attr.Name)}($importUtils.formatJavaType(${attr.Type}) $stringUtils.formatVariableName(${attr.Name})) { + writeProperty("${attr.Name}", $stringUtils.formatVariableName(${attr.Name})); + } +#end +#if ( $importUtils.isBoolean(${attr.Type}) ) + public boolean is${stringUtils.capitalized($attr.Name)}() { + Boolean value = (Boolean)readProperty("${attr.Name}"); + return (value != null) ? value.booleanValue() : false; + } +#elseif ( $importUtils.isNonBooleanPrimitive(${attr.Type}) ) + public ${importUtils.formatJavaType($attr.Type)} get${stringUtils.capitalized($attr.Name)}() { + Object value = readProperty("${attr.Name}"); + return (value != null) ? ($importUtils.formatJavaTypeAsNonBooleanPrimitive(${attr.Type})) value : 0; + } +#else + public $importUtils.formatJavaType(${attr.Type}) get${stringUtils.capitalized($attr.Name)}() { + return ($importUtils.formatJavaType(${attr.Type}))readProperty("${attr.Name}"); + } +#end + +#end +## +## Create list add/remove/get methods +#foreach( $rel in ${object.DeclaredRelationships} ) +#if( $rel.ToMany ) +#if ( ! $rel.ReadOnly ) public void addTo${stringUtils.capitalized($rel.Name)}($importUtils.formatJavaType(${rel.TargetEntity.ClassName}) obj) { + addToManyTarget("${rel.name}", obj, true); + } + public void removeFrom${stringUtils.capitalized($rel.Name)}($importUtils.formatJavaType(${rel.TargetEntity.ClassName}) obj) { + removeToManyTarget("${rel.name}", obj, true); + } +#end + public $importUtils.formatJavaType($rel.CollectionType) get${stringUtils.capitalized($rel.Name)}() { + return ($importUtils.formatJavaType($rel.CollectionType))readProperty("${rel.name}"); + } +#else +#if ( ! ${object.isReadOnly()} && ! $rel.ReadOnly ) + public void set${stringUtils.capitalized($rel.Name)}($importUtils.formatJavaType(${rel.TargetEntity.ClassName}) $stringUtils.formatVariableName(${rel.name})) { + setToOneTarget("${rel.name}", $stringUtils.formatVariableName(${rel.name}), true); + } +#end + public $importUtils.formatJavaType(${rel.TargetEntity.ClassName}) get${stringUtils.capitalized($rel.Name)}() { + return ($importUtils.formatJavaType(${rel.TargetEntity.ClassName}))readProperty("${rel.name}"); + } +#end + + +#end +} + + + diff --git a/plugins/maven-cayenne-plugin/src/test/resources/cgen/testDataMapWithTemplates.map.xml b/plugins/maven-cayenne-plugin/src/test/resources/cgen/testDataMapWithTemplates.map.xml new file mode 100644 index 0000000000..7f780c434e --- /dev/null +++ b/plugins/maven-cayenne-plugin/src/test/resources/cgen/testDataMapWithTemplates.map.xml @@ -0,0 +1,34 @@ + + + + +