You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by sk...@apache.org on 2016/12/19 11:46:18 UTC
[02/10] cayenne git commit: CAY-2172 Split DB actions to different
packages Clean up ReverseEngineeringAction Clean up DataSource select dialogs
http://git-wip-us.apache.org/repos/asf/cayenne/blob/7da0e897/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/gen/TableSelectorController.java
----------------------------------------------------------------------
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/gen/TableSelectorController.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/gen/TableSelectorController.java
new file mode 100644
index 0000000..9304e52
--- /dev/null
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/gen/TableSelectorController.java
@@ -0,0 +1,251 @@
+/*****************************************************************
+ * 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.gen;
+
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.map.DbAttribute;
+import org.apache.cayenne.map.DbEntity;
+import org.apache.cayenne.map.DbRelationship;
+import org.apache.cayenne.modeler.ProjectController;
+import org.apache.cayenne.modeler.util.CayenneController;
+import org.apache.cayenne.project.Project;
+import org.apache.cayenne.project.validation.ProjectValidator;
+import org.apache.cayenne.swing.BindingBuilder;
+import org.apache.cayenne.swing.ObjectBinding;
+import org.apache.cayenne.swing.TableBindingBuilder;
+import org.apache.cayenne.validation.ValidationFailure;
+import org.apache.cayenne.validation.ValidationResult;
+
+import java.awt.Component;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ */
+public class TableSelectorController extends CayenneController {
+
+ protected TableSelectorView view;
+ protected ObjectBinding tableBinding;
+
+ protected DbEntity table;
+ protected List<DbEntity> tables;
+ protected int permanentlyExcludedCount;
+ protected Map<String, DbEntity> excludedTables;
+ protected List<DbEntity> selectableTablesList;
+
+ protected Map<String, String> validationMessages;
+
+ public TableSelectorController(ProjectController parent) {
+ super(parent);
+ this.view = new TableSelectorView();
+ this.excludedTables = new HashMap<>();
+ this.selectableTablesList = new ArrayList<>();
+ this.validationMessages = new HashMap<>();
+ initController();
+ }
+
+ public Component getView() {
+ return view;
+ }
+
+ /**
+ * Called by table binding script to set current table.
+ */
+ public void setTable(DbEntity table) {
+ this.table = table;
+ }
+
+ /**
+ * Returns DbEntities that are excluded from DB generation.
+ */
+ public Collection<DbEntity> getExcludedTables() {
+ return excludedTables.values();
+ }
+
+ public List<DbEntity> getTables() {
+ return tables;
+ }
+
+ public boolean isIncluded() {
+ if (table == null) {
+ return false;
+ }
+
+ return !excludedTables.containsKey(table.getName());
+ }
+
+ public void setIncluded(boolean b) {
+ if (table == null) {
+ return;
+ }
+
+ if (b) {
+ excludedTables.remove(table.getName());
+ }
+ else {
+ excludedTables.put(table.getName(), table);
+ }
+
+ tableSelectedAction();
+ }
+
+ /**
+ * A callback action that updates the state of Select All checkbox.
+ */
+ public void tableSelectedAction() {
+ int unselectedCount = excludedTables.size() - permanentlyExcludedCount;
+
+ if (unselectedCount == selectableTablesList.size()) {
+ view.getCheckAll().setSelected(false);
+ }
+ else if (unselectedCount == 0) {
+ view.getCheckAll().setSelected(true);
+ }
+ }
+
+ public Object getProblem() {
+ return (table != null) ? validationMessages.get(table.getName()) : null;
+ }
+
+ // ------ other stuff ------
+
+ protected void initController() {
+
+ BindingBuilder builder = new BindingBuilder(
+ getApplication().getBindingFactory(),
+ this);
+
+ builder.bindToAction(view.getCheckAll(), "checkAllAction()");
+
+ TableBindingBuilder tableBuilder = new TableBindingBuilder(builder);
+
+ tableBuilder.addColumn(
+ "",
+ "setTable(#item), included",
+ Boolean.class,
+ true,
+ Boolean.TRUE);
+ tableBuilder.addColumn(
+ "Table",
+ "#item.name",
+ String.class,
+ false,
+ "XXXXXXXXXXXXXXXX");
+ tableBuilder.addColumn(
+ "Problems",
+ "setTable(#item), problem",
+ String.class,
+ false,
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
+
+ this.tableBinding = tableBuilder.bindToTable(view.getTables(), "tables");
+ }
+
+ /**
+ * Performs validation of DbEntities in the current DataMap. Returns a collection of
+ * ValidationInfo objects describing the problems.
+ */
+ public void updateTables(Collection<DataMap> dataMaps) {
+ this.tables = new ArrayList<DbEntity>();
+
+ for (DataMap dataMap : dataMaps) {
+ this.tables.addAll(dataMap.getDbEntities());
+ }
+
+ excludedTables.clear();
+ validationMessages.clear();
+
+ // if there were errors, filter out those related to
+ // non-derived DbEntities...
+
+ // TODO: this is inefficient.. we need targeted validation
+ // instead of doing it on the whole project
+
+ Project project = getApplication().getProject();
+
+ ProjectValidator projectValidator = getApplication().getInjector().getInstance(
+ ProjectValidator.class);
+ ValidationResult validationResult = projectValidator.validate(project
+ .getRootNode());
+
+ if (validationResult.getFailures().size() > 0) {
+
+ for (ValidationFailure nextProblem : validationResult.getFailures()) {
+ DbEntity failedEntity = null;
+
+ if (nextProblem.getSource() instanceof DbAttribute) {
+ DbAttribute failedAttribute = (DbAttribute) nextProblem.getSource();
+ failedEntity = (DbEntity) failedAttribute.getEntity();
+ }
+ else if (nextProblem.getSource() instanceof DbRelationship) {
+ DbRelationship failedRelationship = (DbRelationship) nextProblem
+ .getSource();
+ failedEntity = (DbEntity) failedRelationship.getSourceEntity();
+ }
+ else if (nextProblem.getSource() instanceof DbEntity) {
+ failedEntity = (DbEntity) nextProblem.getSource();
+ }
+
+ if (failedEntity == null) {
+ continue;
+ }
+
+ excludedTables.put(failedEntity.getName(), failedEntity);
+ validationMessages.put(failedEntity.getName(), nextProblem
+ .getDescription());
+ }
+ }
+
+ // Find selectable tables
+ permanentlyExcludedCount = excludedTables.size();
+ selectableTablesList.clear();
+ for (DbEntity table : tables) {
+ if (false == excludedTables.containsKey(table.getName())) {
+ selectableTablesList.add(table);
+ }
+ }
+
+ tableBinding.updateView();
+ tableSelectedAction();
+ }
+
+ public void checkAllAction() {
+
+ boolean isCheckAllSelected = view.getCheckAll().isSelected();
+
+ if (isCheckAllSelected) {
+ selectableTablesList.clear();
+ selectableTablesList.addAll(tables);
+ excludedTables.clear();
+ }
+ else {
+ excludedTables.clear();
+ for (DbEntity table : tables) {
+ excludedTables.put(table.getName(), table);
+ }
+ selectableTablesList.clear();
+ }
+
+ tableBinding.updateView();
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/7da0e897/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/gen/TableSelectorView.java
----------------------------------------------------------------------
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/gen/TableSelectorView.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/gen/TableSelectorView.java
new file mode 100644
index 0000000..f618e65
--- /dev/null
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/gen/TableSelectorView.java
@@ -0,0 +1,94 @@
+/*****************************************************************
+ * 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.gen;
+
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+
+import javax.swing.JCheckBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+
+import com.jgoodies.forms.builder.PanelBuilder;
+import com.jgoodies.forms.layout.CellConstraints;
+import com.jgoodies.forms.layout.FormLayout;
+
+/**
+ */
+public class TableSelectorView extends JPanel {
+
+ protected JTable tables;
+ protected JCheckBox checkAll;
+ protected JLabel checkAllLabel;
+
+ public TableSelectorView() {
+
+ this.checkAll = new JCheckBox();
+ this.checkAllLabel = new JLabel("Check All Tables");
+
+ checkAll.addItemListener(new ItemListener() {
+
+ public void itemStateChanged(ItemEvent event) {
+ if (checkAll.isSelected()) {
+ checkAllLabel.setText("Uncheck All Tables");
+ }
+ else {
+ checkAllLabel.setText("Check All Tables");
+ }
+ }
+ });
+
+ // assemble
+ JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.LEADING));
+ topPanel.add(checkAll);
+ topPanel.add(checkAllLabel);
+
+ tables = new JTable();
+ tables.setRowHeight(25);
+ tables.setRowMargin(3);
+
+ CellConstraints cc = new CellConstraints();
+ PanelBuilder builder = new PanelBuilder(new FormLayout(
+ "fill:min(50dlu;pref):grow",
+ "p, 3dlu, fill:40dlu:grow"));
+ builder.setDefaultDialogBorder();
+ builder.addSeparator("Select Tables", cc.xy(1, 1));
+ builder.add(new JScrollPane(
+ tables,
+ JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
+ JScrollPane.HORIZONTAL_SCROLLBAR_NEVER), cc.xy(1, 3));
+
+ setLayout(new BorderLayout());
+ add(topPanel, BorderLayout.NORTH);
+ add(builder.getPanel(), BorderLayout.CENTER);
+ }
+
+ public JTable getTables() {
+ return tables;
+ }
+
+ public JCheckBox getCheckAll() {
+ return checkAll;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/7da0e897/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/DbImportProjectSaver.java
----------------------------------------------------------------------
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/DbImportProjectSaver.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/DbImportProjectSaver.java
new file mode 100644
index 0000000..2a58363
--- /dev/null
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/DbImportProjectSaver.java
@@ -0,0 +1,74 @@
+/*****************************************************************
+ * 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.load;
+
+import org.apache.cayenne.configuration.ConfigurationNameMapper;
+import org.apache.cayenne.configuration.DataChannelDescriptor;
+import org.apache.cayenne.configuration.event.DataMapEvent;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.map.event.MapEvent;
+import org.apache.cayenne.modeler.Application;
+import org.apache.cayenne.modeler.ProjectController;
+import org.apache.cayenne.project.Project;
+import org.apache.cayenne.project.ProjectSaver;
+import org.apache.cayenne.resource.Resource;
+
+public class DbImportProjectSaver implements ProjectSaver {
+
+ private ConfigurationNameMapper nameMapper;
+
+ private ProjectController projectController;
+
+ public DbImportProjectSaver(@Inject ProjectController projectController, @Inject ConfigurationNameMapper nameMapper) {
+ this.projectController = projectController;
+ this.nameMapper = nameMapper;
+ }
+
+ @Override
+ public String getSupportedVersion() {
+ // not important in the context of non-saving saver
+ return "-1";
+ }
+
+ @Override
+ public void save(Project project) {
+
+ DataMap dataMap = (DataMap) project.getRootNode();
+
+ if (projectController.getCurrentDataMap() != null) {
+ projectController.fireDataMapEvent(new DataMapEvent(Application.getFrame(), dataMap, MapEvent.REMOVE));
+ projectController.fireDataMapEvent(new DataMapEvent(Application.getFrame(), dataMap, MapEvent.ADD));
+ } else {
+ DataChannelDescriptor currentDomain = (DataChannelDescriptor) projectController.getProject().getRootNode();
+ Resource baseResource = currentDomain.getConfigurationSource();
+ // a new DataMap, so need to set configuration source for it
+ if (baseResource != null) {
+ Resource dataMapResource = baseResource.getRelativeResource(nameMapper.configurationLocation(dataMap));
+ dataMap.setConfigurationSource(dataMapResource);
+ }
+ projectController.addDataMap(Application.getFrame(), dataMap);
+ }
+ }
+
+ @Override
+ public void saveAs(Project project, Resource baseDirectory) {
+ save(project);
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/7da0e897/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/DbLoaderContext.java
----------------------------------------------------------------------
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/DbLoaderContext.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/DbLoaderContext.java
new file mode 100644
index 0000000..6a550a6
--- /dev/null
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/DbLoaderContext.java
@@ -0,0 +1,176 @@
+/*****************************************************************
+ * 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.load;
+
+import java.io.File;
+import java.sql.Connection;
+import javax.swing.JOptionPane;
+import javax.swing.SwingUtilities;
+
+import org.apache.cayenne.configuration.ConfigurationNode;
+import org.apache.cayenne.dbimport.Catalog;
+import org.apache.cayenne.dbimport.ExcludeTable;
+import org.apache.cayenne.dbimport.IncludeProcedure;
+import org.apache.cayenne.dbimport.IncludeTable;
+import org.apache.cayenne.dbimport.ReverseEngineering;
+import org.apache.cayenne.dbimport.Schema;
+import org.apache.cayenne.dbsync.naming.NameBuilder;
+import org.apache.cayenne.dbsync.reverse.db.DbLoaderDelegate;
+import org.apache.cayenne.dbsync.reverse.filters.FiltersConfigBuilder;
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.modeler.Application;
+import org.apache.cayenne.modeler.ProjectController;
+import org.apache.cayenne.modeler.dialog.db.DataSourceWizard;
+import org.apache.cayenne.modeler.pref.DBConnectionInfo;
+import org.apache.cayenne.tools.dbimport.DbImportConfiguration;
+import org.apache.cayenne.util.Util;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+public class DbLoaderContext {
+
+ private static Log LOGGER = LogFactory.getLog(DbLoaderContext.class);
+
+ private DbImportConfiguration config;
+ private Connection connection;
+ private ProjectController projectController;
+ private boolean existingMap;
+ private DataMap dataMap;
+ private boolean stopping;
+ private String loadStatusNote;
+
+ public DbLoaderContext() {
+ }
+
+ DataMap getDataMap() {
+ return dataMap;
+ }
+
+ boolean isExistingDataMap() {
+ return existingMap;
+ }
+
+ public void setProjectController(ProjectController projectController) {
+ this.projectController = projectController;
+ }
+
+ ProjectController getProjectController() {
+ return projectController;
+ }
+
+ void setConfig(DbImportConfiguration config) {
+ this.config = config;
+ }
+
+ DbImportConfiguration getConfig() {
+ return config;
+ }
+
+ public void setConnection(Connection connection) {
+ this.connection = connection;
+ }
+
+ public Connection getConnection() {
+ return connection;
+ }
+
+ public boolean isStopping() {
+ return stopping;
+ }
+
+ void setStopping(boolean stopping) {
+ this.stopping = stopping;
+ }
+
+ String getStatusNote() {
+ return loadStatusNote;
+ }
+
+ void setStatusNote(String loadStatusNote) {
+ this.loadStatusNote = loadStatusNote;
+ }
+
+ public boolean buildConfig(DataSourceWizard connectionWizard, DbLoaderOptionsDialog dialog) {
+ if (dialog == null || connectionWizard == null) {
+ return false;
+ }
+
+ // Build filters
+ ReverseEngineering reverseEngineering = new ReverseEngineering();
+ reverseEngineering.addCatalog(new Catalog(dialog.getSelectedCatalog()));
+ reverseEngineering.addSchema(new Schema(dialog.getSelectedSchema()));
+ reverseEngineering.addIncludeTable(new IncludeTable(dialog.getTableNamePattern()));
+ reverseEngineering.addExcludeTable(new ExcludeTable("auto_pk_support|AUTO_PK_SUPPORT"));
+ reverseEngineering.addIncludeProcedure(new IncludeProcedure(dialog.getProcedureNamePattern()));
+ FiltersConfigBuilder filtersConfigBuilder = new FiltersConfigBuilder(reverseEngineering);
+
+ DbImportConfiguration config = new DbImportConfiguration() {
+ @Override
+ public DbLoaderDelegate createLoaderDelegate() {
+ return new LoaderDelegate(DbLoaderContext.this);
+ }
+ };
+
+ // Build config
+ DBConnectionInfo connectionInfo = connectionWizard.getConnectionInfo();
+ config.setAdapter(connectionWizard.getAdapter().getClass().getName());
+ config.setUsername(connectionInfo.getUserName());
+ config.setPassword(connectionInfo.getPassword());
+ config.setDriver(connectionInfo.getJdbcDriver());
+ config.setUrl(connectionInfo.getUrl());
+ config.getDbLoaderConfig().setFiltersConfig(filtersConfigBuilder.build());
+ config.setMeaningfulPkTables(dialog.getMeaningfulPk());
+ config.setNamingStrategy(dialog.getNamingStrategy());
+ setConfig(config);
+
+ prepareDataMap();
+
+ return true;
+ }
+
+ private void prepareDataMap() {
+ dataMap = getProjectController().getCurrentDataMap();
+ existingMap = dataMap != null;
+
+ if (!existingMap) {
+ ConfigurationNode root = getProjectController().getProject().getRootNode();
+ dataMap = new DataMap();
+ dataMap.setName(NameBuilder.builder(dataMap, root).name());
+ }
+ if (dataMap.getConfigurationSource() != null) {
+ getConfig().setTargetDataMap(new File(dataMap.getConfigurationSource().getURL().getPath()));
+ }
+ }
+
+ public void processWarn(final Throwable th, final String message) {
+ LOGGER.warn(message, Util.unwindException(th));
+ }
+
+ public void processException(final Throwable th, final String message) {
+ LOGGER.info("Exception on reverse engineering", Util.unwindException(th));
+ SwingUtilities.invokeLater(new Runnable() {
+
+ public void run() {
+ JOptionPane.showMessageDialog(Application.getFrame(), th.getMessage(), message,
+ JOptionPane.ERROR_MESSAGE);
+ }
+ });
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/7da0e897/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/DbLoaderOptionsDialog.java
----------------------------------------------------------------------
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/DbLoaderOptionsDialog.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/DbLoaderOptionsDialog.java
new file mode 100644
index 0000000..1ccc7aa
--- /dev/null
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/DbLoaderOptionsDialog.java
@@ -0,0 +1,247 @@
+/*****************************************************************
+ * 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.load;
+
+import com.jgoodies.forms.builder.DefaultFormBuilder;
+import com.jgoodies.forms.layout.FormLayout;
+import org.apache.cayenne.modeler.Application;
+import org.apache.cayenne.modeler.util.CayenneDialog;
+import org.apache.cayenne.modeler.util.NameGeneratorPreferences;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.Collection;
+import java.util.Vector;
+
+/**
+ * Dialog for selecting database reverse-engineering parameters.
+ */
+public class DbLoaderOptionsDialog extends CayenneDialog {
+
+ public static final String WILDCARD_PATTERN = ".*";
+
+ public static final int CANCEL = 0;
+ public static final int SELECT = 1;
+
+ protected JLabel catalogLabel;
+ protected JComboBox<String> catalogSelector;
+ protected JLabel schemaLabel;
+ protected JComboBox<String> schemaSelector;
+ protected JTextField tableNamePatternField;
+ protected JTextField meaningfulPk;
+ protected JTextField procNamePatternField;
+ protected JLabel procedureLabel;
+ protected JButton selectButton;
+ protected JButton cancelButton;
+
+
+ protected JComboBox<String> strategyCombo;
+ protected String strategy;
+ protected int choice;
+
+ /**
+ * Creates and initializes new ChooseSchemaDialog.
+ */
+ public DbLoaderOptionsDialog(Collection<String> schemas, Collection<String> catalogs, String currentSchema,
+ String dbCatalog) {
+ super(Application.getFrame(), "Reengineer DB Schema: Select Options");
+
+ init();
+ initController();
+ initFromModel(schemas, catalogs, currentSchema, dbCatalog);
+
+ pack();
+ setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+ setModal(true);
+ centerWindow();
+ }
+
+ /** Sets up the graphical components. */
+ protected void init() {
+
+ // create widgets...
+ selectButton = new JButton("Continue");
+ cancelButton = new JButton("Cancel");
+ catalogSelector = new JComboBox<>();
+ schemaSelector = new JComboBox<>();
+ tableNamePatternField = new JTextField();
+ tableNamePatternField.setToolTipText("<html>Regular expression to filter table names.<br>" +
+ "Default expression <b>.*</b> includes all tables.</html>");
+ procNamePatternField = new JTextField();
+ procNamePatternField.setToolTipText("<html>Regular expression to filter stored procedures names.<br>" +
+ "Default expression .* includes all stored procedures.</html>");
+ meaningfulPk = new JTextField();
+ meaningfulPk.setToolTipText("<html>Regular expression to filter tables with meaningful primary keys.<br>" +
+ "Multiple expressions divided by comma can be used.<br>" +
+ "Example: <b>^table1,^table2,^prefix.*</b></html>");
+ strategyCombo = new JComboBox<>();
+ strategyCombo.setEditable(true);
+
+ // assemble
+ FormLayout layout = new FormLayout(
+ "right:pref, 3dlu, fill:max(170dlu;pref):grow",
+ "");
+ DefaultFormBuilder builder = new DefaultFormBuilder(layout);
+ builder.setDefaultDialogBorder();
+
+ catalogLabel = builder.append("Select Catalog:", catalogSelector);
+ schemaLabel = builder.append("Select Schema:", schemaSelector);
+ builder.append("Table Name Pattern:", tableNamePatternField);
+ procedureLabel = builder.append("Procedure Name Pattern:", procNamePatternField);
+ builder.append("Naming Strategy:", strategyCombo);
+ builder.append("Tables with Meaningful PK Pattern:", meaningfulPk);
+
+ JPanel buttons = new JPanel(new FlowLayout(FlowLayout.RIGHT));
+ buttons.add(cancelButton);
+ buttons.add(selectButton);
+
+ getContentPane().setLayout(new BorderLayout());
+ getContentPane().add(builder.getPanel(), BorderLayout.CENTER);
+ getContentPane().add(buttons, BorderLayout.SOUTH);
+ }
+
+ protected void initController() {
+ selectButton.addActionListener(new ActionListener() {
+
+ public void actionPerformed(ActionEvent e) {
+ processSelect();
+ }
+ });
+ cancelButton.addActionListener(new ActionListener() {
+
+ public void actionPerformed(ActionEvent e) {
+ processCancel();
+ }
+ });
+ }
+
+ protected void initFromModel(
+ Collection<String> schemas,
+ Collection<String> catalogs,
+ String currentSchema,
+ String currentCatalog) {
+
+ this.choice = CANCEL;
+ this.tableNamePatternField.setText(WILDCARD_PATTERN);
+ this.procNamePatternField.setText(WILDCARD_PATTERN);
+
+ Vector<String> arr = NameGeneratorPreferences
+ .getInstance()
+ .getLastUsedStrategies();
+ strategyCombo.setModel(new DefaultComboBoxModel<>(arr));
+
+ boolean showSchemaSelector = schemas != null && !schemas.isEmpty();
+ schemaSelector.setVisible(showSchemaSelector);
+ schemaLabel.setVisible(showSchemaSelector);
+
+ if (showSchemaSelector) {
+
+ schemaSelector.setModel(new DefaultComboBoxModel<>(schemas.toArray(new String[0])));
+
+ if (currentSchema != null) {
+ for (String schema : schemas) {
+ if (currentSchema.equalsIgnoreCase(schema)) {
+ schemaSelector.setSelectedItem(schema);
+ break;
+ }
+ }
+ }
+ }
+
+ boolean showCatalogSelector = catalogs != null && !catalogs.isEmpty();
+ catalogSelector.setVisible(showCatalogSelector);
+ catalogLabel.setVisible(showCatalogSelector);
+
+ if (showCatalogSelector) {
+ catalogSelector.setModel(new DefaultComboBoxModel<>(catalogs.toArray(new String[0])));
+
+ if (currentCatalog != null && !currentCatalog.isEmpty()) {
+ for (String catalog : catalogs) {
+ if (currentCatalog.equalsIgnoreCase(catalog)) {
+ catalogSelector.setSelectedItem(catalog);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ public int getChoice() {
+ return choice;
+ }
+
+ private void processSelect() {
+ strategy = (String) strategyCombo.getSelectedItem();
+
+ choice = SELECT;
+ setVisible(false);
+ }
+
+ private void processCancel() {
+ choice = CANCEL;
+ setVisible(false);
+ }
+
+ /**
+ * Returns selected catalog.
+ */
+ public String getSelectedCatalog() {
+ String catalog = (String) catalogSelector.getSelectedItem();
+ return "".equals(catalog) ? null : catalog;
+ }
+
+ /**
+ * Returns selected schema.
+ */
+ public String getSelectedSchema() {
+ String schema = (String) schemaSelector.getSelectedItem();
+ return "".equals(schema) ? null : schema;
+ }
+
+ /**
+ * Returns the tableNamePattern.
+ */
+ public String getTableNamePattern() {
+ return "".equals(tableNamePatternField.getText()) ? null : tableNamePatternField
+ .getText();
+ }
+
+ public String getMeaningfulPk() {
+ return "".equals(meaningfulPk.getText()) ? null : meaningfulPk
+ .getText();
+ }
+
+ /**
+ * Returns the procedure name pattern.
+ */
+ public String getProcedureNamePattern() {
+ return "".equals(procNamePatternField.getText()) ? null : procNamePatternField
+ .getText();
+ }
+
+ /**
+ * Returns configured naming strategy
+ */
+ public String getNamingStrategy() {
+ return strategy;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/7da0e897/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/LoadDataMapTask.java
----------------------------------------------------------------------
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/LoadDataMapTask.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/LoadDataMapTask.java
new file mode 100644
index 0000000..ceb0d44
--- /dev/null
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/LoadDataMapTask.java
@@ -0,0 +1,94 @@
+/*****************************************************************
+ * 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.load;
+
+import javax.swing.JFrame;
+
+import org.apache.cayenne.dbsync.DbSyncModule;
+import org.apache.cayenne.di.DIBootstrap;
+import org.apache.cayenne.di.Injector;
+import org.apache.cayenne.modeler.util.LongRunningTask;
+import org.apache.cayenne.modeler.util.ProjectUtil;
+import org.apache.cayenne.tools.configuration.ToolsModule;
+import org.apache.cayenne.tools.dbimport.DbImportAction;
+import org.apache.cayenne.tools.dbimport.DbImportModule;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+final public class LoadDataMapTask extends LongRunningTask {
+
+ private static Log LOGGER = LogFactory.getLog(DbLoaderContext.class);
+
+ private DbLoaderContext context;
+
+ public LoadDataMapTask(JFrame frame, String title, DbLoaderContext context) {
+ super(frame, title);
+ setMinValue(0);
+ setMaxValue(10);
+ this.context = context;
+ }
+
+ @Override
+ protected void execute() {
+ context.setStatusNote("Preparing...");
+ try {
+ createAction().execute(context.getConfig());
+ } catch (Exception e) {
+ context.processException(e, "Error importing database schema.");
+ }
+ ProjectUtil.cleanObjMappings(context.getDataMap());
+ }
+
+ private DbImportAction createAction() {
+ Injector injector = DIBootstrap.createInjector(new DbSyncModule(),
+ new ToolsModule(LOGGER),
+ new DbImportModule(),
+ new ModelerSyncModule(context));
+ return injector.getInstance(DbImportAction.class);
+ }
+
+ @Override
+ protected String getCurrentNote() {
+ return context.getStatusNote();
+ }
+
+ @Override
+ protected int getCurrentValue() {
+ return getMinValue();
+ }
+
+ @Override
+ protected boolean isIndeterminate() {
+ return true;
+ }
+
+ @Override
+ public boolean isCanceled() {
+ return context.isStopping();
+ }
+
+ @Override
+ public void setCanceled(boolean canceled) {
+ if (canceled) {
+ context.setStatusNote("Canceling..");
+ }
+ context.setStopping(canceled);
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/7da0e897/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/LoaderDelegate.java
----------------------------------------------------------------------
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/LoaderDelegate.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/LoaderDelegate.java
new file mode 100644
index 0000000..92043fc
--- /dev/null
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/LoaderDelegate.java
@@ -0,0 +1,71 @@
+/*****************************************************************
+ * 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.load;
+
+import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.dbsync.reverse.db.DefaultDbLoaderDelegate;
+import org.apache.cayenne.map.DbEntity;
+import org.apache.cayenne.map.DbRelationship;
+import org.apache.cayenne.map.event.EntityEvent;
+import org.apache.cayenne.map.event.MapEvent;
+import org.apache.cayenne.modeler.Application;
+
+final class LoaderDelegate extends DefaultDbLoaderDelegate {
+
+ private DbLoaderContext context;
+
+ LoaderDelegate(DbLoaderContext dbLoaderContext) {
+ this.context = dbLoaderContext;
+ }
+
+ @Override
+ public void dbEntityAdded(DbEntity entity) {
+ checkCanceled();
+ context.setStatusNote("Importing table '" + entity.getName() + "'...");
+ }
+
+ @Override
+ public void dbEntityRemoved(DbEntity entity) {
+ checkCanceled();
+ if (context.isExistingDataMap()) {
+ context.getProjectController().fireDbEntityEvent(new EntityEvent(Application.getFrame(), entity, MapEvent.REMOVE));
+ }
+ }
+
+ @Override
+ public boolean dbRelationship(DbEntity entity) {
+ checkCanceled();
+ context.setStatusNote("Load relationships for '" + entity.getName() + "'...");
+ return true;
+ }
+
+ @Override
+ public boolean dbRelationshipLoaded(DbEntity entity, DbRelationship relationship) {
+ checkCanceled();
+ context.setStatusNote("Load relationship: '" + entity.getName() + "'; '" + relationship.getName() + "'...");
+ return true;
+ }
+
+ private void checkCanceled() {
+ if (context.isStopping()) {
+ throw new CayenneRuntimeException("Reengineering was canceled.");
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/7da0e897/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/ModelerDbImportAction.java
----------------------------------------------------------------------
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/ModelerDbImportAction.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/ModelerDbImportAction.java
new file mode 100644
index 0000000..34ad1b5
--- /dev/null
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/ModelerDbImportAction.java
@@ -0,0 +1,53 @@
+/*****************************************************************
+ * 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.load;
+
+import org.apache.cayenne.configuration.server.DataSourceFactory;
+import org.apache.cayenne.configuration.server.DbAdapterFactory;
+import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactoryProvider;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.map.MapLoader;
+import org.apache.cayenne.project.ProjectSaver;
+import org.apache.cayenne.tools.dbimport.DbImportConfiguration;
+import org.apache.cayenne.tools.dbimport.DefaultDbImportAction;
+import org.apache.commons.logging.Log;
+
+import java.io.IOException;
+
+public class ModelerDbImportAction extends DefaultDbImportAction {
+
+ @Inject
+ private DataMap targetMap;
+
+ public ModelerDbImportAction(@Inject Log logger,
+ @Inject ProjectSaver projectSaver,
+ @Inject DataSourceFactory dataSourceFactory,
+ @Inject DbAdapterFactory adapterFactory,
+ @Inject MapLoader mapLoader,
+ @Inject MergerTokenFactoryProvider mergerTokenFactoryProvider
+ ) {
+ super(logger, projectSaver, dataSourceFactory, adapterFactory, mapLoader, mergerTokenFactoryProvider);
+ }
+
+ @Override
+ protected DataMap existingTargetMap(DbImportConfiguration configuration) throws IOException {
+ return targetMap;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/cayenne/blob/7da0e897/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/ModelerSyncModule.java
----------------------------------------------------------------------
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/ModelerSyncModule.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/ModelerSyncModule.java
new file mode 100644
index 0000000..24fdfcf
--- /dev/null
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/ModelerSyncModule.java
@@ -0,0 +1,44 @@
+/*****************************************************************
+ * 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.load;
+
+import org.apache.cayenne.di.Binder;
+import org.apache.cayenne.di.Module;
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.modeler.ProjectController;
+import org.apache.cayenne.project.ProjectSaver;
+import org.apache.cayenne.tools.dbimport.DbImportAction;
+
+class ModelerSyncModule implements Module {
+
+ private DbLoaderContext dbLoaderContext;
+
+ ModelerSyncModule(DbLoaderContext dbLoaderHelper) {
+ this.dbLoaderContext = dbLoaderHelper;
+ }
+
+ @Override
+ public void configure(Binder binder) {
+ binder.bind(ProjectController.class).toInstance(dbLoaderContext.getProjectController());
+ binder.bind(ProjectSaver.class).to(DbImportProjectSaver.class);
+ binder.bind(DbImportAction.class).to(ModelerDbImportAction.class);
+ binder.bind(DataMap.class).toInstance(dbLoaderContext.getDataMap());
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/7da0e897/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/merge/DbMigrateOptionsDialog.java
----------------------------------------------------------------------
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/merge/DbMigrateOptionsDialog.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/merge/DbMigrateOptionsDialog.java
new file mode 100644
index 0000000..b3fb929
--- /dev/null
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/merge/DbMigrateOptionsDialog.java
@@ -0,0 +1,176 @@
+/*****************************************************************
+ * 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.merge;
+
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.Collection;
+
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import org.apache.cayenne.modeler.Application;
+import org.apache.cayenne.modeler.util.CayenneDialog;
+
+import com.jgoodies.forms.builder.DefaultFormBuilder;
+import com.jgoodies.forms.layout.FormLayout;
+
+public class DbMigrateOptionsDialog extends CayenneDialog {
+ private static final long serialVersionUID = 1L;
+ public static final int CANCEL = 0;
+ public static final int SELECT = 1;
+
+ protected JLabel schemaLabel;
+ protected JLabel catalogLabel;
+ protected JComboBox<String> catalogSelector;
+ protected JComboBox<String> schemaSelector;
+ protected JButton selectButton;
+ protected JButton cancelButton;
+ protected int choice;
+
+ public DbMigrateOptionsDialog(Collection<String> catalogs, Collection<String> schemas, String dbUserName) {
+ super(Application.getFrame(), "Migrate DB Schema: Select Catalog and Schema");
+ init();
+ initController();
+ initFromModel(catalogs, schemas, dbUserName);
+
+ pack();
+ setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+ setModal(true);
+ centerWindow();
+ }
+
+ protected void init() {
+ selectButton = new JButton("Continue");
+ cancelButton = new JButton("Cancel");
+ catalogSelector = new JComboBox<>();
+ schemaSelector = new JComboBox<>();
+ FormLayout layout = new FormLayout(
+ "right:pref, 3dlu, fill:max(170dlu;pref):grow",
+ "");
+ DefaultFormBuilder builder = new DefaultFormBuilder(layout);
+ builder.setDefaultDialogBorder();
+
+ catalogLabel = builder.append("Select Catalog:", catalogSelector, true);
+ schemaLabel = builder.append("Select Schema:", schemaSelector);
+
+ JPanel buttons = new JPanel(new FlowLayout(FlowLayout.RIGHT));
+ buttons.add(selectButton);
+ buttons.add(cancelButton);
+
+ getContentPane().setLayout(new BorderLayout());
+ getContentPane().add(builder.getPanel(), BorderLayout.CENTER);
+ getContentPane().add(buttons, BorderLayout.SOUTH);
+ }
+
+ protected void initController() {
+ selectButton.addActionListener(new ActionListener() {
+
+ public void actionPerformed(ActionEvent e) {
+ processSelect();
+ }
+ });
+
+ cancelButton.addActionListener(new ActionListener() {
+
+ public void actionPerformed(ActionEvent e) {
+ processCancel();
+ }
+ });
+ }
+
+ private void processSelect() {
+ choice = SELECT;
+ setVisible(false);
+ }
+
+ private void processCancel() {
+ schemaSelector.setSelectedItem(null);
+ choice = CANCEL;
+ setVisible(false);
+ }
+
+ protected void initFromModel(Collection<String> catalogs, Collection<String> schemas, String dbUserName) {
+
+ this.choice = CANCEL;
+
+ if(!schemas.isEmpty()) {
+ schemaLabel.setVisible(true);
+ schemaSelector.setModel(new DefaultComboBoxModel<>(schemas.toArray(new String[0])));
+ schemaSelector.setVisible(true);
+ } else {
+ schemaLabel.setVisible(false);
+ schemaSelector.setVisible(false);
+ }
+
+ if(!catalogs.isEmpty()) {
+ catalogLabel.setVisible(true);
+ catalogSelector.setModel(new DefaultComboBoxModel<>(catalogs.toArray(new String[0])));
+ catalogSelector.setVisible(true);
+ } else {
+ catalogLabel.setVisible(false);
+ catalogSelector.setVisible(false);
+ }
+
+ if (dbUserName == null) {
+ return;
+ }
+
+ // select schema belonging to the user
+ for (String schema : schemas) {
+ if (dbUserName.equalsIgnoreCase(schema)) {
+ schemaSelector.setSelectedItem(schema);
+ break;
+ }
+ }
+
+ for(String catalog : catalogs) {
+ if(dbUserName.equalsIgnoreCase(catalog)) {
+ catalogSelector.setSelectedItem(catalog);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Returns selected schema.
+ */
+ public String getSelectedSchema() {
+ return (String) schemaSelector.getSelectedItem();
+ }
+
+ public String getSelectedCatalog() {
+ return (String) catalogSelector.getSelectedItem();
+ }
+
+ public int getChoice() {
+ return choice;
+ }
+
+ public void showDialog() {
+ setVisible(true);
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/7da0e897/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/merge/MergerOptions.java
----------------------------------------------------------------------
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/merge/MergerOptions.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/merge/MergerOptions.java
new file mode 100644
index 0000000..a914cf1
--- /dev/null
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/merge/MergerOptions.java
@@ -0,0 +1,395 @@
+/*****************************************************************
+ * 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.merge;
+
+import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.configuration.event.DataMapEvent;
+import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.dbsync.merge.AbstractToDbToken;
+import org.apache.cayenne.dbsync.merge.DbMerger;
+import org.apache.cayenne.dbsync.merge.DefaultModelMergeDelegate;
+import org.apache.cayenne.dbsync.merge.MergeDirection;
+import org.apache.cayenne.dbsync.merge.MergerContext;
+import org.apache.cayenne.dbsync.merge.MergerToken;
+import org.apache.cayenne.dbsync.merge.ModelMergeDelegate;
+import org.apache.cayenne.dbsync.merge.ProxyModelMergeDelegate;
+import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
+import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactoryProvider;
+import org.apache.cayenne.dbsync.naming.DefaultObjectNameGenerator;
+import org.apache.cayenne.dbsync.naming.NoStemStemmer;
+import org.apache.cayenne.dbsync.reverse.db.DbLoader;
+import org.apache.cayenne.dbsync.reverse.db.DbLoaderConfiguration;
+import org.apache.cayenne.dbsync.reverse.db.LoggingDbLoaderDelegate;
+import org.apache.cayenne.dbsync.reverse.filters.FiltersConfig;
+import org.apache.cayenne.dbsync.reverse.filters.PatternFilter;
+import org.apache.cayenne.dbsync.reverse.filters.TableFilter;
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.map.ObjEntity;
+import org.apache.cayenne.map.event.MapEvent;
+import org.apache.cayenne.modeler.Application;
+import org.apache.cayenne.modeler.ProjectController;
+import org.apache.cayenne.modeler.dialog.ValidationResultBrowser;
+import org.apache.cayenne.modeler.pref.DBConnectionInfo;
+import org.apache.cayenne.modeler.util.CayenneController;
+import org.apache.cayenne.project.Project;
+import org.apache.cayenne.resource.Resource;
+import org.apache.cayenne.swing.BindingBuilder;
+import org.apache.cayenne.swing.ObjectBinding;
+import org.apache.cayenne.tools.dbimport.DefaultDbImportAction;
+import org.apache.cayenne.validation.ValidationResult;
+import org.apache.commons.logging.LogFactory;
+
+import javax.sql.DataSource;
+import javax.swing.JFileChooser;
+import javax.swing.JOptionPane;
+import javax.swing.WindowConstants;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import java.awt.Component;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+public class MergerOptions extends CayenneController {
+
+ protected MergerOptionsView view;
+ protected ObjectBinding sqlBinding;
+
+ protected DBConnectionInfo connectionInfo;
+ protected DataMap dataMap;
+ protected DbAdapter adapter;
+ protected String textForSQL;
+
+ protected MergerTokenSelectorController tokens;
+ protected String defaultCatalog;
+ protected String defaultSchema;
+ private MergerTokenFactoryProvider mergerTokenFactoryProvider;
+
+ public MergerOptions(ProjectController parent,
+ String title,
+ DBConnectionInfo connectionInfo,
+ DataMap dataMap,
+ String defaultCatalog,
+ String defaultSchema,
+ MergerTokenFactoryProvider mergerTokenFactoryProvider) {
+ super(parent);
+
+ this.mergerTokenFactoryProvider = mergerTokenFactoryProvider;
+ this.dataMap = dataMap;
+ this.tokens = new MergerTokenSelectorController(parent);
+ this.view = new MergerOptionsView(tokens.getView());
+ this.connectionInfo = connectionInfo;
+ this.defaultCatalog = defaultCatalog;
+ this.defaultSchema = defaultSchema;
+ this.view.setTitle(title);
+ initController();
+
+ prepareMigrator();
+ createSQL();
+ refreshView();
+ }
+
+ public Component getView() {
+ return view;
+ }
+
+ public String getTextForSQL() {
+ return textForSQL;
+ }
+
+ protected void initController() {
+
+ BindingBuilder builder = new BindingBuilder(
+ getApplication().getBindingFactory(),
+ this);
+
+ sqlBinding = builder.bindToTextArea(view.getSql(), "textForSQL");
+
+ builder.bindToAction(view.getGenerateButton(), "generateSchemaAction()");
+ builder.bindToAction(view.getSaveSqlButton(), "storeSQLAction()");
+ builder.bindToAction(view.getCancelButton(), "closeAction()");
+
+ // refresh SQL if different tables were selected
+ view.getTabs().addChangeListener(new ChangeListener() {
+
+ public void stateChanged(ChangeEvent e) {
+ if (view.getTabs().getSelectedIndex() == 1) {
+ // this assumes that some tables where checked/unchecked... not very
+ // efficient
+ refreshGeneratorAction();
+ }
+ }
+ });
+ }
+
+ /**
+ * check database and create the {@link List} of {@link MergerToken}s
+ */
+ protected void prepareMigrator() {
+ try {
+ adapter = connectionInfo.makeAdapter(getApplication().getClassLoadingService());
+
+ MergerTokenFactory mergerTokenFactory = mergerTokenFactoryProvider.get(adapter);
+ tokens.setMergerTokenFactory(mergerTokenFactory);
+
+
+ FiltersConfig filters = FiltersConfig.create(defaultCatalog, defaultSchema, TableFilter.everything(),
+ PatternFilter.INCLUDE_NOTHING);
+
+ DbMerger merger = DbMerger.builder(mergerTokenFactory)
+ .filters(filters)
+ .build();
+
+ DbLoaderConfiguration config = new DbLoaderConfiguration();
+ config.setFiltersConfig(filters);
+
+ DataSource dataSource = connectionInfo.makeDataSource(getApplication().getClassLoadingService());
+
+ DataMap dbImport = new DataMap();
+ try (Connection conn = dataSource.getConnection();) {
+ new DbLoader(conn,
+ adapter,
+ new LoggingDbLoaderDelegate(LogFactory.getLog(DbLoader.class)),
+ new DefaultObjectNameGenerator(NoStemStemmer.getInstance()))
+ .load(dbImport, config);
+
+ } catch (SQLException e) {
+ throw new CayenneRuntimeException("Can't doLoad dataMap from db.", e);
+ }
+
+ tokens.setTokens(merger.createMergeTokens(dataMap, dbImport));
+ } catch (Exception ex) {
+ reportError("Error loading adapter", ex);
+ }
+ }
+
+ /**
+ * Returns SQL statements generated for selected schema generation options.
+ */
+ protected void createSQL() {
+ // convert them to string representation for display
+ StringBuilder buf = new StringBuilder();
+
+ Iterator<MergerToken> it = tokens.getSelectedTokens().iterator();
+ String batchTerminator = adapter.getBatchTerminator();
+
+ String lineEnd = batchTerminator != null ? "\n" + batchTerminator + "\n\n" : "\n\n";
+ while (it.hasNext()) {
+ MergerToken token = it.next();
+
+ if (token instanceof AbstractToDbToken) {
+ AbstractToDbToken tdb = (AbstractToDbToken) token;
+ for (String sql : tdb.createSql(adapter)) {
+ buf.append(sql);
+ buf.append(lineEnd);
+ }
+ }
+ }
+
+ textForSQL = buf.toString();
+ }
+
+ protected void refreshView() {
+ sqlBinding.updateView();
+ }
+
+ // ===============
+ // Actions
+ // ===============
+
+ /**
+ * Starts options dialog.
+ */
+ public void startupAction() {
+ view.pack();
+ view.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+ view.setModal(true);
+ makeCloseableOnEscape();
+ centerView();
+ view.setVisible(true);
+ }
+
+ public void refreshGeneratorAction() {
+ refreshSQLAction();
+ }
+
+ /**
+ * Updates a text area showing generated SQL.
+ */
+ public void refreshSQLAction() {
+ createSQL();
+ sqlBinding.updateView();
+ }
+
+ /**
+ * Performs configured schema operations via DbGenerator.
+ */
+ public void generateSchemaAction() {
+ refreshGeneratorAction();
+
+ // sanity check...
+ List<MergerToken> tokensToMigrate = tokens.getSelectedTokens();
+ if (tokensToMigrate.isEmpty()) {
+ JOptionPane.showMessageDialog(getView(), "Nothing to migrate.");
+ return;
+ }
+
+ DataSource dataSource;
+ try {
+ dataSource = connectionInfo.makeDataSource(getApplication()
+ .getClassLoadingService());
+ } catch (SQLException ex) {
+ reportError("Migration Error", ex);
+ return;
+ }
+
+ final Collection<ObjEntity> loadedObjEntities = new LinkedList<>();
+
+ MergerContext mergerContext = MergerContext.builder(dataMap)
+ .syntheticDataNode(dataSource, adapter)
+ .delegate(createDelegate(loadedObjEntities))
+ .build();
+
+ boolean modelChanged = applyTokens(tokensToMigrate, mergerContext);
+
+ DefaultDbImportAction.flattenManyToManyRelationships(
+ dataMap,
+ loadedObjEntities,
+ mergerContext.getNameGenerator());
+
+ notifyProjectModified(modelChanged);
+
+ reportFailures(mergerContext);
+ }
+
+ private ModelMergeDelegate createDelegate(final Collection<ObjEntity> loadedObjEntities) {
+ return new ProxyModelMergeDelegate(new DefaultModelMergeDelegate()) {
+ @Override
+ public void objEntityAdded(ObjEntity ent) {
+ loadedObjEntities.add(ent);
+ super.objEntityAdded(ent);
+ }
+ };
+ }
+
+ private boolean applyTokens(List<MergerToken> tokensToMigrate, MergerContext mergerContext) {
+ boolean modelChanged = false;
+
+ try {
+ for (MergerToken tok : tokensToMigrate) {
+ int numOfFailuresBefore = getFailuresCount(mergerContext);
+
+ tok.execute(mergerContext);
+
+ if (!modelChanged && tok.getDirection().equals(MergeDirection.TO_MODEL)) {
+ modelChanged = true;
+ }
+ if (numOfFailuresBefore == getFailuresCount(mergerContext)) {
+ // looks like the token executed without failures
+ tokens.removeToken(tok);
+ }
+ }
+ } catch (Throwable th) {
+ reportError("Migration Error", th);
+ }
+
+ return modelChanged;
+ }
+
+ private int getFailuresCount(MergerContext mergerContext) {
+ return mergerContext.getValidationResult().getFailures().size();
+ }
+
+ private void reportFailures(MergerContext mergerContext) {
+ ValidationResult failures = mergerContext.getValidationResult();
+ if (failures == null || !failures.hasFailures()) {
+ JOptionPane.showMessageDialog(getView(), "Migration Complete.");
+ } else {
+ new ValidationResultBrowser(this).startupAction(
+ "Migration Complete",
+ "Migration finished. The following problem(s) were ignored.",
+ failures);
+ }
+ }
+
+ private void notifyProjectModified(boolean modelChanged) {
+ if(!modelChanged) {
+ return;
+ }
+
+ // mark the model as unsaved
+ Project project = getApplication().getProject();
+ project.setModified(true);
+
+ ProjectController projectController = getProjectController();
+ projectController.setDirty(true);
+
+ projectController.fireDataMapEvent(new DataMapEvent(Application.getFrame(),
+ dataMap, MapEvent.REMOVE));
+ projectController.fireDataMapEvent(new DataMapEvent(Application.getFrame(),
+ dataMap, MapEvent.ADD));
+ }
+
+ /**
+ * Allows user to save generated SQL in a file.
+ */
+ public void storeSQLAction() {
+ JFileChooser fc = new JFileChooser();
+ fc.setDialogType(JFileChooser.SAVE_DIALOG);
+ fc.setDialogTitle("Save SQL Script");
+
+ Resource projectDir = getApplication().getProject().getConfigurationResource();
+
+ if (projectDir != null) {
+ fc.setCurrentDirectory(new File(projectDir.getURL().getPath()));
+ }
+
+ if (fc.showSaveDialog(getView()) == JFileChooser.APPROVE_OPTION) {
+ refreshGeneratorAction();
+
+ try {
+ File file = fc.getSelectedFile();
+ FileWriter fw = new FileWriter(file);
+ PrintWriter pw = new PrintWriter(fw);
+ pw.print(textForSQL);
+ pw.flush();
+ pw.close();
+ } catch (IOException ex) {
+ reportError("Error Saving SQL", ex);
+ }
+ }
+ }
+
+ private ProjectController getProjectController() {
+ return getApplication().getFrameController().getProjectController();
+ }
+
+ public void closeAction() {
+ view.dispose();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/7da0e897/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/merge/MergerOptionsView.java
----------------------------------------------------------------------
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/merge/MergerOptionsView.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/merge/MergerOptionsView.java
new file mode 100644
index 0000000..ba68857
--- /dev/null
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/merge/MergerOptionsView.java
@@ -0,0 +1,134 @@
+/*****************************************************************
+ * 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.merge;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+
+import javax.swing.Box;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTabbedPane;
+import javax.swing.JTextArea;
+import javax.swing.ScrollPaneConstants;
+import javax.swing.SwingConstants;
+
+import org.apache.cayenne.modeler.Application;
+
+import com.jgoodies.forms.builder.PanelBuilder;
+import com.jgoodies.forms.layout.CellConstraints;
+import com.jgoodies.forms.layout.FormLayout;
+
+/**
+ * Wizard for altering the database to match the data map.
+ */
+public class MergerOptionsView extends JDialog {
+
+ protected JTextArea sql;
+ protected JButton generateButton;
+ protected JButton cancelButton;
+ protected JButton saveSqlButton;
+
+ protected Component tables;
+ protected JTabbedPane tabs;
+
+ public MergerOptionsView(Component tables) {
+ super(Application.getFrame());
+
+ // create widgets
+ this.generateButton = new JButton("Migrate");
+ this.cancelButton = new JButton("Close");
+ this.saveSqlButton = new JButton("Save SQL");
+
+ this.tables = tables;
+ this.tabs = new JTabbedPane(SwingConstants.TOP);
+ this.sql = new JTextArea();
+ sql.setEditable(false);
+ sql.setLineWrap(true);
+ sql.setWrapStyleWord(true);
+
+ JPanel sqlTextPanel = new JPanel(new BorderLayout());
+ sqlTextPanel.add(new JScrollPane(
+ sql,
+ ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
+ ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED), BorderLayout.CENTER);
+
+ CellConstraints cc = new CellConstraints();
+ PanelBuilder builder = new PanelBuilder(new FormLayout(
+ "fill:min(50dlu;pref):grow",
+ "p, 9dlu, p, 3dlu, fill:40dlu:grow"));
+ builder.setDefaultDialogBorder();
+ builder.addSeparator("Generated SQL", cc.xywh(1, 3, 1, 1));
+ builder.add(sqlTextPanel, cc.xy(1, 5));
+
+ tabs.addTab("Operations", new JScrollPane(
+ tables,
+ ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
+ ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED));
+ tabs.addTab("Generated SQL", builder.getPanel());
+
+ // we need the right preferred size so that dialog "pack()" produces decent
+ // default size...
+ tabs.setPreferredSize(new Dimension(600, 350));
+
+ JPanel buttons = new JPanel(new FlowLayout(FlowLayout.RIGHT));
+ buttons.add(saveSqlButton);
+ buttons.add(Box.createHorizontalStrut(20));
+ buttons.add(cancelButton);
+ buttons.add(generateButton);
+
+ Container contentPane = this.getContentPane();
+ contentPane.setLayout(new BorderLayout());
+ contentPane.add(tabs, BorderLayout.CENTER);
+ contentPane.add(buttons, BorderLayout.SOUTH);
+ }
+
+ public JButton getCancelButton() {
+ return cancelButton;
+ }
+
+ public JTabbedPane getTabs() {
+ return tabs;
+ }
+
+ /*
+ * public JCheckBox getCreateFK() { return createFK; } public JCheckBox getCreatePK() {
+ * return createPK; } public JCheckBox getCreateTables() { return createTables; }
+ * public JCheckBox getDropPK() { return dropPK; } public JCheckBox getDropTables() {
+ * return dropTables; }
+ */
+
+ public JButton getGenerateButton() {
+ return generateButton;
+ }
+
+ public JButton getSaveSqlButton() {
+ return saveSqlButton;
+ }
+
+ public JTextArea getSql() {
+ return sql;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/7da0e897/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/merge/MergerTokenSelectorController.java
----------------------------------------------------------------------
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/merge/MergerTokenSelectorController.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/merge/MergerTokenSelectorController.java
new file mode 100644
index 0000000..52d6ce3
--- /dev/null
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/merge/MergerTokenSelectorController.java
@@ -0,0 +1,240 @@
+/*****************************************************************
+ * 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.merge;
+
+import org.apache.cayenne.dbsync.merge.MergeDirection;
+import org.apache.cayenne.dbsync.merge.MergerToken;
+import org.apache.cayenne.dbsync.merge.TokenComparator;
+import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
+import org.apache.cayenne.modeler.Application;
+import org.apache.cayenne.modeler.util.CayenneController;
+import org.apache.cayenne.swing.BindingBuilder;
+import org.apache.cayenne.swing.ObjectBinding;
+
+import javax.swing.*;
+import javax.swing.table.AbstractTableModel;
+import javax.swing.table.TableColumn;
+import javax.swing.table.TableColumnModel;
+import javax.swing.table.TableModel;
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class MergerTokenSelectorController extends CayenneController {
+
+ protected MergerTokenSelectorView view;
+ protected ObjectBinding tableBinding;
+
+ protected MergerToken token;
+ protected int permanentlyExcludedCount;
+ protected Set<MergerToken> excludedTokens;
+ protected List<MergerToken> selectableTokensList;
+ protected MergerTokenFactory mergerTokenFactory;
+
+ public MergerTokenSelectorController(CayenneController parent) {
+ super(parent);
+ this.view = new MergerTokenSelectorView();
+ this.excludedTokens = new HashSet<MergerToken>();
+ this.selectableTokensList = new ArrayList<MergerToken>();
+ initController();
+ }
+
+ public void setMergerTokenFactory(MergerTokenFactory mergerTokenFactory) {
+ this.mergerTokenFactory = mergerTokenFactory;
+ }
+
+ public void setTokens(List<MergerToken> tokens) {
+ selectableTokensList.clear();
+ selectableTokensList.addAll(tokens);
+ excludedTokens.addAll(tokens);
+ }
+
+ public List<MergerToken> getSelectedTokens() {
+ List<MergerToken> t = new ArrayList<MergerToken>(selectableTokensList);
+ t.removeAll(excludedTokens);
+ return Collections.unmodifiableList(t);
+ }
+
+ public List<MergerToken> getSelectableTokens() {
+ return Collections.unmodifiableList(selectableTokensList);
+ }
+
+ public void removeToken(MergerToken token) {
+ selectableTokensList.remove(token);
+ excludedTokens.remove(token);
+
+ AbstractTableModel model = (AbstractTableModel) view.getTokens().getModel();
+ model.fireTableDataChanged();
+ }
+
+ // ----- properties -----
+
+ public Component getView() {
+ return view;
+ }
+
+ /**
+ * Called by table binding script to set current token.
+ */
+ public void setToken(MergerToken token) {
+ this.token = token;
+ }
+
+ /**
+ * Returns {@link MergerToken}s that are excluded from DB generation.
+ */
+ /*
+ * public Collection getExcludedTokens() { return excludedTokens; }
+ */
+
+ public boolean isIncluded() {
+ if (token == null) {
+ return false;
+ }
+
+ return !excludedTokens.contains(token);
+ }
+
+ public void setIncluded(boolean b) {
+ if (token == null) {
+ return;
+ }
+
+ if (b) {
+ excludedTokens.remove(token);
+ }
+ else {
+ excludedTokens.add(token);
+ }
+
+ tableSelectedAction();
+ }
+
+ /**
+ * A callback action that updates the state of Select All checkbox.
+ */
+ public void tableSelectedAction() {
+ int unselectedCount = excludedTokens.size() - permanentlyExcludedCount;
+
+ if (unselectedCount == selectableTokensList.size()) {
+ view.getCheckAll().setSelected(false);
+ }
+ else if (unselectedCount == 0) {
+ view.getCheckAll().setSelected(true);
+ }
+ }
+
+ // ------ other stuff ------
+
+ protected void initController() {
+ BindingBuilder builder = new BindingBuilder(
+ getApplication().getBindingFactory(),
+ this);
+
+ builder.bindToAction(view.getCheckAll(), "checkAllAction()");
+ builder.bindToAction(view.getReverseAll(), "reverseAllAction()");
+
+ TableModel model = new MergerTokenTableModel(this);
+
+ MergeDirection[] dirs = new MergeDirection[] {
+ MergeDirection.TO_DB, MergeDirection.TO_MODEL
+ };
+
+ view.getTokens().setModel(model);
+
+ TableColumnModel columnModel = view.getTokens().getColumnModel();
+
+ // dropdown for direction column
+ JComboBox directionCombo = Application.getWidgetFactory().createComboBox(dirs, false);
+ directionCombo.setEditable(false);
+ TableColumn directionColumn = columnModel.getColumn(
+ MergerTokenTableModel.COL_DIRECTION);
+ directionColumn.setCellEditor(new DefaultCellEditor(directionCombo));
+
+ columnModel.getColumn(MergerTokenTableModel.COL_SELECT).setPreferredWidth(50);
+ columnModel.getColumn(MergerTokenTableModel.COL_DIRECTION).setPreferredWidth(100);
+ columnModel.getColumn(MergerTokenTableModel.COL_SELECT).setMaxWidth(50);
+ columnModel.getColumn(MergerTokenTableModel.COL_DIRECTION).setMaxWidth(100);
+ }
+
+ public boolean isSelected(MergerToken token) {
+ return (selectableTokensList.contains(token) && !excludedTokens.contains(token));
+ }
+
+ public void select(MergerToken token, boolean select) {
+ if (select) {
+ excludedTokens.remove(token);
+ }
+ else {
+ excludedTokens.add(token);
+ }
+ }
+
+ public void setDirection(MergerToken token, MergeDirection direction) {
+ if (token.getDirection().equals(direction)) {
+ return;
+ }
+ int i = selectableTokensList.indexOf(token);
+ MergerToken reverse = token.createReverse(mergerTokenFactory);
+ selectableTokensList.set(i, reverse);
+ if (excludedTokens.remove(token)) {
+ excludedTokens.add(reverse);
+ }
+
+ /**
+ * Repaint, so that "Operation" column updates properly
+ */
+ view.getTokens().repaint();
+ }
+
+ public void checkAllAction() {
+
+ boolean isCheckAllSelected = view.getCheckAll().isSelected();
+
+ if (isCheckAllSelected) {
+ excludedTokens.clear();
+ }
+ else {
+ excludedTokens.addAll(selectableTokensList);
+ }
+
+ AbstractTableModel model = (AbstractTableModel) view.getTokens().getModel();
+ model.fireTableDataChanged();
+ }
+
+ public void reverseAllAction() {
+
+ for (int i = 0; i < selectableTokensList.size(); i++) {
+ MergerToken token = selectableTokensList.get(i);
+ MergerToken reverse = token.createReverse(mergerTokenFactory);
+ selectableTokensList.set(i, reverse);
+ if (excludedTokens.remove(token)) {
+ excludedTokens.add(reverse);
+ }
+ }
+
+ Collections.sort(selectableTokensList, new TokenComparator());
+ AbstractTableModel model = (AbstractTableModel) view.getTokens().getModel();
+ model.fireTableDataChanged();
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/7da0e897/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/merge/MergerTokenSelectorView.java
----------------------------------------------------------------------
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/merge/MergerTokenSelectorView.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/merge/MergerTokenSelectorView.java
new file mode 100644
index 0000000..263fb7c
--- /dev/null
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/merge/MergerTokenSelectorView.java
@@ -0,0 +1,106 @@
+/*****************************************************************
+ * 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.merge;
+
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+
+import com.jgoodies.forms.builder.PanelBuilder;
+import com.jgoodies.forms.factories.ButtonBarFactory;
+import com.jgoodies.forms.layout.CellConstraints;
+import com.jgoodies.forms.layout.FormLayout;
+
+/**
+ */
+public class MergerTokenSelectorView extends JPanel {
+
+ protected JTable tokens;
+ protected JCheckBox checkAll;
+ protected JLabel checkAllLabel;
+ protected JButton reverseAll;
+
+ public MergerTokenSelectorView() {
+
+ this.checkAll = new JCheckBox();
+ this.checkAllLabel = new JLabel("Check All Operations");
+ this.reverseAll = new JButton("Reverse All Operations");
+
+ checkAll.addItemListener(new ItemListener() {
+
+ public void itemStateChanged(ItemEvent event) {
+ if (checkAll.isSelected()) {
+ checkAllLabel.setText("Uncheck All Operations");
+ }
+ else {
+ checkAllLabel.setText("Check All Operations");
+ }
+ }
+ });
+
+ // assemble
+ JPanel checkAllPanel = new JPanel(new FlowLayout(FlowLayout.LEADING));
+ checkAllPanel.add(checkAll);
+ checkAllPanel.add(checkAllLabel);
+ JPanel topPanel = new JPanel();
+ topPanel.setLayout(new BorderLayout());
+ topPanel.add(checkAllPanel, BorderLayout.WEST);
+ topPanel.add(ButtonBarFactory.buildRightAlignedBar(reverseAll), BorderLayout.EAST);
+
+ tokens = new JTable();
+ tokens.setRowHeight(25);
+ tokens.setRowMargin(3);
+
+ CellConstraints cc = new CellConstraints();
+ PanelBuilder builder = new PanelBuilder(new FormLayout(
+ "fill:min(50dlu;pref):grow",
+ "p, 3dlu, fill:40dlu:grow"));
+ builder.setDefaultDialogBorder();
+ builder.addSeparator("Select Operations", cc.xy(1, 1));
+ builder.add(new JScrollPane(
+ tokens,
+ JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
+ JScrollPane.HORIZONTAL_SCROLLBAR_NEVER), cc.xy(1, 3));
+
+ setLayout(new BorderLayout());
+ add(topPanel, BorderLayout.NORTH);
+ add(builder.getPanel(), BorderLayout.CENTER);
+ }
+
+ public JTable getTokens() {
+ return tokens;
+ }
+
+ public JCheckBox getCheckAll() {
+ return checkAll;
+ }
+
+ public JButton getReverseAll() {
+ return reverseAll;
+ }
+}