This is an automated email from the ASF dual-hosted git repository. ntimofeev pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/cayenne.git
The following commit(s) were added to refs/heads/master by this push: new 69f1bf066 Drag and drop in DbImport targetTree (#622) 69f1bf066 is described below commit 69f1bf066ec1f846db8ed1af919d2793c1bac0be Author: Mikhail Dzianishchyts <mikhail.dzianishch...@gmail.com> AuthorDate: Fri Aug 2 14:56:13 2024 +0300 Drag and drop in DbImport targetTree (#622) * sorting only by type * migrate from TreeSet to List(to prevent auto sorting patterns), tests added * sort button functionality * modified equals in TableFilter * cleanUp * setDirty after sorting added * drag and drop in DbImport target tree * fix bugs, refactoring * fixed bug with moving node on the parent * Use interfaces for args and returns * Keep expanded nodes on model reload * Add dummy node to fix drag and drop issues * Check for duplicates on drag and drop * Clean up * Update tests --------- Co-authored-by: Ivan Nikitka <70625960+ivan-niki...@users.noreply.github.com> Co-authored-by: Ivan Nikitka <nikitko.i...@gmail.com> --- .../dbsync/reverse/dbimport/PatternParam.java | 27 ++-- .../reverse/filters/FiltersConfigBuilder.java | 10 +- .../dbsync/reverse/filters/IncludeTableFilter.java | 21 ++- .../dbsync/reverse/filters/PatternFilter.java | 43 ++++-- .../dbsync/reverse/filters/TableFilter.java | 37 +++-- .../cayenne/dbsync/reverse/dbload/DbLoaderIT.java | 43 ++++++ .../dbsync/reverse/filters/FiltersConfigTest.java | 134 +++++++++++++++++- .../dbsync/reverse/filters/TableFilterTest.java | 55 ++++++-- .../tools/DbImporterMojoConfigurationTest.java | 7 +- .../modeler/action/DefaultActionManager.java | 3 + .../cayenne/modeler/action/SortNodesAction.java | 59 ++++++++ .../modeler/action/dbimport/DeleteNodeAction.java | 6 +- .../action/dbimport/DragAndDropNodeAction.java | 157 +++++++++++++++++++++ .../modeler/action/dbimport/EditNodeAction.java | 4 +- .../action/dbimport/MoveImportNodeAction.java | 8 +- .../action/dbimport/TreeManipulationAction.java | 2 +- .../modeler/dialog/db/load/DbImportTreeNode.java | 60 +++++--- .../modeler/dialog/db/load/TransferableNode.java | 5 +- .../modeler/editor/dbimport/DbImportSorter.java | 22 +-- .../modeler/editor/dbimport/DbImportTree.java | 46 ++++-- .../editor/dbimport/DbImportTreeCellEditor.java | 7 +- .../editor/dbimport/DbImportTreeCellRenderer.java | 54 ++++++- .../editor/dbimport/DraggableTreePanel.java | 121 +++++++++++++--- .../editor/dbimport/PrintColumnsBiFunction.java | 2 +- .../editor/dbimport/PrintTablesBiFunction.java | 3 +- .../modeler/editor/dbimport/TreeToolbarPanel.java | 40 ++++-- .../modeler/undo/DbImportTreeUndoableEdit.java | 6 +- .../cayenne/modeler/images/icon-dbi-sort.png | Bin 0 -> 315 bytes .../editor/dbimport/DbImportSorterTest.java | 40 ++++-- 29 files changed, 848 insertions(+), 174 deletions(-) diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbimport/PatternParam.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbimport/PatternParam.java index 598bd132d..b62beb4e2 100644 --- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbimport/PatternParam.java +++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbimport/PatternParam.java @@ -19,6 +19,8 @@ package org.apache.cayenne.dbsync.reverse.dbimport; +import java.util.Objects; + import static org.apache.cayenne.util.Util.isBlank; /** @@ -77,16 +79,6 @@ public class PatternParam { set(pattern.getName()); } - @Override - public String toString() { - return toString(new StringBuilder(), "").toString(); - } - - public StringBuilder toString(StringBuilder res, String s) { - res.append(s).append(getClass().getSimpleName()).append(": ").append(pattern).append("\n"); - return res; - } - @Override public boolean equals(Object obj) { if (obj == this) { @@ -101,4 +93,19 @@ public class PatternParam { PatternParam patternParam = (PatternParam) obj; return patternParam.getPattern().equals(pattern); } + + @Override + public int hashCode() { + return Objects.hash(pattern); + } + + @Override + public String toString() { + return toString(new StringBuilder(), "").toString(); + } + + public StringBuilder toString(StringBuilder res, String s) { + res.append(s).append(getClass().getSimpleName()).append(": ").append(pattern).append("\n"); + return res; + } } diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/filters/FiltersConfigBuilder.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/filters/FiltersConfigBuilder.java index aca90baf3..8303c960b 100644 --- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/filters/FiltersConfigBuilder.java +++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/filters/FiltersConfigBuilder.java @@ -27,8 +27,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.SortedSet; -import java.util.TreeSet; import java.util.regex.Pattern; import org.apache.cayenne.dba.DbAdapter; @@ -156,16 +154,16 @@ public final class FiltersConfigBuilder { return schemaFilters; } - private SortedSet<Pattern> transformExcludeTable(Collection<ExcludeTable> excludeTables) { - SortedSet<Pattern> res = new TreeSet<>(PatternFilter.PATTERN_COMPARATOR); + private List<Pattern> transformExcludeTable(Collection<ExcludeTable> excludeTables) { + List<Pattern> res = new ArrayList<>(); for (ExcludeTable exclude : excludeTables) { res.add(PatternFilter.pattern(exclude.getPattern())); } return res; } - private SortedSet<IncludeTableFilter> transformIncludeTable(Collection<IncludeTable> includeTables) { - SortedSet<IncludeTableFilter> includeTableFilters = new TreeSet<>(); + private List<IncludeTableFilter> transformIncludeTable(Collection<IncludeTable> includeTables) { + List<IncludeTableFilter> includeTableFilters = new ArrayList<>(); for (IncludeTable includeTable : includeTables) { includeTableFilters.add(new IncludeTableFilter(includeTable.getPattern() , transform(includeTable.getIncludeColumns(), includeTable.getExcludeColumns()) diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/filters/IncludeTableFilter.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/filters/IncludeTableFilter.java index 35d504f5b..670b63823 100644 --- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/filters/IncludeTableFilter.java +++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/filters/IncludeTableFilter.java @@ -18,11 +18,12 @@ ****************************************************************/ package org.apache.cayenne.dbsync.reverse.filters; +import java.util.Objects; import java.util.regex.Pattern; /** -* @since 4.0. -*/ + * @since 4.0. + */ public class IncludeTableFilter implements Comparable<IncludeTableFilter> { public final Pattern pattern; @@ -50,14 +51,14 @@ public class IncludeTableFilter implements Comparable<IncludeTableFilter> { this.relationshipFilter = relationshipFilter; } - public boolean isIncludeColumn (String name) { + public boolean isIncludeColumn(String name) { return columnsFilter.isIncluded(name); } /** * @since 4.1 */ - public boolean isIncludeRelationship (String name) { + public boolean isIncludeRelationship(String name) { return relationshipFilter.isIncluded(name); } @@ -75,6 +76,18 @@ public class IncludeTableFilter implements Comparable<IncludeTableFilter> { } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + IncludeTableFilter that = (IncludeTableFilter) o; + + if (!Objects.equals(pattern, that.pattern)) return false; + if (!Objects.equals(columnsFilter, that.columnsFilter)) return false; + return Objects.equals(relationshipFilter, that.relationshipFilter); + } + @Override public String toString() { return toString(new StringBuilder(), "").toString(); diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/filters/PatternFilter.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/filters/PatternFilter.java index c06861903..704c14a2f 100644 --- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/filters/PatternFilter.java +++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/filters/PatternFilter.java @@ -20,9 +20,9 @@ package org.apache.cayenne.dbsync.reverse.filters; import org.apache.cayenne.util.Util; +import java.util.ArrayList; import java.util.Comparator; -import java.util.SortedSet; -import java.util.TreeSet; +import java.util.List; import java.util.regex.Pattern; /** @@ -63,18 +63,22 @@ public class PatternFilter { } }; - private final SortedSet<Pattern> includes; - private final SortedSet<Pattern> excludes; + private final List<Pattern> includes; + private final List<Pattern> excludes; public PatternFilter() { - this.includes = new TreeSet<>(PATTERN_COMPARATOR); - this.excludes = new TreeSet<>(PATTERN_COMPARATOR); + this.includes = new ArrayList<>(); + this.excludes = new ArrayList<>(); } - public SortedSet<Pattern> getIncludes() { + public List<Pattern> getIncludes() { return includes; } + public List<Pattern> getExcludes() { + return excludes; + } + public PatternFilter include(Pattern p) { includes.add(p); @@ -132,14 +136,29 @@ public class PatternFilter { return true; } - if (o == null || getClass() != o.getClass()) { + if (!(o instanceof PatternFilter)) { return false; } + PatternFilter that = (PatternFilter) o; + + if (includes == that.includes) { + return true; + } + + if (includes.size() != that.includes.size()) { + return false; + } - PatternFilter filter = (PatternFilter) o; - return includes.equals(filter.includes) - && excludes.equals(filter.excludes); + // Check if the lists have the same patterns in the same order + for (int i = 0; i < includes.size(); i++) { + Pattern pattern = excludes.get(i); + Pattern thatPattern = that.excludes.get(i); + if (!pattern.pattern().equals(thatPattern.pattern())) { + return false; + } + } + return true; } @Override @@ -153,7 +172,7 @@ public class PatternFilter { } else if (includes.size() > 1) { res.append("(").append(Util.join(includes, " OR ")).append(")"); } else { - res.append(includes.first().pattern()); + res.append(includes.get(0).pattern()); } if (!excludes.isEmpty()) { diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/filters/TableFilter.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/filters/TableFilter.java index c403ba0ef..7b7f6d80c 100644 --- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/filters/TableFilter.java +++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/filters/TableFilter.java @@ -19,7 +19,9 @@ package org.apache.cayenne.dbsync.reverse.filters; -import java.util.SortedSet; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; import java.util.TreeSet; import java.util.regex.Pattern; @@ -30,13 +32,13 @@ import org.apache.cayenne.util.Util; */ public class TableFilter { - private final SortedSet<IncludeTableFilter> includes; - private final SortedSet<Pattern> excludes; + private final List<IncludeTableFilter> includes; + private final List<Pattern> excludes; /** * Includes can contain only one include table */ - public TableFilter(SortedSet<IncludeTableFilter> includes, SortedSet<Pattern> excludes) { + public TableFilter(List<IncludeTableFilter> includes, List<Pattern> excludes) { this.includes = includes; this.excludes = excludes; } @@ -89,22 +91,26 @@ public class TableFilter { return include; } - public SortedSet<IncludeTableFilter> getIncludes() { + public List<IncludeTableFilter> getIncludes() { return includes; } + public List<Pattern> getExcludes() { + return excludes; + } + public static TableFilter include(String tablePattern) { - TreeSet<IncludeTableFilter> includes = new TreeSet<>(); + List<IncludeTableFilter> includes = new ArrayList<>(); includes.add(new IncludeTableFilter(tablePattern == null ? null : tablePattern.replaceAll("%", ".*"))); - return new TableFilter(includes, new TreeSet<>()); + return new TableFilter(includes, new ArrayList<>()); } public static TableFilter everything() { - TreeSet<IncludeTableFilter> includes = new TreeSet<>(); + List<IncludeTableFilter> includes = new ArrayList<>(); includes.add(new IncludeTableFilter(null)); - return new TableFilter(includes, new TreeSet<>()); + return new TableFilter(includes, new ArrayList<>()); } protected StringBuilder toString(StringBuilder res, String prefix) { @@ -133,9 +139,18 @@ public class TableFilter { TableFilter that = (TableFilter) o; - return excludes.equals(that.excludes) - && includes.equals(that.includes); + boolean excludeEquals = true; + // Check if the lists have the same patterns in the same order + for (int i = 0; i < excludes.size(); i++) { + Pattern pattern = excludes.get(i); + Pattern thatPattern = that.excludes.get(i); + if (!pattern.pattern().equals(thatPattern.pattern())) { + excludeEquals = false; + break; + } + } + return includes.equals(that.includes) && excludeEquals; } @Override diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/dbload/DbLoaderIT.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/dbload/DbLoaderIT.java index 4bf07fb10..39303bce9 100644 --- a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/dbload/DbLoaderIT.java +++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/dbload/DbLoaderIT.java @@ -22,6 +22,12 @@ package org.apache.cayenne.dbsync.reverse.dbload; import org.apache.cayenne.dba.DbAdapter; import org.apache.cayenne.dbsync.naming.DefaultObjectNameGenerator; import org.apache.cayenne.dbsync.naming.NoStemStemmer; +import org.apache.cayenne.dbsync.reverse.dbimport.ExcludeColumn; +import org.apache.cayenne.dbsync.reverse.dbimport.IncludeTable; +import org.apache.cayenne.dbsync.reverse.dbimport.ReverseEngineering; +import org.apache.cayenne.dbsync.reverse.dbimport.Schema; +import org.apache.cayenne.dbsync.reverse.filters.FiltersConfig; +import org.apache.cayenne.dbsync.reverse.filters.FiltersConfigBuilder; import org.apache.cayenne.di.Inject; import org.apache.cayenne.map.DataMap; import org.apache.cayenne.map.DbAttribute; @@ -42,6 +48,7 @@ import java.sql.Connection; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; /** @@ -67,6 +74,38 @@ public class DbLoaderIT extends RuntimeCase { private Connection connection; + @Test + public void testLoadingOrder() throws Exception { + ReverseEngineering engineering = new ReverseEngineering(); + IncludeTable artistTableWithExclusion = new IncludeTable("ARTIST"); + artistTableWithExclusion.addExcludeColumn(new ExcludeColumn("DATE_OF_BIRTH")); + engineering.addIncludeTable(artistTableWithExclusion); + engineering.addIncludeTable(new IncludeTable("ARTIST")); + + IncludeTable paintingTableWithExclusion = new IncludeTable("PAINTING"); + paintingTableWithExclusion.addExcludeColumn(new ExcludeColumn("PAINTING_DESCRIPTION")); + engineering.addIncludeTable(new IncludeTable("PAINTING")); + engineering.addIncludeTable(paintingTableWithExclusion); + + FiltersConfigBuilder configBuilder = new FiltersConfigBuilder(engineering); + FiltersConfig filtersConfig = configBuilder.build(); + + DbLoaderConfiguration dbLoaderConfiguration = new DbLoaderConfiguration(); + dbLoaderConfiguration.setFiltersConfig(filtersConfig); + + DbLoader loader = createDbLoader(dbLoaderConfiguration); + DataMap loaded = loader.load(); + assertNotNull(loaded); + + DbEntity artist = loaded.getDbEntity("ARTIST"); + DbEntity painting = loaded.getDbEntity("PAINTING"); + assertNotNull(artist); + assertNotNull(painting); + assertNull(getDbAttribute(artist,"DATE_OF_BIRTH")); + assertNotNull(getDbAttribute(painting,"PAINTING_DESCRIPTION")); + } + + /** * Test that parts of loader are in place */ @@ -124,6 +163,10 @@ public class DbLoaderIT extends RuntimeCase { return new DbLoader(adapter, connection, CONFIG, null, new DefaultObjectNameGenerator(NoStemStemmer.getInstance())); } + private DbLoader createDbLoader(DbLoaderConfiguration configuration) { + return new DbLoader(adapter, connection, configuration, null, new DefaultObjectNameGenerator(NoStemStemmer.getInstance())); + } + @After public void after() throws Exception { connection.close(); diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/filters/FiltersConfigTest.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/filters/FiltersConfigTest.java index e947b31c0..da277837f 100644 --- a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/filters/FiltersConfigTest.java +++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/filters/FiltersConfigTest.java @@ -20,14 +20,60 @@ package org.apache.cayenne.dbsync.reverse.filters; import junit.framework.TestCase; +import org.apache.cayenne.dbsync.reverse.dbimport.Catalog; +import org.apache.cayenne.dbsync.reverse.dbimport.ExcludeColumn; +import org.apache.cayenne.dbsync.reverse.dbimport.ExcludeProcedure; +import org.apache.cayenne.dbsync.reverse.dbimport.ExcludeTable; +import org.apache.cayenne.dbsync.reverse.dbimport.IncludeColumn; +import org.apache.cayenne.dbsync.reverse.dbimport.IncludeProcedure; +import org.apache.cayenne.dbsync.reverse.dbimport.IncludeTable; +import org.apache.cayenne.dbsync.reverse.dbimport.ReverseEngineering; +import org.apache.cayenne.dbsync.reverse.dbimport.Schema; +import java.util.ArrayList; import java.util.Collections; -import java.util.SortedSet; -import java.util.TreeSet; +import java.util.List; import java.util.regex.Pattern; +import static org.junit.Assert.assertThrows; + public class FiltersConfigTest extends TestCase { + private FiltersConfig filtersConfig; + + @Override + protected void setUp() throws Exception { + ReverseEngineering engineering = new ReverseEngineering(); + Catalog catalog = new Catalog("catalog"); + Schema schema = new Schema("schema"); + + schema.addIncludeTable(new IncludeTable("iT2")); + schema.addIncludeTable(new IncludeTable("iT1")); + + schema.addExcludeTable(new ExcludeTable("eT2")); + schema.addExcludeTable(new ExcludeTable("eT1")); + + schema.addIncludeProcedure(new IncludeProcedure("iP2")); + schema.addIncludeProcedure(new IncludeProcedure("iP1")); + + schema.addExcludeProcedure(new ExcludeProcedure("eP2")); + schema.addExcludeProcedure(new ExcludeProcedure("eP1")); + + schema.addIncludeColumn(new IncludeColumn("iC2")); + schema.addIncludeColumn(new IncludeColumn("iC1")); + + schema.addExcludeColumn(new ExcludeColumn("eC2")); + schema.addExcludeColumn(new ExcludeColumn("eC1")); + + catalog.addSchema(schema); + engineering.addCatalog(catalog); + + FiltersConfigBuilder configBuilder = new FiltersConfigBuilder(engineering); + configBuilder.compact(); + + filtersConfig = configBuilder.build(); + } + public void testToString_01() { FiltersConfig config = FiltersConfig.create(null, null, TableFilter.everything(), PatternFilter.INCLUDE_EVERYTHING); @@ -75,16 +121,92 @@ public class FiltersConfigTest extends TestCase { " Procedures: NONE\n", config.toString()); } - private SortedSet<Pattern> excludes(String ... p) { - SortedSet<Pattern> patterns = new TreeSet<Pattern>(PatternFilter.PATTERN_COMPARATOR); + public void testTableFilter(){ + assertNull(filtersConfig.tableFilter(null,null)); + + TableFilter tableFilter = filtersConfig.tableFilter("catalog", "schema"); + assertNotNull(tableFilter); + assertEquals(2,tableFilter.getIncludes().size()); + assertEquals(2,tableFilter.getExcludes().size()); + } + + public void testProceduresFilter(){ + assertNull(filtersConfig.proceduresFilter(null,null)); + + PatternFilter patternFilter = filtersConfig.proceduresFilter("catalog", "schema"); + assertNotNull(patternFilter); + assertEquals(2,patternFilter.getIncludes().size()); + assertEquals(2,patternFilter.getExcludes().size()); + } + + public void testGetCatalogs(){ + CatalogFilter[] catalogs = filtersConfig.getCatalogs(); + assertNotNull(catalogs); + assertEquals(1,catalogs.length); + } + + public void testGetCatalog(){ + assertNull(filtersConfig.getCatalog(null)); + CatalogFilter catalog = filtersConfig.getCatalog("catalog"); + assertNotNull(catalog); + assertEquals("catalog",catalog.name); + assertEquals(1,catalog.schemas.length); + } + + public void testGetSchemaFilter() { + assertNull(filtersConfig.getSchemaFilter(null,null)); + + SchemaFilter schemaFilter = filtersConfig.getSchemaFilter("catalog", "schema"); + assertNotNull(schemaFilter); + assertEquals("schema",schemaFilter.name); + assertEquals(2,schemaFilter.procedures.getIncludes().size()); + assertEquals(2,schemaFilter.procedures.getExcludes().size()); + } + + public void testNullArgumentBuild() { + FiltersConfigBuilder configBuilder = new FiltersConfigBuilder(null); + assertThrows(NullPointerException.class, configBuilder::build); + } + + public void testKeepingOrder() { + SchemaFilter schemaFilter = filtersConfig.getCatalog("catalog").getSchema("schema"); + + List<IncludeTableFilter> tablesIncludes = schemaFilter.tables.getIncludes(); + List<Pattern> includeColumns = tablesIncludes.get(0).columnsFilter.getIncludes(); + List<Pattern> excludeColumns = tablesIncludes.get(0).columnsFilter.getExcludes(); + List<Pattern> tablesExcludes = schemaFilter.tables.getExcludes(); + List<Pattern> proceduresIncludes = schemaFilter.procedures.getIncludes(); + List<Pattern> proceduresExcludes = schemaFilter.procedures.getExcludes(); + + assertEquals("iT2", tablesIncludes.get(0).pattern.pattern()); + assertEquals("iT1", tablesIncludes.get(1).pattern.pattern()); + + assertEquals("eT2", tablesExcludes.get(0).pattern()); + assertEquals("eT1", tablesExcludes.get(1).pattern()); + + assertEquals("iC2", includeColumns.get(0).pattern()); + assertEquals("iC1", includeColumns.get(1).pattern()); + + assertEquals("eC2", excludeColumns.get(0).pattern()); + assertEquals("eC1", excludeColumns.get(1).pattern()); + + assertEquals("iP2", proceduresIncludes.get(0).pattern()); + assertEquals("iP1", proceduresIncludes.get(1).pattern()); + + assertEquals("eP2", proceduresExcludes.get(0).pattern()); + assertEquals("eP1", proceduresExcludes.get(1).pattern()); + } + + private List<Pattern> excludes(String ... p) { + List<Pattern> patterns = new ArrayList<Pattern>(); for (String pattern : p) { patterns.add(PatternFilter.pattern(pattern)); } return patterns; } - protected SortedSet<IncludeTableFilter> includes(IncludeTableFilter ... filters) { - SortedSet<IncludeTableFilter> includeTableFilters = new TreeSet<IncludeTableFilter>(); + protected List<IncludeTableFilter> includes(IncludeTableFilter ... filters) { + List<IncludeTableFilter> includeTableFilters = new ArrayList<>(); Collections.addAll(includeTableFilters, filters); return includeTableFilters; diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/filters/TableFilterTest.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/filters/TableFilterTest.java index 21b0e2179..a7a633c84 100644 --- a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/filters/TableFilterTest.java +++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/filters/TableFilterTest.java @@ -18,6 +18,8 @@ ****************************************************************/ package org.apache.cayenne.dbsync.reverse.filters; +import java.util.ArrayList; +import java.util.List; import java.util.TreeSet; import java.util.regex.Pattern; @@ -39,11 +41,11 @@ public class TableFilterTest { @Test public void testInclude() { - TreeSet<IncludeTableFilter> includes = new TreeSet<>(); + List<IncludeTableFilter> includes = new ArrayList<>(); includes.add(new IncludeTableFilter("aaa")); includes.add(new IncludeTableFilter("bb")); - TableFilter filter = new TableFilter(includes, new TreeSet<>(PatternFilter.PATTERN_COMPARATOR)); + TableFilter filter = new TableFilter(includes, new ArrayList<>()); assertTrue(filter.isIncludeTable("aaa")); assertFalse(filter.isIncludeTable("aa")); @@ -56,11 +58,11 @@ public class TableFilterTest { @Test public void testExclude() { - TreeSet<Pattern> excludes = new TreeSet<>(PatternFilter.PATTERN_COMPARATOR); + List<Pattern> excludes = new ArrayList<>(); excludes.add(Pattern.compile("aaa")); excludes.add(Pattern.compile("bb")); - TreeSet<IncludeTableFilter> includes = new TreeSet<>(); + List<IncludeTableFilter> includes = new ArrayList<>(); includes.add(new IncludeTableFilter(null, PatternFilter.INCLUDE_EVERYTHING)); TableFilter filter = new TableFilter(includes, excludes); @@ -76,11 +78,11 @@ public class TableFilterTest { @Test public void testIncludeExclude() { - TreeSet<Pattern> excludes = new TreeSet<>(PatternFilter.PATTERN_COMPARATOR); + List<Pattern> excludes = new ArrayList<>(); excludes.add(Pattern.compile("aaa")); excludes.add(Pattern.compile("bb")); - TreeSet<IncludeTableFilter> includes = new TreeSet<>(); + List<IncludeTableFilter> includes = new ArrayList<>(); includes.add(new IncludeTableFilter("aa.*")); TableFilter filter = new TableFilter(includes, excludes); @@ -96,11 +98,11 @@ public class TableFilterTest { @Test public void testGetTableFilter() { - TreeSet<IncludeTableFilter> includes = new TreeSet<IncludeTableFilter>(); + List<IncludeTableFilter> includes = new ArrayList<>(); includes.add(new IncludeTableFilter("aaa")); includes.add(new IncludeTableFilter("bb")); - TreeSet<Pattern> excludes = new TreeSet<>(); + List<Pattern> excludes = new ArrayList<>(); TableFilter filter = new TableFilter(includes, excludes); @@ -112,4 +114,41 @@ public class TableFilterTest { assertNull(filter.getIncludeTableColumnFilter("")); assertNull(filter.getIncludeTableColumnFilter("bbbb")); } + + @Test + public void testExcludePriority(){ + List<IncludeTableFilter> includes = new ArrayList<>(); + includes.add(new IncludeTableFilter("a")); + + List<Pattern> excludes = new ArrayList<>(); + excludes.add(Pattern.compile("a")); + + TableFilter tableFilter = new TableFilter(includes, excludes); + + assertNull( tableFilter.getIncludeTableColumnFilter("a")); + } + + @Test + public void testPatternsOrder(){ + List<IncludeTableFilter> includes = new ArrayList<>(); + includes.add(new IncludeTableFilter("b")); + includes.add(new IncludeTableFilter("a")); + + List<Pattern> excludes = new ArrayList<>(); + excludes.add(Pattern.compile("b")); + excludes.add(Pattern.compile("a")); + + TableFilter tableFilter = new TableFilter(includes, excludes); + + assertEquals("b",tableFilter.getIncludes().get(0).pattern.pattern()); + assertEquals("b",tableFilter.getExcludes().get(0).pattern()); + } + @Test + public void testNullArguments(){ + TableFilter tableFilter = new TableFilter(null, null); + assertNotNull(tableFilter); + assertThrows(NullPointerException.class, () -> tableFilter.isIncludeTable(null) ); + + } + } \ No newline at end of file diff --git a/maven-plugins/cayenne-maven-plugin/src/test/java/org/apache/cayenne/tools/DbImporterMojoConfigurationTest.java b/maven-plugins/cayenne-maven-plugin/src/test/java/org/apache/cayenne/tools/DbImporterMojoConfigurationTest.java index 6ca8ef2ac..d2c056379 100644 --- a/maven-plugins/cayenne-maven-plugin/src/test/java/org/apache/cayenne/tools/DbImporterMojoConfigurationTest.java +++ b/maven-plugins/cayenne-maven-plugin/src/test/java/org/apache/cayenne/tools/DbImporterMojoConfigurationTest.java @@ -18,9 +18,10 @@ ****************************************************************/ package org.apache.cayenne.tools; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; -import java.util.TreeSet; import java.util.regex.Pattern; import org.apache.cayenne.dbsync.reverse.dbimport.Catalog; @@ -75,10 +76,10 @@ public class DbImporterMojoConfigurationTest extends AbstractMojoTestCase { FiltersConfig filters = dbImportConfiguration.getDbLoaderConfig().getFiltersConfig(); - TreeSet<IncludeTableFilter> includes = new TreeSet<>(); + List<IncludeTableFilter> includes = new ArrayList<>(); includes.add(new IncludeTableFilter(null, new PatternFilter().exclude("^ETL_.*"))); - TreeSet<Pattern> excludes = new TreeSet<>(PatternFilter.PATTERN_COMPARATOR); + List<Pattern> excludes = new ArrayList<>(); excludes.add(PatternFilter.pattern("^ETL_.*")); assertEquals(filters.tableFilter(null, "NHL_STATS"), diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/DefaultActionManager.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/DefaultActionManager.java index 1aec38f2f..24dd89518 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/DefaultActionManager.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/DefaultActionManager.java @@ -31,6 +31,7 @@ import org.apache.cayenne.modeler.action.dbimport.AddIncludeProcedureAction; import org.apache.cayenne.modeler.action.dbimport.AddIncludeTableAction; import org.apache.cayenne.modeler.action.dbimport.AddSchemaAction; import org.apache.cayenne.modeler.action.dbimport.DeleteNodeAction; +import org.apache.cayenne.modeler.action.dbimport.DragAndDropNodeAction; import org.apache.cayenne.modeler.action.dbimport.EditNodeAction; import org.apache.cayenne.modeler.action.dbimport.MoveImportNodeAction; import org.apache.cayenne.modeler.action.dbimport.MoveInvertNodeAction; @@ -124,7 +125,9 @@ public class DefaultActionManager implements ActionManager { registerAction(new EditNodeAction(application)).setAlwaysOn(true); registerAction(new DeleteNodeAction(application)).setAlwaysOn(true); registerAction(new MoveImportNodeAction(application)).setAlwaysOn(true); + registerAction(new DragAndDropNodeAction(application)).setAlwaysOn(true); registerAction(new LoadDbSchemaAction(application)).setAlwaysOn(true); + registerAction(new SortNodesAction(application)).setAlwaysOn(true); registerAction(new MoveInvertNodeAction(application)).setAlwaysOn(true); registerAction(new AboutAction(application)).setAlwaysOn(true); registerAction(new DocumentationAction(application)).setAlwaysOn(true); diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/SortNodesAction.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/SortNodesAction.java new file mode 100644 index 000000000..fd7f856ff --- /dev/null +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/SortNodesAction.java @@ -0,0 +1,59 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * 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.action; + +import org.apache.cayenne.dbsync.reverse.dbimport.ReverseEngineering; +import org.apache.cayenne.modeler.Application; +import org.apache.cayenne.modeler.action.dbimport.TreeManipulationAction; +import org.apache.cayenne.modeler.dialog.db.load.DbImportTreeNode; +import org.apache.cayenne.modeler.editor.dbimport.DbImportSorter; + +import java.awt.event.ActionEvent; +import java.util.List; + +/** + * @since 5.0 + */ +public class SortNodesAction extends TreeManipulationAction { + + private static final String ACTION_NAME = "Sort"; + private static final String ICON_NAME = "icon-dbi-sort.png"; + + public SortNodesAction(Application application) { + super(ACTION_NAME, application); + } + + public String getIconName() { + return ICON_NAME; + } + + @Override + public void performAction(ActionEvent e) { + tree.stopEditing(); + ReverseEngineering reverseEngineeringOldCopy = new ReverseEngineering(tree.getReverseEngineering()); + List<DbImportTreeNode> treeExpandList = tree.getTreeExpandList(); + DbImportSorter.sortSubtree(tree.getRootNode(), DbImportSorter.NODE_COMPARATOR_BY_TYPE_BY_NAME); + putReverseEngineeringToUndoManager(reverseEngineeringOldCopy); + getProjectController().setDirty(true); + tree.reloadModelKeepingExpanded(); + tree.expandTree(treeExpandList); + } + +} diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/dbimport/DeleteNodeAction.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/dbimport/DeleteNodeAction.java index 70f51a47d..4093d9553 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/dbimport/DeleteNodeAction.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/dbimport/DeleteNodeAction.java @@ -38,7 +38,7 @@ import org.apache.cayenne.modeler.editor.dbimport.DraggableTreePanel; import javax.swing.tree.TreePath; import java.awt.event.ActionEvent; -import java.util.ArrayList; +import java.util.List; /** * @since 4.1 @@ -117,7 +117,7 @@ public class DeleteNodeAction extends TreeManipulationAction { DbImportModel model = (DbImportModel) tree.getModel(); model.removeNodeFromParent(selectedElement); getProjectController().setDirty(true); - model.reload(parentElement); + tree.reloadModelKeepingExpanded(parentElement); } @Override @@ -152,7 +152,7 @@ public class DeleteNodeAction extends TreeManipulationAction { } if (paths.length > 1) { getProjectController().setDirty(true); - ArrayList<DbImportTreeNode> expandList = tree.getTreeExpandList(); + List<DbImportTreeNode> expandList = tree.getTreeExpandList(); tree.translateReverseEngineeringToTree(tree.getReverseEngineering(), false); tree.expandTree(expandList); } else { diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/dbimport/DragAndDropNodeAction.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/dbimport/DragAndDropNodeAction.java new file mode 100644 index 000000000..8779723eb --- /dev/null +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/dbimport/DragAndDropNodeAction.java @@ -0,0 +1,157 @@ +/***************************************************************** + * 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.action.dbimport; + +import org.apache.cayenne.dbsync.reverse.dbimport.ReverseEngineering; +import org.apache.cayenne.modeler.Application; +import org.apache.cayenne.modeler.dialog.db.load.DbImportTreeNode; +import org.apache.cayenne.modeler.editor.dbimport.DbImportModel; +import org.apache.cayenne.modeler.editor.dbimport.DbImportSorter; + +import javax.swing.JOptionPane; +import javax.swing.JTree; +import javax.swing.tree.TreePath; +import java.awt.event.ActionEvent; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @since 5.0 + */ +public class DragAndDropNodeAction extends TreeManipulationAction { + + private static final String ACTION_NAME = "DragAndDrop"; + private DbImportTreeNode[] nodes; + private DbImportTreeNode dropLocationParentNode; + private DbImportTreeNode sourceParentNode; + private JTree.DropLocation dropLocation; + + public DragAndDropNodeAction(Application application) { + super(ACTION_NAME, application); + } + + @Override + public void performAction(ActionEvent e) { + if (dropLocationDuplicateFound()) { + return; + } + DbImportModel model = (DbImportModel) tree.getModel(); + ReverseEngineering reverseEngineeringOldCopy = new ReverseEngineering(tree.getReverseEngineering()); + List<DbImportTreeNode> nodesToExpand = Arrays.stream(nodes) + .filter(node -> tree.isExpanded(new TreePath(node.getPath()))) + .collect(Collectors.toList()); + + for (DbImportTreeNode node : nodes) { + if (checkDropPossibility(node)) { + int index = calculateDropIndex(); + model.removeNodeFromParent(node); + model.insertNodeInto(node, dropLocationParentNode, index); + } + } + getProjectController().setDirty(true); + DbImportSorter.syncUserObjectItems(dropLocationParentNode); + DbImportSorter.syncUserObjectItems(sourceParentNode); + putReverseEngineeringToUndoManager(reverseEngineeringOldCopy); + tree.reloadModelKeepingExpanded(dropLocationParentNode); + tree.expandTree(nodesToExpand); + } + + private boolean dropLocationDuplicateFound() { + for (DbImportTreeNode node : nodes) { + if (dropLocationParentNode.isNodeChild(node)) { + // we are fine about this + continue; + } + int duplicateIndex = dropLocationParentNode.getChildNodes().indexOf(node); + if (duplicateIndex >= 0) { + JOptionPane.showMessageDialog( + Application.getFrame(), + dropLocationParentNode.getSimpleNodeName() + " already contains " + node.getSimpleNodeName(), + "Error moving", + JOptionPane.ERROR_MESSAGE); + return true; + } + } + return false; + } + + private int calculateDropIndex() { + int index = dropLocation.getChildIndex(); + //node moving inside a one node + if (sourceParentNode == dropLocationParentNode) { + int childCount = dropLocationParentNode.getChildCount(); + int childIndex = dropLocation.getChildIndex(); + if (childIndex == childCount) { + index = childCount - 1; + } + } + //If target node is collapsed + if (index == -1 && sourceParentNode != dropLocationParentNode) { + index = dropLocationParentNode.getChildCount(); + } + + //If the target node is an expanded parent node, we place the node in the first position + if (index == -1 && sourceParentNode == dropLocationParentNode) { + index = 0; + } + return index; + } + + private boolean checkDropPossibility(DbImportTreeNode node) { + // Don't allow a node to be dropped onto itself + if (node == dropLocationParentNode) { + return false; + } + // Don't allow a node to be dropped onto one of its descendants + for (DbImportTreeNode childNode : node.getChildNodes()) { + if (isNodeAncestor(childNode, dropLocationParentNode)) { + return false; + } + } + return true; + } + + private boolean isNodeAncestor(DbImportTreeNode node1, DbImportTreeNode node2) { + if (node2 == null) { + return false; + } + if (node2.getParent() == node1) { + return true; + } + return isNodeAncestor(node1, node2.getParent()); + } + + public void setNodes(DbImportTreeNode[] nodes) { + this.nodes = nodes; + } + + public void setDropLocationParentNode(DbImportTreeNode dropLocationParentNode) { + this.dropLocationParentNode = dropLocationParentNode; + } + + public void setSourceParentNode(DbImportTreeNode sourceParentNode) { + this.sourceParentNode = sourceParentNode; + } + + public void setDropLocation(JTree.DropLocation dropLocation) { + this.dropLocation = dropLocation; + } +} diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/dbimport/EditNodeAction.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/dbimport/EditNodeAction.java index 1e1864a4b..a1e9ffc3a 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/dbimport/EditNodeAction.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/dbimport/EditNodeAction.java @@ -79,8 +79,8 @@ public class EditNodeAction extends TreeManipulationAction { putReverseEngineeringToUndoManager(reverseEngineeringOldCopy); } } - DbImportSorter.sortSingleNode(selectedElement.getParent()); - tree.reloadModel(); + DbImportSorter.sortSingleNode(selectedElement.getParent(),DbImportSorter.NODE_COMPARATOR_BY_TYPE); + tree.reloadModelKeepingExpanded(); tree.setSelectionPath(new TreePath(selectedElement.getPath())); selectedElement = null; } diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/dbimport/MoveImportNodeAction.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/dbimport/MoveImportNodeAction.java index 455082808..4c0474051 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/dbimport/MoveImportNodeAction.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/dbimport/MoveImportNodeAction.java @@ -39,8 +39,8 @@ import org.apache.cayenne.modeler.util.CayenneAction; import javax.swing.tree.TreePath; import java.awt.event.ActionEvent; -import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.regex.Matcher; @@ -187,7 +187,7 @@ public class MoveImportNodeAction extends CayenneAction { } if ((paths.length > 1) && (targetTree.getSelectionPath() != null)) { getProjectController().setDirty(true); - ArrayList<DbImportTreeNode> expandList = targetTree.getTreeExpandList(); + List<DbImportTreeNode> expandList = targetTree.getTreeExpandList(); targetTree.translateReverseEngineeringToTree(targetTree.getReverseEngineering(), false); targetTree.expandTree(expandList); } @@ -199,8 +199,8 @@ public class MoveImportNodeAction extends CayenneAction { getProjectController().getApplication().getUndoManager().addEdit(undoableEdit); } if (foundNode != null) { - DbImportSorter.sortSubtree((DbImportTreeNode) foundNode.getRoot()); - targetTree.reloadModel(); + DbImportSorter.sortSubtree((DbImportTreeNode) foundNode.getRoot(),DbImportSorter.NODE_COMPARATOR_BY_TYPE); + targetTree.reloadModelKeepingExpanded(); targetTree.setSelectionPath(new TreePath(foundNode.getLastChild().getPath())); } } finally { diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/dbimport/TreeManipulationAction.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/dbimport/TreeManipulationAction.java index fbbc47879..e3cbd9462 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/dbimport/TreeManipulationAction.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/dbimport/TreeManipulationAction.java @@ -190,7 +190,7 @@ public abstract class TreeManipulationAction extends CayenneAction { if (!updateSelected) { savedPath = new TreePath(parentElement.getPath()); } - model.reload(updateSelected ? selectedElement : parentElement); + tree.reloadModelKeepingExpanded(updateSelected ? selectedElement : parentElement); if ((savedPath != null) && (parentElement.getUserObject().getClass() != ReverseEngineering.class)) { tree.setSelectionPath(savedPath); } diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/DbImportTreeNode.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/DbImportTreeNode.java index ada7428d0..332953b9f 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/DbImportTreeNode.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/DbImportTreeNode.java @@ -36,6 +36,7 @@ import javax.swing.tree.DefaultMutableTreeNode; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; @@ -50,64 +51,72 @@ public class DbImportTreeNode extends DefaultMutableTreeNode { this(null); } + public DbImportTreeNode(Object userObject) { + this(userObject, true); + } + private DbImportTreeNode(Object userObject, boolean allowsChildren) { super(); this.userObject = userObject; this.allowsChildren = allowsChildren; parent = null; + + if (userObject != null && (isCatalog() || isSchema() || isIncludeTable())) { + add(new ExpandableEnforcerNode()); + } } public boolean isIncludeTable() { - return (getUserObject().getClass() == IncludeTable.class); + return (getUserObjectClass() == IncludeTable.class); } public boolean isExcludeTable() { - return (getUserObject().getClass() == ExcludeTable.class); + return (getUserObjectClass() == ExcludeTable.class); } public boolean isIncludeColumn() { - return (getUserObject().getClass() == IncludeColumn.class); + return (getUserObjectClass() == IncludeColumn.class); } public boolean isExcludeColumn() { - return (getUserObject().getClass() == ExcludeColumn.class); + return (getUserObjectClass() == ExcludeColumn.class); } public boolean isExcludeProcedure() { - return (getUserObject().getClass() == ExcludeProcedure.class); + return (getUserObjectClass() == ExcludeProcedure.class); } public boolean isIncludeProcedure() { - return (getUserObject().getClass() == IncludeProcedure.class); + return (getUserObjectClass() == IncludeProcedure.class); } public boolean isLabel() { - return (getUserObject().getClass() == String.class); + return (getUserObjectClass() == String.class); } public boolean isSchema() { - return (getUserObject().getClass() == Schema.class); + return (getUserObjectClass() == Schema.class); } public boolean isCatalog() { - return (getUserObject().getClass() == Catalog.class); + return (getUserObjectClass() == Catalog.class); } public boolean isReverseEngineering() { - return (getUserObject().getClass() == ReverseEngineering.class); + return (getUserObjectClass() == ReverseEngineering.class); } - public DbImportTreeNode(Object userObject) { - this(userObject, true); + public Class<?> getUserObjectClass() { + return getUserObject() != null ? getUserObject().getClass() : null; } // Compare parents chain public boolean parentsIsEqual(DbImportTreeNode reverseEngineeringNode) { ArrayList<DbImportTreeNode> reverseEngineeringNodeParents; if (reverseEngineeringNode == null) { - reverseEngineeringNodeParents = new ArrayList<>(); + reverseEngineeringNodeParents = new ArrayList<>(); } else { - reverseEngineeringNodeParents = reverseEngineeringNode.getParents(); + reverseEngineeringNodeParents = reverseEngineeringNode.getParents(); } ArrayList<DbImportTreeNode> dbNodeParents = getParents(); for (DbImportTreeNode node : reverseEngineeringNodeParents) { @@ -140,7 +149,7 @@ public class DbImportTreeNode extends DefaultMutableTreeNode { @Override public DbImportTreeNode getParent() { - return (DbImportTreeNode)super.getParent(); + return (DbImportTreeNode) super.getParent(); } protected String getFormattedName(String className, String nodeName) { @@ -194,13 +203,13 @@ public class DbImportTreeNode extends DefaultMutableTreeNode { return false; } DbImportTreeNode objNode = (DbImportTreeNode) obj; - if (!objNode.getSimpleNodeName().equals(this.getSimpleNodeName())) { - return false; - } - if (objNode.getUserObject().getClass() != this.getUserObject().getClass()) { - return false; - } - return true; + return Objects.equals(getSimpleNodeName(), objNode.getSimpleNodeName()) + && getUserObjectClass() == objNode.getUserObjectClass(); + } + + @Override + public int hashCode() { + return Objects.hash(getSimpleNodeName(), getUserObjectClass()); } public boolean isLoaded() { @@ -250,4 +259,11 @@ public class DbImportTreeNode extends DefaultMutableTreeNode { public DbImportTreeNode getLastChild() { return (DbImportTreeNode) super.getLastChild(); } + + public static class ExpandableEnforcerNode extends DbImportTreeNode { + + public ExpandableEnforcerNode() { + super("", false); + } + } } diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/TransferableNode.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/TransferableNode.java index a7d8af6b3..7a8eaa8f0 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/TransferableNode.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/TransferableNode.java @@ -20,11 +20,8 @@ package org.apache.cayenne.modeler.dialog.db.load; import org.apache.cayenne.dbsync.reverse.dbimport.Catalog; -import org.apache.cayenne.dbsync.reverse.dbimport.FilterContainer; -import org.apache.cayenne.dbsync.reverse.dbimport.IncludeProcedure; import org.apache.cayenne.dbsync.reverse.dbimport.IncludeTable; import org.apache.cayenne.dbsync.reverse.dbimport.PatternParam; -import org.apache.cayenne.dbsync.reverse.dbimport.ReverseEngineering; import org.apache.cayenne.dbsync.reverse.dbimport.Schema; import java.awt.datatransfer.DataFlavor; @@ -45,7 +42,7 @@ public class TransferableNode extends DbImportTreeNode implements Transferable { includeTableFlavor, patternParamFlavor }; public TransferableNode(Object userObject) { - this.userObject = userObject; + super(userObject); } @Override diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbimport/DbImportSorter.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbimport/DbImportSorter.java index b44bc6246..2b585d6d4 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbimport/DbImportSorter.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbimport/DbImportSorter.java @@ -36,28 +36,32 @@ import java.util.List; * @since 5.0 */ public class DbImportSorter { - private static final Comparator<DbImportTreeNode> NODE_COMPARATOR = Comparator + public static final Comparator<DbImportTreeNode> NODE_COMPARATOR_BY_TYPE = Comparator + .comparing(DbImportTreeNode::getNodeType); + + + public static final Comparator<DbImportTreeNode> NODE_COMPARATOR_BY_TYPE_BY_NAME = Comparator .comparing(DbImportTreeNode::getNodeType) .thenComparing(DbImportTreeNode::getSimpleNodeName); - public static void sortSingleNode(DbImportTreeNode node) { - sortNodeItems(node); + public static void sortSingleNode(DbImportTreeNode node, Comparator<DbImportTreeNode> comparator) { + sortNodeItems(node, comparator); syncUserObjectItems(node); } - public static void sortSubtree(DbImportTreeNode root) { - sortSingleNode(root); - root.getChildNodes().forEach(DbImportSorter::sortSubtree); + public static void sortSubtree(DbImportTreeNode root,Comparator<DbImportTreeNode> comparator) { + sortSingleNode(root, comparator); + root.getChildNodes().forEach(r -> sortSubtree(r, comparator)); } - private static void sortNodeItems(DbImportTreeNode node) { + private static void sortNodeItems(DbImportTreeNode node, Comparator<DbImportTreeNode> comparator) { List<DbImportTreeNode> childNodes = node.getChildNodes(); node.removeAllChildren(); - childNodes.sort(NODE_COMPARATOR); + childNodes.sort(comparator); childNodes.forEach(node::add); } - private static void syncUserObjectItems(DbImportTreeNode parentNode) { + public static void syncUserObjectItems(DbImportTreeNode parentNode) { Object userObject = parentNode.getUserObject(); diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbimport/DbImportTree.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbimport/DbImportTree.java index 93afaedb8..b2262e071 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbimport/DbImportTree.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbimport/DbImportTree.java @@ -35,11 +35,13 @@ import org.apache.cayenne.modeler.dialog.db.load.TransferableNode; import javax.swing.JTree; import javax.swing.event.TreeExpansionEvent; import javax.swing.event.TreeExpansionListener; +import javax.swing.plaf.basic.BasicTreeUI; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.List; import java.util.function.BiFunction; @@ -53,6 +55,8 @@ public class DbImportTree extends JTree { public DbImportTree(TreeNode node) { super(node); + setRowHeight(0); + setUI(new TreeUI()); createTreeExpandListener(); } @@ -71,7 +75,7 @@ public class DbImportTree extends JTree { printParams(reverseEngineering.getExcludeColumns(), root); printParams(reverseEngineering.getIncludeProcedures(), root); printParams(reverseEngineering.getExcludeProcedures(), root); - DbImportSorter.sortSubtree(root); + DbImportSorter.sortSubtree(root, DbImportSorter.NODE_COMPARATOR_BY_TYPE); model.reload(); } @@ -212,7 +216,7 @@ public class DbImportTree extends JTree { } // Create list of expanded elements - private ArrayList<DbImportTreeNode> createTreeExpandList(DbImportTreeNode rootNode, ArrayList<DbImportTreeNode> resultList) { + private List<DbImportTreeNode> createTreeExpandList(DbImportTreeNode rootNode, List<DbImportTreeNode> resultList) { for (int i = 0; i < rootNode.getChildCount(); i++) { DbImportTreeNode childNode = (DbImportTreeNode) rootNode.getChildAt(i); TreePath childPath = new TreePath(childNode.getPath()); @@ -226,12 +230,23 @@ public class DbImportTree extends JTree { return resultList; } - public ArrayList<DbImportTreeNode> getTreeExpandList() { + public void reloadModelKeepingExpanded(DbImportTreeNode node) { + DbImportModel model = (DbImportModel) getModel(); + List<DbImportTreeNode> nodesToExpand = getTreeExpandList(); + model.reload(node); + expandTree(nodesToExpand); + } + + public void reloadModelKeepingExpanded() { + reloadModelKeepingExpanded(getRootNode()); + } + + public List<DbImportTreeNode> getTreeExpandList() { ArrayList<DbImportTreeNode> resultList = new ArrayList<>(); return createTreeExpandList(getRootNode(), resultList); } - private void expandBeginningWithNode(DbImportTreeNode rootNode, ArrayList<DbImportTreeNode> list) { + private void expandBeginningWithNode(DbImportTreeNode rootNode, List<DbImportTreeNode> list) { for (int i = 0; i < rootNode.getChildCount(); i++) { DbImportTreeNode childNode = (DbImportTreeNode) rootNode.getChildAt(i); list.forEach((element) -> { @@ -245,7 +260,7 @@ public class DbImportTree extends JTree { } } - public void expandTree(ArrayList<DbImportTreeNode> expandIndexesList) { + public void expandTree(List<DbImportTreeNode> expandIndexesList) { expandBeginningWithNode(getRootNode(), expandIndexesList); } @@ -258,15 +273,10 @@ public class DbImportTree extends JTree { } } - public void reloadModel(){ - DbImportModel model = (DbImportModel)this.getModel(); - model.reload(); - } - private void printIncludeTables(Collection<IncludeTable> collection, DbImportTreeNode parent) { for (IncludeTable includeTable : collection) { DbImportTreeNode node = !isTransferable ? new DbImportTreeNode(includeTable) : new TransferableNode(includeTable); - if (!node.getSimpleNodeName().equals("")) { + if (!node.getSimpleNodeName().isEmpty()) { if (isTransferable && includeTable.getIncludeColumns().isEmpty() && includeTable.getExcludeColumns().isEmpty()) { printParams(Collections.singletonList(new IncludeColumn("Loading...")), node); @@ -370,4 +380,18 @@ public class DbImportTree extends JTree { public boolean isTransferable() { return isTransferable; } + + static class TreeUI extends BasicTreeUI { + + @Override + protected boolean shouldPaintExpandControl(TreePath path, int row, boolean isExpanded, + boolean hasBeenExpanded, boolean isLeaf) { + DbImportTreeNode node = (DbImportTreeNode) path.getLastPathComponent(); + int childCount = node.getChildCount(); + boolean onlyEnforcerChild = childCount == 1 + && node.getFirstChild().getClass() == DbImportTreeNode.ExpandableEnforcerNode.class; + return super.shouldPaintExpandControl(path, row, isExpanded, hasBeenExpanded, isLeaf) + && (childCount > 1 || !onlyEnforcerChild); + } + } } diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbimport/DbImportTreeCellEditor.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbimport/DbImportTreeCellEditor.java index 1789f25cb..e4e354c54 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbimport/DbImportTreeCellEditor.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbimport/DbImportTreeCellEditor.java @@ -26,6 +26,7 @@ import org.apache.cayenne.modeler.dialog.db.load.DbImportTreeNode; import org.apache.cayenne.util.Util; import javax.swing.JTree; +import javax.swing.UIManager; import javax.swing.event.CellEditorListener; import javax.swing.event.ChangeEvent; import javax.swing.tree.DefaultTreeCellEditor; @@ -44,6 +45,7 @@ public class DbImportTreeCellEditor extends DefaultTreeCellEditor { public DbImportTreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer) { super(tree, renderer); + setFont(UIManager.getFont("Tree.font")); this.addCellEditorListener(new CellEditorListener() { @Override public void editingStopped(ChangeEvent e) { @@ -73,8 +75,7 @@ public class DbImportTreeCellEditor extends DefaultTreeCellEditor { if (value instanceof DbImportTreeNode) { value = ((DbImportTreeNode) value).getSimpleNodeName(); } - return super.getTreeCellEditorComponent(tree, value, isSelected, expanded, - leaf, row); + return super.getTreeCellEditorComponent(tree, value, isSelected, expanded, leaf, row); } @Override @@ -122,7 +123,7 @@ public class DbImportTreeCellEditor extends DefaultTreeCellEditor { } if (tree.getSelectionPath() != null) { DbImportTreeNode selectedNode = (DbImportTreeNode) tree.getSelectionPath().getLastPathComponent(); - ((DbImportModel) tree.getModel()).reload(selectedNode); + ((DbImportTree) tree).reloadModelKeepingExpanded(selectedNode); } } diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbimport/DbImportTreeCellRenderer.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbimport/DbImportTreeCellRenderer.java index d2a1ce14e..4e5e5ed47 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbimport/DbImportTreeCellRenderer.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbimport/DbImportTreeCellRenderer.java @@ -30,10 +30,13 @@ import org.apache.cayenne.dbsync.reverse.dbimport.Schema; import org.apache.cayenne.modeler.dialog.db.load.DbImportTreeNode; import org.apache.cayenne.modeler.util.ModelerUtil; +import javax.swing.Icon; import javax.swing.ImageIcon; +import javax.swing.JLabel; import javax.swing.JTree; import javax.swing.tree.DefaultTreeCellRenderer; import java.awt.Component; +import java.awt.Dimension; import java.util.HashMap; import java.util.Map; @@ -43,16 +46,15 @@ import java.util.Map; public class DbImportTreeCellRenderer extends DefaultTreeCellRenderer { protected DbImportTreeNode node; - private Map<Class, String> icons; - private Map<Class, String> transferableTreeIcons; + private Map<Class<?>, String> icons; + private Map<Class<?>, String> transferableTreeIcons; public DbImportTreeCellRenderer() { - super(); initIcons(); - initTrasferableTreeIcons(); + initTransferableTreeIcons(); } - private void initTrasferableTreeIcons() { + private void initTransferableTreeIcons() { transferableTreeIcons = new HashMap<>(); transferableTreeIcons.put(Catalog.class, "icon-dbi-catalog.png"); transferableTreeIcons.put(Schema.class, "icon-dbi-schema.png"); @@ -73,7 +75,7 @@ public class DbImportTreeCellRenderer extends DefaultTreeCellRenderer { icons.put(ExcludeProcedure.class, "icon-dbi-excludeProcedure.png"); } - private ImageIcon getIconByNodeType(Class nodeClass, boolean isTransferable) { + private ImageIcon getIconByNodeType(Class<?> nodeClass, boolean isTransferable) { String iconName = !isTransferable ? icons.get(nodeClass) : transferableTreeIcons.get(nodeClass); if (iconName == null) { return null; @@ -91,6 +93,44 @@ public class DbImportTreeCellRenderer extends DefaultTreeCellRenderer { super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); node = (DbImportTreeNode) value; setIcon(getIconByNodeType(node.getUserObject().getClass(), ((DbImportTree) tree).isTransferable())); - return this; + return value instanceof DbImportTreeNode.ExpandableEnforcerNode + ? ExpandableEnforcer.getInstance() + : this; + } + + @Override + public Icon getLeafIcon() { + return null; + } + + @Override + public Icon getOpenIcon() { + return null; + } + + @Override + public Icon getClosedIcon() { + return null; + } + + private static class ExpandableEnforcer extends JLabel { + + private static ExpandableEnforcer instance; + + public ExpandableEnforcer() { + setPreferredSize(new Dimension(0, 0)); + } + + public static ExpandableEnforcer getInstance() { + if (instance == null) { + instance = new ExpandableEnforcer(); + } + return instance; + } + + @Override + public Dimension getPreferredSize() { + return super.getPreferredSize(); + } } } diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbimport/DraggableTreePanel.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbimport/DraggableTreePanel.java index 320ac406b..a24b3ad3a 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbimport/DraggableTreePanel.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbimport/DraggableTreePanel.java @@ -38,6 +38,7 @@ import org.apache.cayenne.modeler.action.dbimport.AddIncludeColumnAction; import org.apache.cayenne.modeler.action.dbimport.AddIncludeProcedureAction; import org.apache.cayenne.modeler.action.dbimport.AddIncludeTableAction; import org.apache.cayenne.modeler.action.dbimport.AddSchemaAction; +import org.apache.cayenne.modeler.action.dbimport.DragAndDropNodeAction; import org.apache.cayenne.modeler.action.dbimport.MoveImportNodeAction; import org.apache.cayenne.modeler.action.dbimport.MoveInvertNodeAction; import org.apache.cayenne.modeler.action.dbimport.TreeManipulationAction; @@ -54,7 +55,6 @@ import javax.swing.JTree; import javax.swing.TransferHandler; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; -import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.TreePath; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; @@ -93,6 +93,7 @@ public class DraggableTreePanel extends JScrollPane { private CayenneAction.CayenneToolbarButton moveButton; private CayenneAction.CayenneToolbarButton moveInvertButton; + private ImportSourceTree importSourceTree; public DraggableTreePanel(ProjectController projectController, DbImportTree sourceTree, DbImportTree targetTree) { super(sourceTree); @@ -143,6 +144,7 @@ public class DraggableTreePanel extends JScrollPane { targetTree.setTransferHandler(new TargetTreeTransferHandler()); targetTree.addTreeSelectionListener(new TargetTreeSelectionListener()); targetTree.setDragEnabled(true); + targetTree.setDropMode(DropMode.INSERT); } private boolean canBeInverted() { @@ -174,12 +176,6 @@ public class DraggableTreePanel extends JScrollPane { moveInvertButton = (CayenneAction.CayenneToolbarButton) actionInv.buildButton(); moveInvertButton.setShowingText(true); moveInvertButton.setText(MOVE_INV_BUTTON_LABEL); - - - DefaultTreeCellRenderer renderer = (DefaultTreeCellRenderer) sourceTree.getCellRenderer(); - renderer.setLeafIcon(null); - renderer.setClosedIcon(null); - renderer.setOpenIcon(null); } private void initLevels() { @@ -226,7 +222,7 @@ public class DraggableTreePanel extends JScrollPane { if (selectedElement.isIncludeColumn() || selectedElement.isExcludeColumn()) { DbImportTreeNode node = targetTree.findNode(targetTree.getRootNode(), selectedElement.getParent(), 0); - if(node != null && node.isExcludeTable()) { + if (node != null && node.isExcludeTable()) { return false; } } @@ -273,7 +269,7 @@ public class DraggableTreePanel extends JScrollPane { return sourceTree; } - private static class SourceTreeTransferHandler extends TransferHandler { + private class SourceTreeTransferHandler extends TransferHandler { @Override public int getSourceActions(JComponent c) { @@ -282,6 +278,7 @@ public class DraggableTreePanel extends JScrollPane { @Override public Transferable createTransferable(JComponent c) { + importSourceTree = ImportSourceTree.SOURCE_TREE; JTree tree = (JTree) c; TreePath[] paths = tree.getSelectionPaths(); int pathLength = paths == null ? 0 : paths.length; @@ -356,14 +353,59 @@ public class DraggableTreePanel extends JScrollPane { } private class TargetTreeTransferHandler extends TransferHandler { + private DbImportTreeNode sourceParentNode; + + @Override + protected Transferable createTransferable(JComponent c) { + JTree tree = (JTree) c; + importSourceTree = ImportSourceTree.TARGET_TREE; + TreePath[] paths = tree.getSelectionPaths(); + DbImportTreeNode lastSelectedNode = (DbImportTreeNode) tree.getLastSelectedPathComponent(); + sourceParentNode = lastSelectedNode.getParent(); + if (paths != null && paths.length > 0) { + DbImportTreeNode[] nodes = new DbImportTreeNode[paths.length]; + for (int i = 0; i < paths.length; i++) { + nodes[i] = (DbImportTreeNode) paths[i].getLastPathComponent(); + } + return new Transferable() { + @Override + public DataFlavor[] getTransferDataFlavors() { + return TransferableNode.flavors; + } + + @Override + public boolean isDataFlavorSupported(DataFlavor flavor) { + return true; + } + + @Override + public Object getTransferData(DataFlavor flavor) { + return nodes; + } + }; + } + return null; + } @Override public int getSourceActions(JComponent c) { - return COPY_OR_MOVE; + return TransferHandler.MOVE; } @Override public boolean canImport(TransferSupport support) { + JTree.DropLocation dropLocation = (JTree.DropLocation) support.getDropLocation(); + DbImportTreeNode dropLocationParentNode = (DbImportTreeNode) dropLocation.getPath().getLastPathComponent(); + + List<Class<?>> allowedItemsList = insertableLevels.get(dropLocationParentNode.getUserObject().getClass()); + DbImportTreeNode[] nodes = getNodesFromSupport(support); + if (nodes != null && allowedItemsList != null) { + for (DbImportTreeNode node : nodes) { + if (!allowedItemsList.contains(node.getUserObject().getClass())) { + return false; + } + } + } return support.isDrop(); } @@ -372,19 +414,21 @@ public class DraggableTreePanel extends JScrollPane { if (!canImport(support)) { return false; } - if (!canBeMoved()) { - return false; + if (importSourceTree == ImportSourceTree.TARGET_TREE) { + return importDataFromTargetTree(support); } - Transferable transferable = support.getTransferable(); - DbImportTreeNode[] transferData = null; - try { - for (DataFlavor dataFlavor : transferable.getTransferDataFlavors()) { - transferData = (DbImportTreeNode[]) transferable.getTransferData(dataFlavor); - } - } catch (IOException | UnsupportedFlavorException e) { + if (importSourceTree == ImportSourceTree.SOURCE_TREE) { + return importDataFromSourceTree(support); + } + return false; + } + + private boolean importDataFromSourceTree(TransferSupport support) { + if (!canBeMoved()) { return false; } - if (transferData != null) { + DbImportTreeNode[] nodes = getNodesFromSupport(support); + if (nodes != null) { MoveImportNodeAction action = projectController.getApplication().getActionManager() .getAction(MoveImportNodeAction.class); action.setSourceTree(sourceTree); @@ -395,6 +439,38 @@ public class DraggableTreePanel extends JScrollPane { } return false; } + + private boolean importDataFromTargetTree(TransferSupport support) { + JTree.DropLocation dropLocation = (JTree.DropLocation) support.getDropLocation(); + DbImportTreeNode dropLocationParentNode = (DbImportTreeNode) dropLocation.getPath().getLastPathComponent(); + + DbImportTreeNode[] nodes = getNodesFromSupport(support); + if (nodes != null) { + DragAndDropNodeAction action = projectController.getApplication().getActionManager() + .getAction(DragAndDropNodeAction.class); + action.setDropLocationParentNode(dropLocationParentNode); + action.setSourceParentNode(sourceParentNode); + action.setDropLocation(dropLocation); + action.setNodes(nodes); + action.setTree(targetTree); + action.performAction(null); + return true; + } + return false; + } + + private DbImportTreeNode[] getNodesFromSupport(TransferSupport support) { + Transferable transferable = support.getTransferable(); + DbImportTreeNode[] nodes = null; + try { + for (DataFlavor dataFlavor : transferable.getTransferDataFlavors()) { + nodes = (DbImportTreeNode[]) transferable.getTransferData(dataFlavor); + } + } catch (IOException | UnsupportedFlavorException e) { + return null; + } + return nodes; + } } private class SourceTreeSelectionListener implements TreeSelectionListener { @@ -418,4 +494,9 @@ public class DraggableTreePanel extends JScrollPane { } } } + + private enum ImportSourceTree { + TARGET_TREE, + SOURCE_TREE + } } diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbimport/PrintColumnsBiFunction.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbimport/PrintColumnsBiFunction.java index be77314be..6781c8a8e 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbimport/PrintColumnsBiFunction.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbimport/PrintColumnsBiFunction.java @@ -55,6 +55,6 @@ public class PrintColumnsBiFunction implements BiFunction<FilterContainer, DbImp dbImportTree.packColumns(tableFilter, container); container.setLoaded(true); - model.reload(container); + dbImportTree.reloadModelKeepingExpanded(container); } } diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbimport/PrintTablesBiFunction.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbimport/PrintTablesBiFunction.java index b547fb9a2..65f50b52d 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbimport/PrintTablesBiFunction.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbimport/PrintTablesBiFunction.java @@ -39,7 +39,6 @@ public class PrintTablesBiFunction implements BiFunction<FilterContainer, DbImpo @Override public Void apply(FilterContainer filterContainer, DbImportTreeNode root) { - DbImportModel model = (DbImportModel) dbImportTree.getModel(); boolean isTransferable = dbImportTree.isTransferable(); if (root.getChildCount() != 0) { root.removeAllChildren(); @@ -58,7 +57,7 @@ public class PrintTablesBiFunction implements BiFunction<FilterContainer, DbImpo root.add(node); dbImportTree.packColumns(includeTable, node); } - model.reload(root); + dbImportTree.reloadModelKeepingExpanded(root); return null; } diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbimport/TreeToolbarPanel.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbimport/TreeToolbarPanel.java index 506cc2d05..00f8d406c 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbimport/TreeToolbarPanel.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbimport/TreeToolbarPanel.java @@ -30,6 +30,7 @@ import org.apache.cayenne.dbsync.reverse.dbimport.ReverseEngineering; import org.apache.cayenne.dbsync.reverse.dbimport.Schema; import org.apache.cayenne.modeler.ProjectController; import org.apache.cayenne.modeler.action.GetDbConnectionAction; +import org.apache.cayenne.modeler.action.SortNodesAction; import org.apache.cayenne.modeler.action.dbimport.AddCatalogAction; import org.apache.cayenne.modeler.action.dbimport.AddExcludeColumnAction; import org.apache.cayenne.modeler.action.dbimport.AddExcludeProcedureAction; @@ -68,6 +69,7 @@ class TreeToolbarPanel extends JToolBar { private JButton editButton; private JButton deleteButton; private JButton configureButton; + private JButton sortButton; private DbImportTree reverseEngineeringTree; private Map<Class, List<JButton>> levels; @@ -170,6 +172,7 @@ class TreeToolbarPanel extends JToolBar { this.add(includeProcedureButton); this.add(excludeProcedureButton); this.add(editButton); + this.add(sortButton); this.addSeparator(); this.add(deleteButton); this.add(configureButton); @@ -198,6 +201,23 @@ class TreeToolbarPanel extends JToolBar { deleteButton.setEnabled(true); } + + + private void createButtons(DraggableTreePanel panel) { + schemaButton = createButton(AddSchemaAction.class, 0); + catalogButton = createButton(AddCatalogAction.class, 0); + includeTableButton = createButton(AddIncludeTableAction.class, 1); + excludeTableButton = createButton(AddExcludeTableAction.class, 2, ExcludeTable.class); + includeColumnButton = createButton(AddIncludeColumnAction.class, 2, IncludeColumn.class); + excludeColumnButton = createButton(AddExcludeColumnAction.class, 2, ExcludeColumn.class); + includeProcedureButton = createButton(AddIncludeProcedureAction.class, 2, IncludeProcedure.class); + excludeProcedureButton = createButton(AddExcludeProcedureAction.class, 3, ExcludeProcedure.class); + sortButton = createButton(SortNodesAction.class,0); + editButton = createButton(EditNodeAction.class, 0); + deleteButton = createDeleteButton(panel); + configureButton = createConfigureButton(); + } + private <T extends TreeManipulationAction> JButton createButton(Class<T> actionClass, int position) { TreeManipulationAction action = projectController.getApplication().getActionManager().getAction(actionClass); action.setTree(reverseEngineeringTree); @@ -211,21 +231,15 @@ class TreeToolbarPanel extends JToolBar { return action.buildButton(position); } - private void createButtons(DraggableTreePanel panel) { - schemaButton = createButton(AddSchemaAction.class, 0); - catalogButton = createButton(AddCatalogAction.class, 0); - includeTableButton = createButton(AddIncludeTableAction.class, 1); - excludeTableButton = createButton(AddExcludeTableAction.class, 2, ExcludeTable.class); - includeColumnButton = createButton(AddIncludeColumnAction.class, 2, IncludeColumn.class); - excludeColumnButton = createButton(AddExcludeColumnAction.class, 2, ExcludeColumn.class); - includeProcedureButton = createButton(AddIncludeProcedureAction.class, 2, IncludeProcedure.class); - excludeProcedureButton = createButton(AddExcludeProcedureAction.class, 3, ExcludeProcedure.class); - editButton = createButton(EditNodeAction.class, 0); + private JButton createConfigureButton() { + GetDbConnectionAction action = projectController.getApplication().getActionManager().getAction(GetDbConnectionAction.class); + return action.buildButton(0); + } + + private JButton createDeleteButton(DraggableTreePanel panel) { DeleteNodeAction deleteNodeAction = projectController.getApplication().getActionManager().getAction(DeleteNodeAction.class); deleteNodeAction.setTree(reverseEngineeringTree); deleteNodeAction.setPanel(panel); - deleteButton = deleteNodeAction.buildButton(0); - GetDbConnectionAction action = projectController.getApplication().getActionManager().getAction(GetDbConnectionAction.class); - configureButton = action.buildButton(0); + return deleteNodeAction.buildButton(0); } } diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/undo/DbImportTreeUndoableEdit.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/undo/DbImportTreeUndoableEdit.java index 6fe77b051..2d775e4ae 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/undo/DbImportTreeUndoableEdit.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/undo/DbImportTreeUndoableEdit.java @@ -27,7 +27,7 @@ import org.apache.cayenne.modeler.editor.dbimport.DbImportTree; import javax.swing.undo.AbstractUndoableEdit; import javax.swing.undo.CannotRedoException; import javax.swing.undo.CannotUndoException; -import java.util.ArrayList; +import java.util.List; /** * @since 4.1 @@ -56,7 +56,7 @@ public class DbImportTreeUndoableEdit extends AbstractUndoableEdit { public void redo() throws CannotRedoException { tree.stopEditing(); tree.setReverseEngineering(this.nextReverseEngineering); - ArrayList<DbImportTreeNode> list = tree.getTreeExpandList(); + List<DbImportTreeNode> list = tree.getTreeExpandList(); projectController.getApplication().getMetaData().add(projectController.getCurrentDataMap(), tree.getReverseEngineering()); projectController.setDirty(true); tree.translateReverseEngineeringToTree(tree.getReverseEngineering(), false); @@ -67,7 +67,7 @@ public class DbImportTreeUndoableEdit extends AbstractUndoableEdit { public void undo() throws CannotUndoException { tree.stopEditing(); tree.setReverseEngineering(this.previousReverseEngineering); - ArrayList<DbImportTreeNode> list = tree.getTreeExpandList(); + List<DbImportTreeNode> list = tree.getTreeExpandList(); projectController.getApplication().getMetaData().add(projectController.getCurrentDataMap(), tree.getReverseEngineering()); projectController.setDirty(true); tree.translateReverseEngineeringToTree(tree.getReverseEngineering(), false); diff --git a/modeler/cayenne-modeler/src/main/resources/org/apache/cayenne/modeler/images/icon-dbi-sort.png b/modeler/cayenne-modeler/src/main/resources/org/apache/cayenne/modeler/images/icon-dbi-sort.png new file mode 100644 index 000000000..f7dca9afe Binary files /dev/null and b/modeler/cayenne-modeler/src/main/resources/org/apache/cayenne/modeler/images/icon-dbi-sort.png differ diff --git a/modeler/cayenne-modeler/src/test/java/org/apache/cayenne/modeler/editor/dbimport/DbImportSorterTest.java b/modeler/cayenne-modeler/src/test/java/org/apache/cayenne/modeler/editor/dbimport/DbImportSorterTest.java index 50c57bf2a..a64ab86fa 100644 --- a/modeler/cayenne-modeler/src/test/java/org/apache/cayenne/modeler/editor/dbimport/DbImportSorterTest.java +++ b/modeler/cayenne-modeler/src/test/java/org/apache/cayenne/modeler/editor/dbimport/DbImportSorterTest.java @@ -47,22 +47,44 @@ public class DbImportSorterTest { } @Test - public void sortNodeTest(){ - DbImportSorter.sortSingleNode(node); + public void sortByTypeByNameSingleNodeTest(){ + DbImportSorter.sortSingleNode(node,DbImportSorter.NODE_COMPARATOR_BY_TYPE_BY_NAME); - assertEquals("a", node.getChildNodes().get(0).getSimpleNodeName()); - assertEquals("b", node.getChildNodes().get(1).getSimpleNodeName()); - assertEquals("c", node.getChildNodes().get(2).getSimpleNodeName()); + // DbImportTreeNode.ExpandableEnforcerNode at index 0 + assertEquals("a", node.getChildNodes().get(1).getSimpleNodeName()); + assertEquals("b", node.getChildNodes().get(2).getSimpleNodeName()); + assertEquals("c", node.getChildNodes().get(3).getSimpleNodeName()); + } + + @Test + public void sortByTypeSingleNodeTest(){ + DbImportSorter.sortSingleNode(node,DbImportSorter.NODE_COMPARATOR_BY_TYPE); + + // DbImportTreeNode.ExpandableEnforcerNode at index 0 + assertEquals("a", node.getChildNodes().get(3).getSimpleNodeName()); + assertEquals("b", node.getChildNodes().get(2).getSimpleNodeName()); + assertEquals("c", node.getChildNodes().get(1).getSimpleNodeName()); } @Test - public void sortNodeWithAllChildrenTest(){ - DbImportSorter.sortSubtree(node); + public void sortByTypeByNameSubtreeTest(){ + DbImportSorter.sortSubtree(node,DbImportSorter.NODE_COMPARATOR_BY_TYPE_BY_NAME); - DbImportTreeNode tableNode = node.getChildNodes().get(0); - DbImportTreeNode columnNode = tableNode.getChildNodes().get(0); + // DbImportTreeNode.ExpandableEnforcerNode at index 0 + DbImportTreeNode tableNode = node.getChildNodes().get(1); + DbImportTreeNode columnNode = tableNode.getChildNodes().get(1); assertEquals("1", columnNode.getSimpleNodeName()); } + @Test + public void sortByTypeBySubtreeTest(){ + DbImportSorter.sortSubtree(node,DbImportSorter.NODE_COMPARATOR_BY_TYPE); + + // DbImportTreeNode.ExpandableEnforcerNode at index 0 + DbImportTreeNode tableNode = node.getChildNodes().get(3); + DbImportTreeNode columnNode = tableNode.getChildNodes().get(1); + assertEquals("2", columnNode.getSimpleNodeName()); + } + }