| /***************************************************************** |
| * 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 |
| * |
| * https://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.editor.cgen; |
| |
| import javax.swing.JOptionPane; |
| import java.io.IOException; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.concurrent.ConcurrentMap; |
| import java.util.function.Predicate; |
| import java.util.prefs.Preferences; |
| import java.util.stream.Collectors; |
| |
| import org.apache.cayenne.configuration.event.DataMapEvent; |
| import org.apache.cayenne.configuration.event.DataMapListener; |
| import org.apache.cayenne.configuration.xml.DataChannelMetaData; |
| import org.apache.cayenne.dbsync.reverse.configuration.ToolsModule; |
| import org.apache.cayenne.di.DIBootstrap; |
| import org.apache.cayenne.di.Module; |
| import org.apache.cayenne.di.spi.ModuleLoader; |
| import org.apache.cayenne.gen.CgenConfiguration; |
| import org.apache.cayenne.gen.ClassGenerationAction; |
| import org.apache.cayenne.gen.ClassGenerationActionFactory; |
| import org.apache.cayenne.gen.ClientClassGenerationAction; |
| import org.apache.cayenne.map.DataMap; |
| import org.apache.cayenne.map.Embeddable; |
| import org.apache.cayenne.map.Entity; |
| import org.apache.cayenne.map.ObjEntity; |
| import org.apache.cayenne.map.event.EmbeddableEvent; |
| import org.apache.cayenne.map.event.EmbeddableListener; |
| import org.apache.cayenne.map.event.EntityEvent; |
| import org.apache.cayenne.map.event.ObjEntityListener; |
| import org.apache.cayenne.modeler.ProjectController; |
| import org.apache.cayenne.modeler.dialog.ErrorDebugDialog; |
| import org.apache.cayenne.modeler.dialog.pref.GeneralPreferences; |
| import org.apache.cayenne.modeler.editor.DbImportController; |
| import org.apache.cayenne.modeler.util.CayenneController; |
| import org.apache.cayenne.modeler.util.ModelerUtil; |
| import org.apache.cayenne.swing.BindingBuilder; |
| import org.apache.cayenne.tools.CayenneToolsModuleProvider; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * @since 4.1 |
| * A controller for the class generator dialog. |
| */ |
| public class CodeGeneratorController extends CayenneController implements ObjEntityListener, EmbeddableListener, DataMapListener { |
| private static final Logger LOGGER = LoggerFactory.getLogger(ErrorDebugDialog.class); |
| |
| public static final String SELECTED_PROPERTY = "selected"; |
| |
| protected ProjectController projectController; |
| |
| protected List<Object> classes; |
| protected SelectionModel selectionModel; |
| protected Object currentClass; |
| |
| protected boolean initFromModel; |
| |
| protected CodeGeneratorPane view; |
| |
| protected ClassesTabController classesSelector; |
| protected GeneratorTabController generatorSelector; |
| private ConcurrentMap<DataMap, GeneratorController> prevGeneratorController; |
| |
| public CodeGeneratorController(ProjectController projectController) { |
| super(projectController); |
| this.classesSelector = new ClassesTabController(this); |
| this.generatorSelector = new GeneratorTabController(this); |
| this.view = new CodeGeneratorPane(generatorSelector.getView(), classesSelector.getView()); |
| this.prevGeneratorController = new ConcurrentHashMap<>(); |
| this.projectController = projectController; |
| this.classes = new ArrayList<>(); |
| this.selectionModel = new SelectionModel(); |
| initBindings(); |
| initListeners(); |
| } |
| |
| public void initFromModel() { |
| initFromModel = true; |
| prepareClasses(projectController.getCurrentDataMap()); |
| DataMap dataMap = projectController.getCurrentDataMap(); |
| classesSelectedAction(); |
| CgenConfiguration cgenConfiguration = createConfiguration(); |
| GeneratorController modeController = prevGeneratorController.get(dataMap) != null |
| ? prevGeneratorController.get(dataMap) |
| : isDefaultConfig(cgenConfiguration) |
| ? cgenConfiguration.isClient() |
| ? generatorSelector.getClientGeneratorController() |
| : generatorSelector.getStandartController() |
| : generatorSelector.getCustomModeController(); |
| |
| prevGeneratorController.put(dataMap, modeController); |
| generatorSelector.setSelectedController(modeController); |
| classesSelector.startup(); |
| initFromModel = false; |
| classesSelector.validate(classes); |
| } |
| |
| private boolean isDefaultConfig(CgenConfiguration cgenConfiguration) { |
| return cgenConfiguration.isMakePairs() && cgenConfiguration.isUsePkgPath() && |
| !cgenConfiguration.isOverwrite() && !cgenConfiguration.isCreatePKProperties() && |
| !cgenConfiguration.isCreatePropertyNames() && cgenConfiguration.getOutputPattern().equals("*.java") && |
| (cgenConfiguration.getTemplate().equals(ClassGenerationAction.SUBCLASS_TEMPLATE) || |
| cgenConfiguration.getTemplate().equals(ClientClassGenerationAction.SUBCLASS_TEMPLATE)) && |
| (cgenConfiguration.getSuperTemplate().equals(ClassGenerationAction.SUPERCLASS_TEMPLATE) || |
| cgenConfiguration.getSuperTemplate().equals(ClientClassGenerationAction.SUPERCLASS_TEMPLATE)) && |
| (cgenConfiguration.getSuperPkg() == null || cgenConfiguration.getSuperPkg().isEmpty()); |
| |
| } |
| |
| private void initListeners(){ |
| projectController.addObjEntityListener(this); |
| projectController.addEmbeddableListener(this); |
| projectController.addDataMapListener(this); |
| } |
| |
| @Override |
| public CodeGeneratorPane getView() { |
| return view; |
| } |
| |
| protected void initBindings() { |
| BindingBuilder builder = new BindingBuilder(getApplication().getBindingFactory(), this); |
| |
| builder.bindToAction(view.getGenerateButton(), "generateAction()"); |
| builder.bindToAction(this, "classesSelectedAction()", SELECTED_PROPERTY); |
| builder.bindToAction(generatorSelector, "generatorSelectedAction()", |
| GeneratorTabController.GENERATOR_PROPERTY); |
| |
| generatorSelectedAction(); |
| } |
| |
| public void generatorSelectedAction() { |
| GeneratorController controller = generatorSelector.getGeneratorController(); |
| classesSelector.validate(classes); |
| |
| Predicate<Object> defaultPredicate = object -> { |
| if (object instanceof ObjEntity) { |
| return classesSelector.getProblem(((ObjEntity) object).getName()) == null; |
| } |
| |
| if (object instanceof Embeddable) { |
| return classesSelector.getProblem(((Embeddable) object).getClassName()) == null; |
| } |
| return false; |
| }; |
| |
| Predicate<Object> predicate = controller != null |
| ? defaultPredicate |
| : o -> false; |
| |
| updateSelection(predicate); |
| classesSelector.classSelectedAction(); |
| } |
| |
| public void classesSelectedAction() { |
| if(!isInitFromModel()) { |
| getProjectController().setDirty(true); |
| } |
| } |
| |
| public void generateAction() { |
| CgenConfiguration cgenConfiguration = createConfiguration(); |
| List<Module> modules = new ModuleLoader() |
| .load(CayenneToolsModuleProvider.class); |
| modules.add(binder -> binder.bind(DataChannelMetaData.class) |
| .toInstance(projectController.getApplication().getMetaData())); |
| ClassGenerationAction generator = DIBootstrap |
| .createInjector(modules) |
| .getInstance(ClassGenerationActionFactory.class) |
| .createAction(cgenConfiguration); |
| |
| try { |
| generator.prepareArtifacts(); |
| generator.execute(); |
| JOptionPane.showMessageDialog( |
| this.getView(), |
| "Class generation finished"); |
| } catch (Exception e) { |
| LOGGER.error("Error generating classes", e); |
| JOptionPane.showMessageDialog( |
| this.getView(), |
| "Error generating classes - " + e.getMessage()); |
| } |
| } |
| |
| public ConcurrentMap<DataMap, GeneratorController> getPrevGeneratorController() { |
| return prevGeneratorController; |
| } |
| |
| public void enableGenerateButton(boolean enable) { |
| view.getGenerateButton().setEnabled(enable); |
| } |
| |
| private void prepareClasses(DataMap dataMap) { |
| classes.clear(); |
| classes.add(dataMap); |
| classes.addAll(dataMap.getObjEntities()); |
| classes.addAll(dataMap.getEmbeddables()); |
| selectionModel.initCollectionsForSelection(dataMap); |
| } |
| |
| /** |
| * Creates a class generator for provided selections. |
| */ |
| public CgenConfiguration createConfiguration() { |
| DataMap map = projectController.getCurrentDataMap(); |
| CgenConfiguration cgenConfiguration = projectController.getApplication().getMetaData().get(map, CgenConfiguration.class); |
| if(cgenConfiguration != null){ |
| addToSelectedEntities(cgenConfiguration.getEntities()); |
| addToSelectedEmbeddables(cgenConfiguration.getEmbeddables()); |
| cgenConfiguration.setForce(true); |
| return cgenConfiguration; |
| } |
| |
| cgenConfiguration = new CgenConfiguration(false); |
| cgenConfiguration.setForce(true); |
| cgenConfiguration.setDataMap(map); |
| |
| Path basePath = Paths.get(ModelerUtil.initOutputFolder()); |
| |
| // TODO: this should be done in actual generation, not here |
| // no such folder |
| if (!Files.exists(basePath)) { |
| try { |
| Files.createDirectories(basePath); |
| } catch (IOException e) { |
| JOptionPane.showMessageDialog(getView(), "Can't create directory. Select a different one."); |
| return null; |
| } |
| } |
| // not a directory |
| if (!Files.isDirectory(basePath)) { |
| JOptionPane.showMessageDialog(this.getView(), basePath + " is not a valid directory."); |
| return null; |
| } |
| |
| cgenConfiguration.setRootPath(basePath); |
| Preferences preferences = application.getPreferencesNode(GeneralPreferences.class, ""); |
| if (preferences != null) { |
| cgenConfiguration.setEncoding(preferences.get(GeneralPreferences.ENCODING_PREFERENCE, null)); |
| } |
| |
| addToSelectedEntities(map.getObjEntities() |
| .stream() |
| .map(Entity::getName) |
| .collect(Collectors.toList())); |
| addToSelectedEmbeddables(map.getEmbeddables() |
| .stream() |
| .map(Embeddable::getClassName) |
| .collect(Collectors.toList())); |
| getApplication().getMetaData().add(map, cgenConfiguration); |
| projectController.setDirty(true); |
| |
| return cgenConfiguration; |
| } |
| |
| public List<Object> getClasses() { |
| return classes; |
| } |
| |
| public boolean updateSelection(Predicate<Object> predicate) { |
| boolean modified = selectionModel.updateSelection(predicate, classes); |
| |
| for (Object classObj : classes) { |
| if(classObj instanceof DataMap) { |
| boolean select = predicate.test(classObj); |
| updateArtifactGenerationMode(classObj, select); |
| } |
| } |
| |
| if (modified) { |
| firePropertyChange(SELECTED_PROPERTY, null, null); |
| } |
| |
| return modified; |
| } |
| |
| private void updateArtifactGenerationMode(Object classObj, boolean selected) { |
| DataMap dataMap = (DataMap) classObj; |
| CgenConfiguration cgenConfiguration = projectController.getApplication().getMetaData().get(dataMap, CgenConfiguration.class); |
| if(selected) { |
| cgenConfiguration.setArtifactsGenerationMode("all"); |
| } else { |
| cgenConfiguration.setArtifactsGenerationMode("entity"); |
| } |
| } |
| |
| public boolean isSelected() { |
| return selectionModel.isSelected(currentClass); |
| } |
| |
| public void setSelected(boolean selectedFlag) { |
| if (currentClass instanceof DataMap) { |
| updateArtifactGenerationMode(currentClass, selectedFlag); |
| } |
| if (selectionModel.setSelected(currentClass, selectedFlag)) { |
| firePropertyChange(SELECTED_PROPERTY, null, null); |
| } |
| } |
| |
| public Object getCurrentClass() { |
| return currentClass; |
| } |
| |
| public void setCurrentClass(Object currentClass) { |
| this.currentClass = currentClass; |
| } |
| |
| public void updateSelectedEntities(){ |
| updateEntities(); |
| updateEmbeddables(); |
| } |
| |
| CgenConfiguration getCurrentConfiguration() { |
| DataMap map = getProjectController().getCurrentDataMap(); |
| return projectController.getApplication().getMetaData().get(map, CgenConfiguration.class); |
| } |
| |
| private void updateEntities() { |
| CgenConfiguration cgenConfiguration = getCurrentConfiguration(); |
| if(cgenConfiguration != null) { |
| cgenConfiguration.getEntities().clear(); |
| for(ObjEntity entity: selectionModel.getSelectedEntities(classes)) { |
| cgenConfiguration.loadEntity(entity); |
| } |
| } |
| } |
| |
| private void updateEmbeddables() { |
| CgenConfiguration cgenConfiguration = getCurrentConfiguration(); |
| if(cgenConfiguration != null) { |
| cgenConfiguration.getEmbeddables().clear(); |
| for(Embeddable embeddable : selectionModel.getSelectedEmbeddables(classes)) { |
| cgenConfiguration.loadEmbeddable(embeddable.getClassName()); |
| } |
| } |
| } |
| |
| private void addToSelectedEntities(Collection<String> entities) { |
| selectionModel.addSelectedEntities(entities); |
| updateEntities(); |
| } |
| |
| void addEntity(DataMap dataMap, ObjEntity objEntity) { |
| prepareClasses(dataMap); |
| selectionModel.addSelectedEntity(objEntity.getName()); |
| CgenConfiguration cgenConfiguration = getCurrentConfiguration(); |
| if(cgenConfiguration != null) { |
| cgenConfiguration.loadEntity(objEntity); |
| } |
| } |
| |
| private void addToSelectedEmbeddables(Collection<String> embeddables) { |
| selectionModel.addSelectedEmbeddables(embeddables); |
| updateEmbeddables(); |
| } |
| |
| |
| public int getSelectedEntitiesSize() { |
| return selectionModel.getSelectedEntitiesCount(); |
| } |
| |
| public int getSelectedEmbeddablesSize() { |
| return selectionModel.getSelecetedEmbeddablesCount(); |
| } |
| |
| public boolean isDataMapSelected() { |
| return selectionModel.getSelectedDataMapsCount() > 0; |
| } |
| |
| public ProjectController getProjectController() { |
| return projectController; |
| } |
| |
| public boolean isInitFromModel() { |
| return initFromModel; |
| } |
| |
| public void setInitFromModel(boolean initFromModel) { |
| this.initFromModel = initFromModel; |
| } |
| |
| @Override |
| public void objEntityChanged(EntityEvent e) {} |
| |
| @Override |
| public void objEntityAdded(EntityEvent e) { |
| addEntity(e.getEntity().getDataMap(), (ObjEntity) e.getEntity()); |
| } |
| |
| @Override |
| public void objEntityRemoved(EntityEvent e) { |
| selectionModel.removeFromSelectedEntities((ObjEntity) e.getEntity()); |
| DataMap map = e.getEntity().getDataMap(); |
| CgenConfiguration cgenConfiguration = projectController.getApplication().getMetaData().get(map, CgenConfiguration.class); |
| if(cgenConfiguration != null) { |
| cgenConfiguration.getEntities().remove(e.getEntity().getName()); |
| } |
| } |
| |
| @Override |
| public void embeddableChanged(EmbeddableEvent e, DataMap map) {} |
| |
| @Override |
| public void embeddableAdded(EmbeddableEvent e, DataMap map) { |
| prepareClasses(map); |
| String embeddableClassName = e.getEmbeddable().getClassName(); |
| selectionModel.addSelectedEmbeddable(embeddableClassName); |
| CgenConfiguration cgenConfiguration = getCurrentConfiguration(); |
| if(cgenConfiguration != null) { |
| cgenConfiguration.loadEmbeddable(embeddableClassName); |
| } |
| } |
| |
| @Override |
| public void embeddableRemoved(EmbeddableEvent e, DataMap map) { |
| selectionModel.removeFromSelectedEmbeddables(e.getEmbeddable()); |
| CgenConfiguration cgenConfiguration = projectController.getApplication().getMetaData().get(map, CgenConfiguration.class); |
| if(cgenConfiguration != null) { |
| cgenConfiguration.getEmbeddables().remove(e.getEmbeddable().getClassName()); |
| } |
| } |
| |
| @Override |
| public void dataMapChanged(DataMapEvent e) { |
| if(e.getSource() instanceof DbImportController) { |
| CgenConfiguration cgenConfiguration = getCurrentConfiguration(); |
| if(cgenConfiguration != null) { |
| for(ObjEntity objEntity : e.getDataMap().getObjEntities()) { |
| if(!cgenConfiguration.getExcludeEntityArtifacts().contains(objEntity.getName())) { |
| addEntity(cgenConfiguration.getDataMap(), objEntity); |
| } |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void dataMapAdded(DataMapEvent e) {} |
| |
| @Override |
| public void dataMapRemoved(DataMapEvent e) {} |
| } |