CAY-2003 cdbimport doesn't work properly with several includeTable tags

Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/f438729f
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/f438729f
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/f438729f

Branch: refs/heads/master
Commit: f438729f19bd9a3c90d78e251413421e24bf4ec2
Parents: c401537
Author: Alex Kolonitsky <alex.kolonit...@gmail.com>
Authored: Thu Apr 23 18:20:54 2015 +0300
Committer: Alex Kolonitsky <alex.kolonit...@gmail.com>
Committed: Thu Apr 23 18:20:54 2015 +0300

----------------------------------------------------------------------
 .../org/apache/cayenne/access/DbLoader.java     | 132 +++---
 .../access/loader/BooleanNameFilter.java        |  35 --
 .../access/loader/DbLoaderConfiguration.java    |   7 +-
 .../access/loader/filters/CatalogFilter.java    |  62 +++
 .../cayenne/access/loader/filters/DbPath.java   | 153 -------
 .../access/loader/filters/EntityFilters.java    | 424 -------------------
 .../access/loader/filters/ExcludeFilter.java    |  41 --
 .../cayenne/access/loader/filters/Filter.java   |  30 --
 .../access/loader/filters/FilterFactory.java    |  94 ----
 .../access/loader/filters/FiltersConfig.java    | 177 ++------
 .../access/loader/filters/IncludeFilter.java    |  75 ----
 .../loader/filters/IncludeTableFilter.java      |  71 ++++
 .../access/loader/filters/ListFilter.java       | 122 ------
 .../loader/filters/OldFilterConfigBridge.java   | 150 +++++++
 .../access/loader/filters/PatternFilter.java    | 167 ++++++++
 .../access/loader/filters/SchemaFilter.java     |  49 +++
 .../access/loader/filters/TableFilter.java      | 112 +++++
 .../org/apache/cayenne/access/DbLoaderIT.java   |  46 +-
 .../cayenne/access/DbLoaderPartialIT.java       |   4 +-
 .../access/loader/filters/DbPathTest.java       |  75 ----
 .../loader/filters/EntityFiltersTest.java       |  62 ---
 .../loader/filters/FiltersConfigTest.java       | 236 +++--------
 .../access/loader/filters/FiltersFactory.java   |  44 --
 .../loader/filters/IncludeFilterTest.java       |  13 +-
 .../loader/filters/PatternFilterTest.java       |  78 ++++
 .../access/loader/filters/TableFilterTest.java  |  91 ++++
 .../apache/cayenne/tools/DbImporterTask.java    |   9 +-
 .../tools/dbimport/DbImportConfiguration.java   |  10 +-
 .../cayenne/tools/dbimport/config/Catalog.java  |  12 +
 .../tools/dbimport/config/FilterContainer.java  |  28 ++
 .../dbimport/config/FiltersConfigBuilder.java   | 386 ++++++++++++-----
 .../tools/dbimport/config/IncludeTable.java     |  18 +-
 .../tools/dbimport/config/PatternParam.java     |   7 +-
 .../dbimport/config/ReverseEngineering.java     |  19 +
 .../cayenne/tools/dbimport/config/Schema.java   |   7 +
 .../config/FiltersConfigBuilderTest.java        | 238 ++++++++++-
 .../modeler/dialog/db/DbLoaderHelper.java       |  34 +-
 .../modeler/dialog/db/MergerOptions.java        |  49 +--
 .../apache/cayenne/tools/DbImporterMojo.java    |  16 +-
 .../cayenne/tools/DbImporterMojoTest.java       |   5 +-
 40 files changed, 1633 insertions(+), 1755 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/f438729f/cayenne-server/src/main/java/org/apache/cayenne/access/DbLoader.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/DbLoader.java 
b/cayenne-server/src/main/java/org/apache/cayenne/access/DbLoader.java
index d759213..8e2d82f 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/DbLoader.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/DbLoader.java
@@ -30,15 +30,16 @@ import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 
 import org.apache.cayenne.CayenneException;
 import org.apache.cayenne.access.loader.DbLoaderConfiguration;
 import org.apache.cayenne.access.loader.ManyToManyCandidateEntity;
-import org.apache.cayenne.access.loader.filters.EntityFilters;
-import org.apache.cayenne.access.loader.filters.Filter;
-import org.apache.cayenne.access.loader.filters.FiltersConfig;
-import org.apache.cayenne.access.loader.filters.DbPath;
+import org.apache.cayenne.access.loader.filters.*;
+import org.apache.cayenne.access.loader.filters.CatalogFilter;
+import org.apache.cayenne.access.loader.filters.SchemaFilter;
+import org.apache.cayenne.access.loader.filters.TableFilter;
 import org.apache.cayenne.dba.DbAdapter;
 import org.apache.cayenne.dba.TypesMapping;
 import org.apache.cayenne.map.DataMap;
@@ -61,8 +62,6 @@ import org.apache.cayenne.util.EntityMergeSupport;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
-import static org.apache.cayenne.access.loader.filters.FilterFactory.*;
-
 /**
  * Utility class that does reverse engineering of the database. It can create
  * DataMaps using database meta data obtained via JDBC driver.
@@ -223,27 +222,29 @@ public class DbLoader {
      * @return
      * @since 3.2
      */
-    public List<DbEntity> getTables(DbLoaderConfiguration config, String[] 
types)
+    public Map<DbEntity, PatternFilter> getTables(DbLoaderConfiguration 
config, String[] types)
             throws SQLException {
 
-        List<DbEntity> tables = new LinkedList<DbEntity>();
+        Map<DbEntity, PatternFilter> tables = new HashMap<DbEntity, 
PatternFilter>();
         FiltersConfig filters = config.getFiltersConfig();
-        for (DbPath path : filters.pathsForQueries()) {
-            tables.addAll(getDbEntities(filters, path, types));
+        for (CatalogFilter o : filters.catalogs) {
+            for (SchemaFilter schema : o.schemas) {
+                tables.putAll(getDbEntities(filters, o.name, schema.name, 
types));
+            }
         }
 
         return tables;
     }
 
-    private List<DbEntity> getDbEntities(FiltersConfig filters, DbPath dbPath, 
String[] types) throws SQLException {
+    private Map<DbEntity, PatternFilter> getDbEntities(FiltersConfig filters, 
String catalog, String schema, String[] types) throws SQLException {
         if (LOGGER.isDebugEnabled()) {
-            LOGGER.debug("Read tables: catalog=" + dbPath.catalog + ", 
schema=" + dbPath.schema + ", types="
+            LOGGER.debug("Read tables: catalog=" + catalog + ", schema=" + 
schema + ", types="
                     + Arrays.toString(types));
         }
 
-        ResultSet rs = getMetaData().getTables(dbPath.catalog, dbPath.schema, 
WILDCARD, types);
+        ResultSet rs = getMetaData().getTables(catalog, schema, WILDCARD, 
types);
 
-        List<DbEntity> tables = new ArrayList<DbEntity>();
+        Map<DbEntity, PatternFilter> tables = new HashMap<DbEntity, 
PatternFilter>();
         try {
             while (rs.next()) {
                 // Oracle 9i and newer has a nifty recycle bin feature... but 
we don't
@@ -258,14 +259,15 @@ public class DbLoader {
 
                 DbEntity table = new DetectedDbEntity(name);
 
-                String catalog = rs.getString("TABLE_CAT");
-                table.setCatalog(catalog);
+                String c = rs.getString("TABLE_CAT");
+                table.setCatalog(c);
 
-                String schema = rs.getString("TABLE_SCHEM");
-                table.setSchema(schema);
+                String s = rs.getString("TABLE_SCHEM");
+                table.setSchema(s);
 
-                if (filters.filter(new DbPath(catalog, 
schema)).tableFilter().isInclude(table)) {
-                    tables.add(table);
+                PatternFilter filter = filters.tableFilter(c, 
s).isIncludeTable(table.getName());
+                if (filter != null) {
+                    tables.put(table, filter);
                 }
             }
         } finally {
@@ -276,18 +278,17 @@ public class DbLoader {
 
     /**
      * Loads dbEntities for the specified tables.
-     * 
      * @param map
      *            DataMap to be populated with DbEntities.
-     * @param config
      * @param tables
-     *            The list of org.apache.cayenne.ashwood.dbutil.Table objects
-     *            for which DbEntities must be created.  @return false if 
loading must be immediately aborted.
+     * @param config
      */
-    public List<DbEntity> loadDbEntities(DataMap map, DbLoaderConfiguration 
config, Collection<? extends DbEntity> tables) throws SQLException {
+    public List<DbEntity> loadDbEntities(DataMap map, Map<DbEntity, 
PatternFilter> tables, DbLoaderConfiguration config) throws SQLException {
         /** List of db entities to process. */
         List<DbEntity> dbEntityList = new ArrayList<DbEntity>();
-        for (DbEntity dbEntity : tables) {
+        for (Entry<DbEntity, PatternFilter> entry : tables.entrySet()) {
+            DbEntity dbEntity = entry.getKey();
+            PatternFilter tableFilter = entry.getValue();
 
             // Check if there already is a DbEntity under such name
             // if so, consult the delegate what to do
@@ -326,7 +327,7 @@ public class DbLoader {
             if (delegate != null) {
                 delegate.dbEntityAdded(dbEntity);
             }
-            loadDbAttributes(config.getFiltersConfig(), dbEntity);
+            loadDbAttributes(dbEntity, tableFilter);
 
             // delegate might have thrown this entity out... so check if it is 
still
             // around before continuing processing
@@ -347,11 +348,11 @@ public class DbLoader {
 
     }
 
-    private void getPrimaryKeysForEachTableAndStoreItInDbEntity(DataMap map, 
Collection<? extends DbEntity> tables)
+    private void getPrimaryKeysForEachTableAndStoreItInDbEntity(DataMap map, 
Map<DbEntity, PatternFilter> tables)
             throws SQLException {
 
         for (DbEntity dbEntity : map.getDbEntities()) {
-            if (!tables.contains(dbEntity)) { // TODO is it ok? equals is not 
overridden
+            if (tables.get(dbEntity) == null) { // TODO is it ok? equals is 
not overridden
                 continue;
             }
 
@@ -381,7 +382,7 @@ public class DbLoader {
         }
     }
 
-    private void loadDbAttributes(FiltersConfig filters, DbEntity dbEntity) 
throws SQLException {
+    private void loadDbAttributes(DbEntity dbEntity, PatternFilter 
columnFilter) throws SQLException {
         ResultSet rs = getMetaData().getColumns(dbEntity.getCatalog(), 
dbEntity.getSchema(), dbEntity.getName(), "%");
 
         try {
@@ -399,12 +400,12 @@ public class DbLoader {
 
                 DbAttribute attr = loadDbAttribute(rs);
                 attr.setEntity(dbEntity);
-                DbPath path = new DbPath(dbEntity.getCatalog(), 
dbEntity.getSchema(), tableName);
-                Filter<DbAttribute> filter = 
filters.filter(path).columnFilter();
-                if (!filter.isInclude(attr)) {
+
+                if (!columnFilter.isInclude(attr.getName())) {
                     if (LOGGER.isDebugEnabled()) {
                         LOGGER.debug("Importing: attribute '" + 
attr.getEntity().getName() + "." + attr.getName()
-                                + "' is skipped (Path: " + path + "; Filter: " 
+ filter + ")");
+                                + "' is skipped (Path: " + 
dbEntity.getCatalog() + "/" + dbEntity.getSchema() + "/"
+                                + dbEntity.getName() + "; Filter: " + 
columnFilter + ")");
                     }
                     continue;
                 }
@@ -540,8 +541,10 @@ public class DbLoader {
                     String fkEntityName = key.getFKTableName();
 
                     fkEntity = map.getDbEntity(fkEntityName);
-                    DbPath path = new DbPath(entity.getCatalog(), 
entity.getSchema(), entity.getName());
-                    if 
(!config.getFiltersConfig().filter(path).tableFilter().isInclude(fkEntity)) {
+                    PatternFilter filter = config.getFiltersConfig()
+                            .tableFilter(entity.getCatalog(), 
entity.getSchema())
+                                .isIncludeTable(fkEntity.getName());
+                    if (filter == null) {
                         continue;
                     }
 
@@ -699,19 +702,17 @@ public class DbLoader {
      * 
      * @since 1.0.7
      * @deprecated since 4.0 use
-     *             {@link #load(org.apache.cayenne.map.DataMap, 
DbLoaderConfiguration, String...)}
-     *             method that supports catalogs.
      */
     @Deprecated
-       public DataMap loadDataMapFromDB(String schemaPattern, String 
tablePattern, DataMap dataMap) throws SQLException {
+    public DataMap loadDataMapFromDB(String schemaPattern, String 
tablePattern, DataMap dataMap) throws SQLException {
 
-               DbLoaderConfiguration configuration = new 
DbLoaderConfiguration();
-               configuration.setFiltersConfig(new FiltersConfig(new 
EntityFilters(new DbPath(null, schemaPattern),
-                               include(tablePattern), TRUE, NULL)));
+        DbLoaderConfiguration configuration = new DbLoaderConfiguration();
+        configuration.setFiltersConfig(FiltersConfig.create(null, 
schemaPattern,
+                        TableFilter.include(tablePattern), 
PatternFilter.INCLUDE_NOTHING));
 
-               load(dataMap, configuration);
-               return dataMap;
-       }
+        load(dataMap, configuration);
+        return dataMap;
+    }
 
     /**
      * Performs database reverse engineering and generates DataMap object that
@@ -719,8 +720,6 @@ public class DbLoader {
      * of tables to read.
      * 
      * @deprecated since 4.0 use
-     *             {@link #load(org.apache.cayenne.map.DataMap, 
DbLoaderConfiguration, String...)}
-     *             method that supports catalogs.
      */
     @Deprecated
     public DataMap loadDataMapFromDB(String schemaPattern, String 
tablePattern, String[] tableTypes, DataMap dataMap)
@@ -728,24 +727,14 @@ public class DbLoader {
         dataMap.clear();
 
         DbLoaderConfiguration config = new DbLoaderConfiguration();
-        config.setFiltersConfig(new FiltersConfig(new EntityFilters(
-                new DbPath(null, schemaPattern), 
transformPatternToFilter(tablePattern), TRUE, NULL)));
+        config.setFiltersConfig(FiltersConfig.create(null, schemaPattern,
+                TableFilter.include(tablePattern), 
PatternFilter.INCLUDE_NOTHING));
         config.setTableTypes(tableTypes);
         
         load(dataMap, config);
         return dataMap;
     }
 
-    private Filter<String> transformPatternToFilter(String tablePattern) {
-        Filter<String> table;
-        if (tablePattern == null) {
-            table = NULL;
-        } else {
-            table = include(tablePattern.replaceAll("%", ".*"));
-        }
-        return table;
-    }
-
     /**
      * Performs database reverse engineering based on the specified config 
      * and fills the specified
@@ -756,7 +745,7 @@ public class DbLoader {
        public void load(DataMap dataMap, DbLoaderConfiguration config) throws 
SQLException {
 
                String[] tableTypes = config.getTableTypes() == null ? 
this.getDefaultTableTypes() : config.getTableTypes();
-               List<DbEntity> entities = loadDbEntities(dataMap, config, 
getTables(config, tableTypes));
+               List<DbEntity> entities = loadDbEntities(dataMap, 
getTables(config, tableTypes), config);
 
                if (entities != null) {
                        loadDbRelationships(dataMap, config, entities);
@@ -784,8 +773,8 @@ public class DbLoader {
     @Deprecated
     public void loadProceduresFromDB(String schemaPattern, String namePattern, 
DataMap dataMap) throws SQLException {
         DbLoaderConfiguration configuration = new DbLoaderConfiguration();
-        configuration.setFiltersConfig(new FiltersConfig(new EntityFilters(
-                new DbPath(null, schemaPattern), NULL, NULL, 
include(namePattern))));
+        configuration.setFiltersConfig(FiltersConfig.create(null, 
schemaPattern,
+                        TableFilter.include(null), new 
PatternFilter().include(namePattern)));
 
         loadProcedures(dataMap, configuration);
     }
@@ -896,21 +885,23 @@ public class DbLoader {
         Map<String, Procedure> procedures = new HashMap<String, Procedure>();
 
         FiltersConfig filters = config.getFiltersConfig();
-        for (DbPath dbPath : filters.pathsForQueries()) {
-            if (filters.filter(dbPath).procedureFilter().equals(NULL)) {
-                continue;
-            }
+        for (CatalogFilter catalogFilter : filters.catalogs) {
+            for (SchemaFilter schema : catalogFilter.schemas) {
+                if (filters.proceduresFilter(catalogFilter.name, schema.name) 
== null) {
+                    continue;
+                }
 
-            procedures.putAll(loadProcedures(filters, dbPath));
+                procedures.putAll(loadProcedures(filters, catalogFilter.name, 
schema.name));
+            }
         }
 
         return procedures;
     }
 
-    private Map<String, Procedure> loadProcedures(FiltersConfig filters, 
DbPath dbPath) throws SQLException {
+    private Map<String, Procedure> loadProcedures(FiltersConfig filters, 
String catalog, String schema) throws SQLException {
         Map<String, Procedure> procedures = new HashMap<String, Procedure>();
         // get procedures
-        ResultSet rs = getMetaData().getProcedures(dbPath.catalog, 
dbPath.schema, WILDCARD);
+        ResultSet rs = getMetaData().getProcedures(catalog, schema, WILDCARD);
         try {
             while (rs.next()) {
 
@@ -919,8 +910,7 @@ public class DbLoader {
                 procedure.setCatalog(rs.getString("PROCEDURE_CAT"));
                 procedure.setSchema(rs.getString("PROCEDURE_SCHEM"));
 
-                if (filters.filter(new DbPath(procedure.getCatalog(), 
procedure.getSchema()))
-                                .procedureFilter().isInclude(procedure)) {
+                if (filters.proceduresFilter(procedure.getCatalog(), 
procedure.getSchema()).isInclude(name)) {
                     LOGGER.info("skipping Cayenne PK procedure: " + name);
                     continue;
                 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/f438729f/cayenne-server/src/main/java/org/apache/cayenne/access/loader/BooleanNameFilter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/loader/BooleanNameFilter.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/access/loader/BooleanNameFilter.java
deleted file mode 100644
index c816af8..0000000
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/access/loader/BooleanNameFilter.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*****************************************************************
- *   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.access.loader;
-
-/**
- * @since 3.2.
- */
-public class BooleanNameFilter implements NameFilter {
-    private final boolean isInclude;
-
-    public BooleanNameFilter(boolean isInclude) {
-        this.isInclude = isInclude;
-    }
-
-    @Override
-    public boolean isIncluded(String string) {
-        return this.isInclude;
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/f438729f/cayenne-server/src/main/java/org/apache/cayenne/access/loader/DbLoaderConfiguration.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/loader/DbLoaderConfiguration.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/access/loader/DbLoaderConfiguration.java
index 6c6a628..8360837 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/access/loader/DbLoaderConfiguration.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/access/loader/DbLoaderConfiguration.java
@@ -18,10 +18,9 @@
  ****************************************************************/
 package org.apache.cayenne.access.loader;
 
-import org.apache.cayenne.access.loader.filters.DbPath;
-import org.apache.cayenne.access.loader.filters.EntityFilters;
-import org.apache.cayenne.access.loader.filters.FilterFactory;
+import org.apache.cayenne.access.loader.filters.TableFilter;
 import org.apache.cayenne.access.loader.filters.FiltersConfig;
+import org.apache.cayenne.access.loader.filters.PatternFilter;
 
 /**
  * @since 4.0
@@ -99,7 +98,7 @@ public class DbLoaderConfiguration {
     public FiltersConfig getFiltersConfig() {
         if (filtersConfig == null) {
             // this case is used often in tests where config not initialized 
properly
-            return new FiltersConfig(new EntityFilters(new DbPath(), 
FilterFactory.TRUE, FilterFactory.TRUE, FilterFactory.TRUE));
+            return FiltersConfig.create(null, null, TableFilter.everything(), 
PatternFilter.INCLUDE_NOTHING);
         }
         return filtersConfig;
     }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/f438729f/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/CatalogFilter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/CatalogFilter.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/CatalogFilter.java
new file mode 100644
index 0000000..830c9c8
--- /dev/null
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/CatalogFilter.java
@@ -0,0 +1,62 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+package org.apache.cayenne.access.loader.filters;
+
+import java.util.Arrays;
+
+/**
+* @since 4.0.
+*/
+public class CatalogFilter {
+    public final String name;
+    public final SchemaFilter[] schemas;
+
+    public CatalogFilter(String name, SchemaFilter... schemas) {
+        if (schemas == null || schemas.length == 0) {
+            throw new IllegalArgumentException("schemas(" + 
Arrays.toString(schemas) + ") can't be null or empty");
+        }
+
+        this.name = name;
+        this.schemas = schemas;
+    }
+
+    public SchemaFilter getSchema(String schema) {
+        for (SchemaFilter schemaFilter : schemas) {
+            if (schemaFilter.name == null || schemaFilter.name.equals(schema)) 
{
+                return schemaFilter;
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    public String toString() {
+        return toString(new StringBuilder(), "").toString();
+    }
+
+    public StringBuilder toString(StringBuilder res, String prefix) {
+        res.append(prefix).append("Catalog: ").append(name).append("\n");
+        for (SchemaFilter schema : schemas) {
+            schema.toString(res, prefix + "  ");
+        }
+
+        return res;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/f438729f/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/DbPath.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/DbPath.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/DbPath.java
deleted file mode 100644
index 813ce94..0000000
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/DbPath.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*****************************************************************
- *   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.access.loader.filters;
-
-import java.util.regex.Pattern;
-
-/**
-* @since 4.0.
-*/
-public class DbPath implements Comparable<DbPath> {
-
-    public static final DbPath EMPTY = new DbPath();
-
-    public static final String SEPARATOR = "/";
-    public final String catalog;
-    public final String schema;
-    public final String tablePattern;
-
-    private final String path;
-
-    public DbPath() {
-        this(null, null, null);
-    }
-
-    public DbPath(String catalog) {
-        this(catalog, null, null);
-    }
-
-    public DbPath(String catalog, String schema) {
-        this(catalog, schema, null);
-    }
-
-    public DbPath(String catalog, String schema, String tablePattern) {
-        this.catalog = prepareValue(catalog);
-        this.schema = prepareValue(schema);
-        this.tablePattern = prepareValue(tablePattern);
-
-        this.path = join(this.catalog, this.schema, this.tablePattern);
-    }
-
-    private static String join(String first, String second) {
-        if (second == null || second.equals("%")) {
-            return first;
-        } else {
-            return escapeNull(first) + SEPARATOR + second;
-        }
-    }
-
-    private static String join(String catalog, String schema, String table) {
-        String join = join(catalog, join(schema, table));
-        return escapeNull(join);
-    }
-
-    private static String escapeNull(String join) {
-        return join == null ? "%" : join;
-    }
-
-    private String prepareValue(String value) {
-        return value == null ? null : value.trim();
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-
-        DbPath dbPath = (DbPath) o;
-
-        return path.equals(dbPath.path);
-    }
-
-
-    @Override
-    public int hashCode() {
-        return path.hashCode();
-    }
-
-    @Override
-    public String toString() {
-        return path;
-    }
-
-    @Override
-    public int compareTo(DbPath o) {
-        return path.compareTo(o.path);
-    }
-
-    public boolean isCover(String catalog, String schema) {
-        return isCover(catalog, schema, null);
-    }
-
-    public boolean isCover(String catalog, String schema, String table) {
-        if (this.catalog == null && catalog == null) {
-            return schemaCover(schema, table);
-        } else if (this.catalog == null) {
-            return schemaCover(schema, table);
-        } else {
-            return this.catalog.equalsIgnoreCase(catalog) && 
schemaCover(schema, table);
-        }
-    }
-
-    private boolean schemaCover(String schema, String table) {
-        if (this.schema == null && schema == null) {
-            return tableCover(table);
-        } else if (this.schema == null) {
-            return tableCover(table);
-        } else {
-            return this.schema.equalsIgnoreCase(schema) && tableCover(table);
-        }
-    }
-
-    private boolean tableCover(String table) {
-        return this.tablePattern == null
-                || table != null && Pattern.compile(this.tablePattern, 
Pattern.CASE_INSENSITIVE).matcher(table).matches();
-    }
-
-    public boolean isCover(DbPath dbPath) {
-        if (dbPath == null) {
-            throw new IllegalArgumentException("dbPath can't be null");
-        }
-        return isCover(dbPath.catalog, dbPath.schema, dbPath.tablePattern);
-    }
-
-    public DbPath merge(DbPath path) {
-        if (this.isCover(path)) {
-            return this;
-        } else if (path.isCover(this)) {
-            return path;
-        } else {
-            return null;
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/f438729f/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/EntityFilters.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/EntityFilters.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/EntityFilters.java
deleted file mode 100644
index 42321e2..0000000
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/EntityFilters.java
+++ /dev/null
@@ -1,424 +0,0 @@
-/*****************************************************************
- *   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.access.loader.filters;
-
-import org.apache.cayenne.map.DbAttribute;
-import org.apache.cayenne.map.DbEntity;
-import org.apache.cayenne.map.Procedure;
-import org.apache.cayenne.util.EqualsBuilder;
-import org.apache.cayenne.util.HashCodeBuilder;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-import static org.apache.cayenne.access.loader.filters.FilterFactory.*;
-import static org.apache.commons.lang.StringUtils.isBlank;
-
-/**
- * @since 3.2.
- */
-public class EntityFilters {
-
-    private static final Log LOG = LogFactory.getLog(Filter.class);
-
-    private final DbPath dbPath;
-
-    private final Filter<String> tableFilters;
-    private final Filter<String> columnFilters;
-    private final Filter<String> proceduresFilters;
-
-
-    public EntityFilters(DbPath dbPath) {
-        this(dbPath, NULL, NULL, NULL);
-    }
-    public EntityFilters(DbPath dbPath, Filter<String> tableFilters, 
Filter<String> columnFilters, Filter<String> proceduresFilters) {
-        this.dbPath = dbPath;
-        this.tableFilters = set(tableFilters);
-        this.columnFilters = set(columnFilters);
-        this.proceduresFilters = set(proceduresFilters);
-    }
-
-    public boolean isEmpty() {
-        return (tableFilters == null || NULL.equals(tableFilters))
-                && (columnFilters == null || NULL.equals(columnFilters))
-                && (proceduresFilters == null || 
NULL.equals(proceduresFilters));
-    }
-
-    public DbPath getDbPath() {
-        return dbPath;
-    }
-
-    private Filter<String> set(Filter<String> tableFilters) {
-        return tableFilters == null ? NULL : tableFilters;
-    }
-
-    public Filter<DbEntity> tableFilter() {
-        return new DbEntityFilter(dbPath, tableFilters);
-    }
-
-    public Filter<DbAttribute> columnFilter() {
-        return new DbAttributeFilter(dbPath, columnFilters);
-    }
-
-    public Filter<Procedure> procedureFilter() {
-        return new ProcedureFilter(dbPath, proceduresFilters);
-    }
-
-    public EntityFilters join(EntityFilters filter) {
-        if (filter == null) {
-            return this;
-        }
-
-        DbPath path;
-        if (this.dbPath == null) {
-            path = filter.dbPath;
-        } else if (filter.dbPath == null) {
-            path = this.dbPath;
-        } else {
-            path = this.dbPath.merge(filter.dbPath);
-        }
-
-        return new EntityFilters(path,
-                this.tableFilters.join(filter.tableFilters),
-                this.columnFilters.join(filter.columnFilters),
-                this.proceduresFilters.join(filter.proceduresFilters));
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder res = new StringBuilder();
-        res.append(dbPath).append(":\n");
-        if (tableFilters != null) {
-            res.append("    Table: ").append(tableFilters).append("\n");
-        }
-
-        if (columnFilters != null) {
-            res.append("    Column: ").append(columnFilters).append("\n");
-        }
-
-        if (proceduresFilters != null) {
-            res.append("    Procedures: 
").append(proceduresFilters).append("\n");
-        }
-
-        return res.toString();
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == null) {
-            return false;
-        }
-        if (obj == this) {
-            return true;
-        }
-        if (obj.getClass() != getClass()) {
-            return false;
-        }
-
-        EntityFilters rhs = (EntityFilters) obj;
-        return new EqualsBuilder()
-                .append(this.dbPath, rhs.dbPath)
-                .append(this.tableFilters, rhs.tableFilters)
-                .append(this.columnFilters, rhs.columnFilters)
-                .append(this.proceduresFilters, rhs.proceduresFilters)
-                .isEquals();
-    }
-
-    @Override
-    public int hashCode() {
-        return new HashCodeBuilder()
-                .append(dbPath)
-                .append(tableFilters)
-                .append(columnFilters)
-                .append(proceduresFilters)
-                .toHashCode();
-    }
-
-    /**
-     * @param <T>
-     */
-    private abstract static class EntityFilter<T> implements Filter<T> {
-
-        private final DbPath dbPath;
-        private final Filter<String> filter;
-
-        protected EntityFilter(DbPath dbPath, Filter<String> filter) {
-            this.dbPath = dbPath;
-            this.filter = filter;
-        }
-
-        DbPath getDbPath() {
-            return dbPath;
-        }
-
-        Filter<String> getFilter() {
-            return filter;
-        }
-
-        @Override
-        public EntityFilter<T> join(Filter<T> filter) {
-            if (!(filter instanceof EntityFilter)) {
-                throw new IllegalArgumentException("Unexpected filter join '" 
+ this + "' and '" + filter + "'");
-            }
-
-            EntityFilter<T> entityFilter = (EntityFilter<T>) filter;
-            DbPath dbPath;
-            if (entityFilter.dbPath.isCover(this.dbPath)) {
-                dbPath = entityFilter.dbPath;
-            } else if (this.dbPath.isCover(entityFilter.dbPath)) {
-                dbPath = this.dbPath;
-            } else {
-                throw new IllegalArgumentException("Attempt to merge filter 
with incompatible tuples: '" + entityFilter.dbPath + "'");
-            }
-
-            return create(dbPath, this.filter.join(entityFilter.filter));
-        }
-
-        @Override
-        public String toString() {
-            return getClass().getSimpleName() + " (" + dbPath + " -> " + 
filter + ")";
-        }
-
-        public abstract EntityFilter<T> create(DbPath dbPath, Filter<String> 
filter);
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) {
-                return true;
-            }
-
-            if (o == null) {
-                return false;
-            }
-
-            if (o instanceof Filter) { // TODO
-                return filter.equals(o);
-            }
-
-            if (getClass() != o.getClass()) {
-                return false;
-            }
-
-            return filter.equals(((EntityFilter) o).filter);
-
-        }
-
-        @Override
-        public int hashCode() {
-            return filter.hashCode();
-        }
-    }
-
-    private static class DbEntityFilter extends EntityFilter<DbEntity> {
-
-        public DbEntityFilter(DbPath dbPath, Filter<String> filter) {
-            super(dbPath, filter);
-        }
-
-        @Override
-        public boolean isInclude(DbEntity obj) {
-            if (LOG.isTraceEnabled()
-                    && this.getDbPath().isCover(obj.getCatalog(), 
obj.getSchema())) {
-
-                LOG.warn("Attempt to apply inconvenient filter '" + this + "' 
for dbEntity '" + obj + "'");
-            }
-
-            return this.getFilter().isInclude(obj.getName());
-        }
-
-        @Override
-        public EntityFilter<DbEntity> create(DbPath dbPath, Filter<String> 
filter) {
-            return new DbEntityFilter(dbPath, filter);
-        }
-    }
-
-    private static class DbAttributeFilter extends EntityFilter<DbAttribute> {
-
-        public DbAttributeFilter(DbPath dbPath, Filter<String> filter) {
-            super(dbPath, filter);
-        }
-
-        @Override
-        public boolean isInclude(DbAttribute obj) {
-            DbEntity entity = obj.getEntity();
-            if (LOG.isTraceEnabled()
-                    && this.getDbPath().isCover(entity.getCatalog(), 
entity.getSchema(), entity.getName())) {
-
-                LOG.warn("Attempt to apply inconvenient filter '" + this + "' 
for attribute '" + obj + "'");
-            }
-
-            return this.getFilter().isInclude(obj.getName());
-        }
-
-        @Override
-        public EntityFilter<DbAttribute> create(DbPath dbPath, Filter<String> 
filter) {
-            return new DbAttributeFilter(dbPath, filter);
-        }
-    }
-
-    private static class ProcedureFilter extends EntityFilter<Procedure> {
-
-        public ProcedureFilter(DbPath dbPath, Filter<String> filter) {
-            super(dbPath, filter);
-        }
-
-        @Override
-        public boolean isInclude(Procedure obj) {
-            if (LOG.isTraceEnabled()
-                    && this.getDbPath().isCover(obj.getCatalog(), 
obj.getSchema())) {
-
-                LOG.warn("Attempt to apply inconvenient filter '" + this + "' 
for procedure '" + obj + "'");
-            }
-            return this.getFilter().isInclude(obj.getName());
-        }
-
-        @Override
-        public EntityFilter<Procedure> create(DbPath dbPath, Filter<String> 
filter) {
-            return new ProcedureFilter(dbPath, filter);
-        }
-    }
-
-
-    public static final class Builder {
-        private String catalog;
-        private String schema;
-
-        private Filter<String> tableFilters = TRUE;
-        private Filter<String> columnFilters = TRUE;
-        private Filter<String> proceduresFilters = NULL;
-
-        public Builder() {
-        }
-
-        public Builder catalog(String catalog) {
-            this.catalog = catalog;
-            return this;
-        }
-
-        public Builder schema(String schema) {
-            this.schema = schema;
-            return this;
-        }
-
-        public String schema() {
-            return schema;
-        }
-
-        public Builder includeTables(String tableFilters) {
-            if (isBlank(tableFilters)) {
-                return this;
-            }
-
-            this.tableFilters = includeFilter(tableFilters, this.tableFilters);
-            return this;
-        }
-
-        public Builder includeColumns(String columnFilters) {
-            if (isBlank(columnFilters)) {
-                return this;
-            }
-
-            this.columnFilters = includeFilter(columnFilters, 
this.columnFilters);
-            return this;
-        }
-
-        public Builder includeProcedures(String proceduresFilters) {
-            if (isBlank(proceduresFilters)) {
-                return this;
-            }
-
-            this.proceduresFilters = includeFilter(proceduresFilters, 
this.proceduresFilters);
-            return this;
-        }
-
-        private Filter<String> includeFilter(String tableFilters, 
Filter<String> filter) {
-            for (String pattern : tableFilters.split(",")) {
-                filter = filter.join(include(transform(pattern)));
-            }
-
-            return filter;
-        }
-
-        public Builder excludeTables(String tableFilters) {
-            if (isBlank(tableFilters)) {
-                return this;
-            }
-
-            this.tableFilters = excludeFilter(tableFilters, this.tableFilters);
-            return this;
-        }
-
-        public Builder excludeColumns(String columnFilters) {
-            if (isBlank(columnFilters)) {
-                return this;
-            }
-
-            this.columnFilters = excludeFilter(columnFilters, 
this.columnFilters);
-            return this;
-        }
-
-        public Builder excludeProcedures(String proceduresFilters) {
-            if (isBlank(proceduresFilters)) {
-                return this;
-            }
-
-            this.proceduresFilters = excludeFilter(proceduresFilters, 
this.proceduresFilters);
-            return this;
-        }
-
-        private Filter<String> excludeFilter(String tableFilters, 
Filter<String> filter) {
-            for (String pattern : tableFilters.split(",")) {
-                filter = filter.join(exclude(transform(pattern)));
-            }
-
-            return filter;
-        }
-
-        private static String transform(String pattern) {
-            return "^" + pattern.replaceAll("[*?]", ".$0") + "$";
-        }
-
-        public Filter<String> tableFilters() {
-            return tableFilters;
-        }
-
-        public Filter<String> columnFilters() {
-            return columnFilters;
-        }
-
-        public Filter<String> proceduresFilters() {
-            return proceduresFilters;
-        }
-
-        public void setTableFilters(Filter<String> tableFilters) {
-            this.tableFilters = tableFilters;
-        }
-
-        public void setColumnFilters(Filter<String> columnFilters) {
-            this.columnFilters = columnFilters;
-        }
-
-        public void setProceduresFilters(Filter<String> proceduresFilters) {
-            this.proceduresFilters = proceduresFilters;
-        }
-
-        public EntityFilters build() {
-            return new EntityFilters(new DbPath(catalog, schema), 
tableFilters, columnFilters, proceduresFilters);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/f438729f/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/ExcludeFilter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/ExcludeFilter.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/ExcludeFilter.java
deleted file mode 100644
index 3db193e..0000000
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/ExcludeFilter.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*****************************************************************
- *   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.access.loader.filters;
-
-import java.util.regex.Pattern;
-
-/**
- * @since 4.0
- */
-public class ExcludeFilter extends IncludeFilter {
-
-    ExcludeFilter(Pattern pattern) {
-        super(pattern);
-    }
-
-    @Override
-    public boolean isInclude(String obj) {
-        return !super.isInclude(obj);
-    }
-
-    @Override
-    public String toString() {
-        return "-(" + super.getPattern() + ')';
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/f438729f/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/Filter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/Filter.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/Filter.java
deleted file mode 100644
index bee1d1d..0000000
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/Filter.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*****************************************************************
- *   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.access.loader.filters;
-
-/**
- * @since 4.0
- */
-public interface Filter<T> {
-
-    boolean isInclude(T obj);
-
-    Filter<T> join(Filter<T> filter);
-
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/f438729f/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/FilterFactory.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/FilterFactory.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/FilterFactory.java
deleted file mode 100644
index 88f3d8d..0000000
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/FilterFactory.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*****************************************************************
- *   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.access.loader.filters;
-
-import java.util.regex.Pattern;
-
-/**
- * @since 3.2.
- */
-public class FilterFactory {
-
-    public static Filter<String> TRUE = new Filter<String>() {
-        @Override
-        public boolean isInclude(String obj) {
-            return true;
-        }
-
-        @Override
-        public Filter<String> join(Filter<String> filter) {
-            return filter == null || NULL.equals(filter) ? this : filter;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            return this == o || o != null && getClass() == o.getClass();
-        }
-
-        @Override
-        public String toString() {
-            return "true";
-        }
-    };
-
-    public static Filter<String> NULL = new Filter<String>() {
-
-        @Override
-        public boolean isInclude(String obj) {
-            return false;
-        }
-
-        @Override
-        public Filter<String> join(Filter<String> filter) {
-            return filter == null ? this : filter;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            return this == o || o != null && getClass() == o.getClass();
-        }
-
-        @Override
-        public String toString() {
-            return "null";
-        }
-    };
-
-    public static Filter<String> include(String tablePattern) {
-        return new IncludeFilter(pattern(tablePattern));
-    }
-
-    public static Filter<String> exclude(String tablePattern) {
-        return new ExcludeFilter(pattern(tablePattern));
-    }
-
-    public static Filter<String> list(Filter<String> ... filters) {
-        Filter<String> res = NULL;
-        for (Filter<String> filter : filters) {
-            res = res.join(filter);
-        }
-        return res;
-    }
-
-    public static Pattern pattern(String tablePattern) {
-        return Pattern.compile(tablePattern, Pattern.CASE_INSENSITIVE);
-    }
-
-
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/f438729f/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/FiltersConfig.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/FiltersConfig.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/FiltersConfig.java
index c59da15..5f69501 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/FiltersConfig.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/FiltersConfig.java
@@ -1,179 +1,62 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
 package org.apache.cayenne.access.loader.filters;
 
 import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
 
 /**
- * @since 4.0
+ * @since 4.0.
  */
 public class FiltersConfig {
 
-    private final List<DbPath> dbPaths;
-    private final Map<DbPath, EntityFilters> filters;
+    public final CatalogFilter[] catalogs;
 
-    private List<DbPath> pathsForQueries;
-
-    public FiltersConfig(EntityFilters ... filters) {
-        this(Arrays.asList(filters));
-    }
-
-    public FiltersConfig(Collection<EntityFilters> filters) {
-        this.dbPaths = new LinkedList<DbPath>();
-        this.filters = new HashMap<DbPath, EntityFilters>();
-        for (EntityFilters filter : filters) {
-            if (filter == null) {
-                continue;
-            }
-
-            DbPath path = filter.getDbPath();
-            if (this.dbPaths.contains(path)) {
-                this.filters.put(path, this.filters.get(path).join(filter));
-                continue;
-            }
-
-            this.dbPaths.add(path);
-            this.filters.put(path, filter);
+    public FiltersConfig(CatalogFilter ... catalogs) {
+        if (catalogs == null || catalogs.length == 0) {
+            throw new IllegalArgumentException("catalogs(" + 
Arrays.toString(catalogs) + ") can't be null or empty");
         }
 
-        Collections.sort(this.dbPaths);
+        this.catalogs = catalogs;
     }
 
-    /**
-     * Used for loading tables and procedures, it's aim avoid unnecessary 
queries by compacting pairs of
-     * (Catalog, Schema)
-     *
-     * Example:
-     * <ul>
-     *      <li>"aaa", null</li>
-     *      <li>"aaa", "11"</li>
-     *      <li>"aa", null</li>
-     *      <li>"aa", "a"</li>
-     *      <li>"aa", "aa"</li>
-     *      <li>"aa", "aa"</li>
-     * </ul>
-     *
-     * Should return
-     * <ul>
-     *      <li>"aa", null</li>
-     *      <li>"aaa", null</li>
-     * </ul>
-     * For more examples please see tests.
-     *
-     * @return list of pairs (Catalog, Schema) for which getTables and 
getProcedures should be called
-     *
-     **/
-    public List<DbPath> pathsForQueries() {
-        if (pathsForQueries != null) {
-            return pathsForQueries;
-        }
-
-        pathsForQueries = new LinkedList<DbPath>();
-        if (filters.isEmpty()) {
-            return pathsForQueries;
-        }
-
-        boolean save = true;
-        String catalog = null;
-        String schema = null;
-        for (DbPath path : dbPaths) {
-            if (save || catalog != null && !catalog.equals(path.catalog)) {
-                catalog = path.catalog;
-                schema = null;
-                save = true;
-            }
-
-            if (save || schema != null && !schema.equals(path.schema)) {
-                schema = path.schema;
-                save = true;
-            }
-
-            if (save) {
-                save = false;
-                pathsForQueries.add(new DbPath(catalog, schema));
-            }
-        }
-
-        return pathsForQueries;
+    public PatternFilter proceduresFilter(String catalog, String schema) {
+        return getSchemaFilter(catalog, schema).procedures;
     }
 
-    /**
-     * TODO comment
-     *
-     * Return filters that applicable for path (filters which path covering 
path passed in method)
-     * */
-    public EntityFilters filter(DbPath path) {
-        EntityFilters res = new EntityFilters(path);
-        for (Map.Entry<DbPath, EntityFilters> entry : filters.entrySet()) {
-            if (entry.getKey().isCover(path)) {
-                res = res.join(entry.getValue());
-            }
-        }
-
-        return res;
+    public TableFilter tableFilter(String catalog, String schema) {
+        return getSchemaFilter(catalog, schema).tables;
     }
 
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
+    protected SchemaFilter getSchemaFilter(String catalog, String schema) {
+        CatalogFilter catalogFilter = getCatalog(catalog);
+        if (catalogFilter == null) {
+            return null;
         }
 
-        Map<DbPath, EntityFilters> filters = ((FiltersConfig) o).filters;
-        if (this.filters.size() != filters.size()) {
-            return false;
-        }
+        return catalogFilter.getSchema(schema);
+    }
 
-        for (Map.Entry<DbPath, EntityFilters> entry : this.filters.entrySet()) 
{
-            EntityFilters f = filters.get(entry.getKey());
-            if (f == null || !f.equals(entry.getValue())) {
-                return false;
+    protected CatalogFilter getCatalog(String catalog) {
+        for (CatalogFilter catalogFilter : catalogs) {
+            if (catalogFilter.name == null || 
catalogFilter.name.equals(catalog)) {
+                return catalogFilter;
             }
         }
-        return true;
+
+        return null;
     }
 
     @Override
     public String toString() {
-        StringBuilder res = new StringBuilder();
-        for (DbPath dbPath : dbPaths) {
-            res.append("    ").append(dbPath).append(" -> 
").append(filters.get(dbPath)).append("\n");
+        StringBuilder builder = new StringBuilder();
+        for (CatalogFilter catalog : catalogs) {
+            catalog.toString(builder, "");
         }
 
-        return res.toString();
-    }
-
-    @Override
-    public int hashCode() {
-        return filters.hashCode();
+        return builder.toString();
     }
 
-    public List<DbPath> getDbPaths() {
-        return dbPaths;
+    public static FiltersConfig create(String catalog, String schema, 
TableFilter tableFilter, PatternFilter procedures) {
+        return new FiltersConfig(
+                    new CatalogFilter(catalog,
+                        new SchemaFilter(schema, tableFilter, procedures)));
     }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/f438729f/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/IncludeFilter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/IncludeFilter.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/IncludeFilter.java
deleted file mode 100644
index 98fa87c..0000000
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/IncludeFilter.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*****************************************************************
- *   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.access.loader.filters;
-
-import java.util.regex.Pattern;
-
-/**
- * @since 4.0
- */
-public class IncludeFilter implements Filter<String> {
-
-    private final Pattern pattern;
-
-    IncludeFilter(Pattern pattern) {
-        this.pattern = pattern;
-    }
-
-    @Override
-    public boolean isInclude(String obj) {
-        return pattern.matcher(obj).matches();
-    }
-
-    @Override
-    public Filter join(Filter filter) {
-        return ListFilter.create(this, filter);
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-
-        if (o instanceof ListFilter) {
-            return o.equals(this);
-        }
-
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-
-        return pattern.toString().equals(((IncludeFilter) 
o).pattern.toString());
-
-    }
-
-    @Override
-    public int hashCode() {
-        return pattern.hashCode();
-    }
-
-    @Override
-    public String toString() {
-        return "+(" + pattern + ')';
-    }
-
-    protected Pattern getPattern() {
-        return pattern;
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/f438729f/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/IncludeTableFilter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/IncludeTableFilter.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/IncludeTableFilter.java
new file mode 100644
index 0000000..dcb144b
--- /dev/null
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/IncludeTableFilter.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.access.loader.filters;
+
+import java.util.regex.Pattern;
+
+/**
+* @since 4.0.
+*/
+public class IncludeTableFilter implements Comparable<IncludeTableFilter> {
+    public final Pattern pattern;
+
+    public final PatternFilter columnsFilter;
+
+    public IncludeTableFilter(String pattern) {
+        this(pattern, PatternFilter.INCLUDE_EVERYTHING);
+    }
+
+    public IncludeTableFilter(String pattern, PatternFilter columnsFilter) {
+        this.pattern = PatternFilter.pattern(pattern);
+        this.columnsFilter = columnsFilter;
+    }
+
+    public boolean isIncludeColumn (String name) {
+        return columnsFilter.isInclude(name);
+    }
+
+    @Override
+    public int compareTo(IncludeTableFilter o) {
+        if (pattern == null && o.pattern == null) {
+            return 0;
+        } else if (pattern == null) {
+            return 1;
+        } else if (o.pattern == null) {
+            return -1;
+        } else {
+            return pattern.pattern().compareTo(o.pattern.pattern());
+        }
+
+    }
+
+
+    @Override
+    public String toString() {
+        return toString(new StringBuilder(), "").toString();
+    }
+
+    protected StringBuilder toString(StringBuilder res, String prefix) {
+        res.append(prefix).append("Include: 
").append(String.valueOf(pattern)).append(" Columns: ");
+        columnsFilter.toString(res);
+        res.append("\n");
+
+        return res;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/f438729f/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/ListFilter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/ListFilter.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/ListFilter.java
deleted file mode 100644
index 7703dfb..0000000
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/ListFilter.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*****************************************************************
- *   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.access.loader.filters;
-
-import org.apache.commons.lang.StringUtils;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.LinkedList;
-
-import static org.apache.cayenne.access.loader.filters.FilterFactory.NULL;
-import static org.apache.cayenne.access.loader.filters.FilterFactory.TRUE;
-
-/**
- * @since 4.0
- */
-public class ListFilter<T> implements Filter<T> {
-
-    private final Collection<Filter<T>> filters;
-
-    public ListFilter(Collection<Filter<T>> filters) {
-        this.filters = filters;
-    }
-
-    @Override
-    public boolean isInclude(T obj) {
-        for (Filter<T> filter : filters) {
-            if (!filter.isInclude(obj)) {
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    @Override
-    public Filter<T> join(Filter<T> filter) {
-        LinkedList<Filter<T>> list = new LinkedList<Filter<T>>(filters);
-        if (TRUE.equals(filter) || NULL.equals(filter)) {
-            // Do nothing.
-        } else if (filter instanceof ListFilter) {
-            list.addAll(((ListFilter<T>) filter).filters);
-        } else {
-            list.add(filter);
-        }
-
-        return new ListFilter<T>(list);
-    }
-
-    public static <T> Filter<T> create(Filter<T> filter1, Filter<T> filter2) {
-        if (filter1 == null || TRUE.equals(filter1) || NULL.equals(filter1)) {
-            return filter2;
-        }
-
-        if (filter2 == null || TRUE.equals(filter2) || NULL.equals(filter2)) {
-            return filter1;
-        }
-
-        if (filter1 instanceof ListFilter) {
-            return filter1.join(filter2);
-        }
-
-        if (filter2 instanceof ListFilter) {
-            return filter2.join(filter1);
-        }
-
-        if (filter1.equals(filter2)) {
-            return filter1;
-        }
-
-        return new ListFilter<T>(Arrays.asList(filter1, filter2));
-    }
-
-    @Override
-    public String toString() {
-        return StringUtils.join(filters, " & ");
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-
-        if (o == null) {
-            return false;
-        }
-
-        if (getClass() != o.getClass()) {
-            if (o instanceof Filter && filters.size() == 1) {
-                return o.equals(filters.iterator().next());
-            } else {
-                return false;
-            }
-        }
-
-        ListFilter that = (ListFilter) o;
-        return filters != null ? filters.equals(that.filters) : that.filters 
== null;
-
-    }
-
-    @Override
-    public int hashCode() {
-        return filters != null ? filters.hashCode() : 0;
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/f438729f/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/OldFilterConfigBridge.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/OldFilterConfigBridge.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/OldFilterConfigBridge.java
new file mode 100644
index 0000000..647d477
--- /dev/null
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/OldFilterConfigBridge.java
@@ -0,0 +1,150 @@
+/*****************************************************************
+ *   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.access.loader.filters;
+
+import static org.apache.commons.lang.StringUtils.isBlank;
+
+/**
+ * @since 3.2.
+ */
+public class OldFilterConfigBridge {
+
+    private String catalog;
+    private String schema;
+
+    private String includeTableFilters;
+    private String includeColumnFilters;
+    private String includeProceduresFilters;
+    private String excludeTableFilters;
+    private String excludeColumnFilters;
+    private String excludeProceduresFilters;
+
+    private boolean loadProcedures;
+
+    public OldFilterConfigBridge() {
+    }
+
+    public OldFilterConfigBridge catalog(String catalog) {
+        this.catalog = catalog;
+        return this;
+    }
+
+    public String catalog() {
+        return catalog;
+    }
+
+    public OldFilterConfigBridge schema(String schema) {
+        this.schema = schema;
+        return this;
+    }
+
+    public String schema() {
+        return schema;
+    }
+
+    public OldFilterConfigBridge includeTables(String tableFilters) {
+        if (isBlank(tableFilters)) {
+            return this;
+        }
+
+        this.includeTableFilters = transform(tableFilters);
+        return this;
+    }
+
+    public OldFilterConfigBridge includeColumns(String columnFilters) {
+        if (isBlank(columnFilters)) {
+            return this;
+        }
+
+        this.includeColumnFilters = transform(columnFilters);
+        return this;
+    }
+
+    public OldFilterConfigBridge includeProcedures(String proceduresFilters) {
+        if (isBlank(proceduresFilters)) {
+            return this;
+        }
+
+        this.includeProceduresFilters = transform(proceduresFilters);
+        return this;
+    }
+
+    public OldFilterConfigBridge excludeTables(String tableFilters) {
+        if (isBlank(tableFilters)) {
+            return this;
+        }
+
+        this.excludeTableFilters = transform(tableFilters);
+        return this;
+    }
+
+    public OldFilterConfigBridge excludeColumns(String columnFilters) {
+        if (isBlank(columnFilters)) {
+            return this;
+        }
+
+        this.excludeColumnFilters = transform(columnFilters);
+        return this;
+    }
+
+    public OldFilterConfigBridge excludeProcedures(String proceduresFilters) {
+        if (isBlank(proceduresFilters)) {
+            return this;
+        }
+
+        this.excludeProceduresFilters = transform(proceduresFilters);
+        return this;
+    }
+
+    private static String transform(String pattern) {
+        return "^" + pattern.replaceAll("[*?]", ".$0") + "$";
+    }
+
+    public void setProceduresFilters(boolean loadProcedures) {
+        this.loadProcedures = loadProcedures;
+    }
+
+    public String getIncludeTableFilters() {
+        return includeTableFilters;
+    }
+
+    public String getIncludeColumnFilters() {
+        return includeColumnFilters;
+    }
+
+    public String getIncludeProceduresFilters() {
+        return includeProceduresFilters;
+    }
+
+    public String getExcludeTableFilters() {
+        return excludeTableFilters;
+    }
+
+    public String getExcludeColumnFilters() {
+        return excludeColumnFilters;
+    }
+
+    public String getExcludeProceduresFilters() {
+        return excludeProceduresFilters;
+    }
+
+    public boolean isLoadProcedures() {
+        return loadProcedures;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/f438729f/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/PatternFilter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/PatternFilter.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/PatternFilter.java
new file mode 100644
index 0000000..83789ba
--- /dev/null
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/PatternFilter.java
@@ -0,0 +1,167 @@
+/*****************************************************************
+ *   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.access.loader.filters;
+
+import org.apache.commons.lang.StringUtils;
+
+import java.util.Comparator;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.regex.Pattern;
+
+/**
+ * @since 4.0
+ */
+public class PatternFilter {
+
+    public static final PatternFilter INCLUDE_EVERYTHING = new PatternFilter() 
{
+
+        @Override
+        public boolean isInclude(String obj) {
+            return true;
+        }
+
+        @Override
+        public StringBuilder toString(StringBuilder res) {
+            return res.append("ALL");
+        }
+    };
+
+    public static final PatternFilter INCLUDE_NOTHING = new PatternFilter() {
+        @Override
+        public boolean isInclude(String obj) {
+            return false;
+        }
+
+        @Override
+        public StringBuilder toString(StringBuilder res) {
+            return res.append("NONE");
+        }
+    };
+
+    public static final Comparator<Pattern> PATTERN_COMPARATOR = new 
Comparator<Pattern>() {
+        @Override
+        public int compare(Pattern o1, Pattern o2) {
+            return o1.pattern().compareTo(o2.pattern());
+        }
+    };
+
+    private final SortedSet<Pattern> includes;
+    private final SortedSet<Pattern> excludes;
+
+    public PatternFilter() {
+        this.includes = new TreeSet<Pattern>(PATTERN_COMPARATOR);
+        this.excludes = new TreeSet<Pattern>(PATTERN_COMPARATOR);
+    }
+
+    public PatternFilter include(Pattern p) {
+        includes.add(p);
+
+        return this;
+    }
+
+    public PatternFilter exclude(Pattern p) {
+        excludes.add(p);
+
+        return this;
+    }
+
+    public static Pattern pattern(String pattern) {
+        if (pattern == null) {
+            return null;
+        }
+        return Pattern.compile(pattern, Pattern.CASE_INSENSITIVE);
+    }
+
+    public PatternFilter include(String p) {
+        return include(pattern(p));
+    }
+
+    public PatternFilter exclude(String p) {
+        return exclude(pattern(p));
+    }
+
+    public boolean isInclude(String obj) {
+        boolean include = includes.isEmpty();
+        for (Pattern p : includes) {
+            if (p.matcher(obj).matches()) {
+                include = true;
+                break;
+            }
+        }
+
+        if (!include) {
+            return false;
+        }
+
+        for (Pattern p : excludes) {
+            if (p.matcher(obj).matches()) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+
+        PatternFilter filter = (PatternFilter) o;
+        return includes.equals(filter.includes)
+                && excludes.equals(filter.excludes);
+    }
+
+    @Override
+    public int hashCode() {
+        return includes.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return toString(new StringBuilder()).toString();
+    }
+
+    public StringBuilder toString(StringBuilder res) {
+        if (includes.isEmpty()) {
+            // Do nothing.
+        } else if (includes.size() > 1) {
+            res.append("(").append(StringUtils.join(includes, " OR 
")).append(")");
+        } else {
+            res.append(includes.first().pattern());
+        }
+
+        if (!excludes.isEmpty()) {
+            res.append(" AND NOT (").append(StringUtils.join(includes, " OR 
")).append(")");
+        }
+
+        return res;
+    }
+
+    public boolean isEmpty() {
+        return includes.isEmpty() && excludes.isEmpty();
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/f438729f/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/SchemaFilter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/SchemaFilter.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/SchemaFilter.java
new file mode 100644
index 0000000..2331938
--- /dev/null
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/SchemaFilter.java
@@ -0,0 +1,49 @@
+/*****************************************************************
+ *   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.access.loader.filters;
+
+/**
+* @since 4.0.
+*/
+public class SchemaFilter {
+    public final String name;
+    public final TableFilter tables;
+    public final PatternFilter procedures;
+
+    public SchemaFilter(String name, TableFilter tables, PatternFilter 
procedures) {
+        this.name = name;
+        this.tables = tables;
+        this.procedures = procedures;
+    }
+
+    @Override
+    public String toString() {
+        return toString(new StringBuilder(), "").toString();
+    }
+
+    protected StringBuilder toString(StringBuilder res, String prefix) {
+        res.append(prefix).append("Schema: ").append(name).append("\n");
+        tables.toString(res, prefix + "  ");
+
+        res.append(prefix).append("  Procedures: ");
+        procedures.toString(res).append("\n");
+
+        return res;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/f438729f/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/TableFilter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/TableFilter.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/TableFilter.java
new file mode 100644
index 0000000..fa0ad2f
--- /dev/null
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/TableFilter.java
@@ -0,0 +1,112 @@
+/*****************************************************************
+ *   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.access.loader.filters;
+
+import org.apache.commons.lang.StringUtils;
+
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.regex.Pattern;
+
+/**
+ * TableFilter contain at least one IncludeTable always
+ *
+ */
+public class TableFilter {
+
+    private final SortedSet<IncludeTableFilter> includes;
+    private final SortedSet<Pattern> excludes;
+
+    /**
+     * Includes can contain only One includetable
+     *
+     * @param includes
+     * @param excludes
+     */
+    public TableFilter(SortedSet<IncludeTableFilter> includes, 
SortedSet<Pattern> excludes) {
+        if (includes.isEmpty()) {
+            throw new IllegalArgumentException("TableFilter should contain at 
least one IncludeTableFilter always " +
+                    "and it is builder responsibility. If you need table 
filter without includes, use EmptyTableFilter");
+        }
+
+        this.includes = includes;
+        this.excludes = excludes;
+    }
+
+    /**
+     * Return filter for columns in case we should take this table
+     *
+     * @param tableName
+     * @return
+     */
+    public PatternFilter isIncludeTable(String tableName) {
+        IncludeTableFilter include = null;
+        for (IncludeTableFilter p : includes) {
+            if (p.pattern == null || p.pattern.matcher(tableName).matches()) {
+                include = p;
+                break;
+            }
+        }
+
+        if (include == null) {
+            return null;
+        }
+
+        for (Pattern p : excludes) {
+            if (p.matcher(tableName).matches()) {
+                return null;
+            }
+        }
+
+        return include.columnsFilter;
+    }
+
+    public static TableFilter include(String tablePattern) {
+        TreeSet<IncludeTableFilter> includes = new 
TreeSet<IncludeTableFilter>();
+        includes.add(new IncludeTableFilter(tablePattern == null ? null : 
tablePattern.replaceAll("%", ".*")));
+
+        return new TableFilter(includes, new TreeSet<Pattern>());
+    }
+
+    public static TableFilter everything() {
+        TreeSet<IncludeTableFilter> includes = new 
TreeSet<IncludeTableFilter>();
+        includes.add(new IncludeTableFilter(null));
+
+        return new TableFilter(includes, new TreeSet<Pattern>());
+    }
+
+    @Override
+    public String toString() {
+        return toString(new StringBuilder(), "").toString();
+    }
+
+    protected StringBuilder toString(StringBuilder res, String prefix) {
+        res.append(prefix).append("Tables: ").append("\n");
+
+        for (IncludeTableFilter include : includes) {
+            include.toString(res, prefix + "  ");
+        }
+
+        if (!excludes.isEmpty()) {
+            res.append(prefix).append("  ").append(StringUtils.join(excludes, 
" OR ")).append("\n");
+        }
+
+        return res;
+    }
+}

Reply via email to