You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ibatis.apache.org by cb...@apache.org on 2006/01/22 04:13:47 UTC

svn commit: r371173 - in /ibatis/trunk/java/mapper/mapper2: src/com/ibatis/sqlmap/engine/mapper/ src/com/ibatis/sqlmap/engine/mapper/metadata/ test/com/ibatis/sqlmap/mapper/

Author: cbegin
Date: Sat Jan 21 19:13:39 2006
New Revision: 371173

URL: http://svn.apache.org/viewcvs?rev=371173&view=rev
Log:
Implemented stats based name matcher for use with auto crud ops generation

Added:
    ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/
    ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/Canonicalizer.java
    ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/Match.java
    ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/MatchCalculator.java
    ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/NameMatcher.java
    ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/metadata/
    ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/metadata/Column.java
    ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/metadata/Database.java
    ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/metadata/DatabaseFactory.java
    ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/metadata/Table.java
    ibatis/trunk/java/mapper/mapper2/test/com/ibatis/sqlmap/mapper/
    ibatis/trunk/java/mapper/mapper2/test/com/ibatis/sqlmap/mapper/CanonicalizerTest.java
    ibatis/trunk/java/mapper/mapper2/test/com/ibatis/sqlmap/mapper/DatabaseMetadataTest.java
    ibatis/trunk/java/mapper/mapper2/test/com/ibatis/sqlmap/mapper/MatchCalculatorTest.java
    ibatis/trunk/java/mapper/mapper2/test/com/ibatis/sqlmap/mapper/NameMatcherTest.java

Added: ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/Canonicalizer.java
URL: http://svn.apache.org/viewcvs/ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/Canonicalizer.java?rev=371173&view=auto
==============================================================================
--- ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/Canonicalizer.java (added)
+++ ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/Canonicalizer.java Sat Jan 21 19:13:39 2006
@@ -0,0 +1,177 @@
+package com.ibatis.sqlmap.engine.mapper;
+
+import java.util.StringTokenizer;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Iterator;
+
+public class Canonicalizer {
+
+  public Map buildCanonicalMap(String[] originals) {
+    return buildCanonicalMap(originals, null);
+  }
+  public Map buildCanonicalMap(String[] originals, String parentName) {
+    Map map = new HashMap();
+    for (int i=0; i < originals.length; i++) {
+      map.put(originals[i], originals[i]);
+    }
+    upperCase(map);
+    removeUnderscores(map);
+    removePKFK(map);
+    removePrefixes(map);
+    removeSuffixes(map);
+    removePluralization(map);
+    removeParentName(map, parentName);
+    return map;
+  }
+
+  private void removeParentName(Map map, String parentName) {
+    if (parentName != null) {
+      parentName = parentName.toUpperCase();
+      Iterator i = map.keySet().iterator();
+      while (i.hasNext()) {
+        String original = (String) i.next();
+        String canonical = (String) map.get(original);
+        if (canonical.startsWith(parentName)) {
+          map.put(original, canonical.substring(parentName.length()));
+        }
+        if (canonical.endsWith(parentName)) {
+          map.put(original, canonical.substring(0, canonical.length() - parentName.length()));
+        }
+      }
+    }
+  }
+
+  private void upperCase(Map map) {
+    Iterator i = map.keySet().iterator();
+    while (i.hasNext()) {
+      String original = (String) i.next();
+      String canonical = (String) map.get(original);
+      map.put(original, canonical.toUpperCase());
+    }
+  }
+
+  private void removePKFK(Map map) {
+    Iterator i = map.keySet().iterator();
+    while (i.hasNext()) {
+      String original = (String) i.next();
+      String canonical = (String) map.get(original);
+      if (canonical.startsWith("PK")) {
+        map.put(original, canonical.substring(2));
+      } else if (canonical.startsWith("FK")) {
+        map.put(original, canonical.substring(2));
+      }
+      if (canonical.endsWith("PK")) {
+        map.put(original, canonical.substring(0, canonical.length() - 2));
+      } else if (canonical.endsWith("FK")) {
+        map.put(original, canonical.substring(0, canonical.length() - 2));
+      }
+    }
+  }
+
+  private void removePrefixes(Map map) {
+    int prefix = findPrefixLength(map);
+
+    if (prefix > 1) {
+      Iterator i = map.keySet().iterator();
+      while (i.hasNext()) {
+        String original = (String) i.next();
+        String canonical = (String) map.get(original);
+        map.put(original, canonical.substring(prefix));
+      }
+    }
+  }
+
+  private void removeSuffixes(Map map) {
+    int suffix = findSuffixLength(map);
+
+    if (suffix > 1) {
+      Iterator i = map.keySet().iterator();
+      while (i.hasNext()) {
+        String original = (String) i.next();
+        String canonical = (String) map.get(original);
+        map.put(original, canonical.substring(0, canonical.length() - suffix));
+      }
+    }
+  }
+
+  private int findPrefixLength(Map map) {
+    String[] originals = (String[])map.keySet().toArray(new String[map.keySet().size()]);
+    char[] samples = ((String) map.get(findShortestString(originals))).toCharArray();
+    int prefix = 0;
+    for (int i=0; i < samples.length; i++) {
+      for (int j=0; j < originals.length; j++) {
+        String original = originals[j];
+        String canonical = (String) map.get(original);
+        if (canonical.charAt(prefix) != samples[i]) {
+          return prefix;
+        }
+      }
+      prefix++;
+    }
+    if (prefix == samples.length) {
+      prefix = 0;
+    }
+    return prefix;
+  }
+
+  private int findSuffixLength(Map map) {
+    String[] originals = (String[])map.keySet().toArray(new String[map.keySet().size()]);
+    char[] samples = ((String) map.get(findShortestString(originals))).toCharArray();
+    int suffix = 0;
+    for (int i=0; i < samples.length; i++) {
+      for (int j=0; j < originals.length; j++) {
+        String original = originals[j];
+        String canonical = (String) map.get(original);
+        if (canonical.charAt(canonical.length() - suffix - 1) != samples[samples.length - i - 1]) {
+          return suffix;
+        }
+      }
+      suffix++;
+    }
+    if (suffix == samples.length) {
+      suffix = 0;
+    }
+    return suffix;
+  }
+
+  private String findShortestString(String[] originals) {
+    String shortest = originals[0];
+    for (int i=0; i < originals.length; i++) {
+      if (originals[i].length() < shortest.length()) {
+        shortest = originals[i];
+      }
+    }
+    return shortest;
+  }
+
+  private void removeUnderscores (Map map) {
+    Iterator i = map.keySet().iterator();
+    while (i.hasNext()) {
+      String original = (String) i.next();
+      String canonical = (String) map.get(original);
+      map.put(original, removeUnderscoresFromString(canonical));
+    }
+  }
+
+  private void removePluralization (Map map) {
+    Iterator i = map.keySet().iterator();
+    while (i.hasNext()) {
+      String original = (String) i.next();
+      String canonical = (String) map.get(original);
+      if (canonical.endsWith("S") || canonical.endsWith("s")) {
+        map.put(original, canonical.substring(0, canonical.length() - 1));
+      }
+    }
+  }
+
+  private String removeUnderscoresFromString (String original) {
+    StringBuffer canonical = new StringBuffer();
+    StringTokenizer parser = new StringTokenizer (original, "_", false);
+    while(parser.hasMoreTokens()) {
+      canonical.append(parser.nextToken());
+    }
+    return canonical.toString();
+  }
+
+}

Added: ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/Match.java
URL: http://svn.apache.org/viewcvs/ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/Match.java?rev=371173&view=auto
==============================================================================
--- ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/Match.java (added)
+++ ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/Match.java Sat Jan 21 19:13:39 2006
@@ -0,0 +1,32 @@
+package com.ibatis.sqlmap.engine.mapper;
+
+public class Match {
+
+  private String property;
+  private String field;
+  private double matchScore;
+
+  public Match(String property, String field, double matchScore) {
+    this.property = property;
+    this.field = field;
+    this.matchScore = matchScore;
+  }
+
+  public String getProperty() {
+    return property;
+  }
+
+  public String getField() {
+    return field;
+  }
+
+  public double getMatchScore() {
+    return matchScore;
+  }
+
+
+  public String toString() {
+    return property + " => " + field + " ("+matchScore+")";
+  }
+
+}

Added: ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/MatchCalculator.java
URL: http://svn.apache.org/viewcvs/ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/MatchCalculator.java?rev=371173&view=auto
==============================================================================
--- ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/MatchCalculator.java (added)
+++ ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/MatchCalculator.java Sat Jan 21 19:13:39 2006
@@ -0,0 +1,69 @@
+package com.ibatis.sqlmap.engine.mapper;
+
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Iterator;
+
+public class MatchCalculator {
+
+  private static final int SET_BEGIN = 2;
+  private static final int SET_END = 5;
+  private static final double MAX_VALUE = 1;
+
+  public MatchCalculator() {
+  }
+
+  public double calculateMatch (String first, String second) {
+
+    Set firstSet = buildSets(first);
+    Set secondSet = buildSets(second);
+
+    double value1 = compareSets(firstSet, secondSet);
+    double value2 = compareSets(secondSet, firstSet);
+    return (value1 + value2) / 2;
+  }
+
+  private Set buildSets(String string) {
+    Set set = new HashSet();
+    char[] chars = string.toUpperCase().toCharArray();
+    for (int i = SET_BEGIN; i <= SET_END; i++) {
+      setsOf(i, chars, set);
+    }
+    return set;
+  }
+
+  private void setsOf(int size, char[] chars, Set set) {
+    for (int i=0; i < chars.length - size + 1; i++) {
+      char[] group = new char[size];
+      for (int j=0; j < group.length; j++) {
+        group[j] = chars[i+j];
+      }
+      set.add(new String(group));
+    }
+  }
+
+  private double compareSets(Set firstSet, Set secondSet) {
+    double value = MAX_VALUE;
+    double interval = calculateInterval(firstSet, secondSet);
+    Iterator i = firstSet.iterator();
+    while (i.hasNext()) {
+      String group = (String)i.next();
+      if (!secondSet.contains(group)) {
+        value -= interval;
+      }
+    }
+    return value;
+  }
+
+  private double calculateInterval(Set firstSet, Set secondSet) {
+    double interval;
+    if (firstSet.size() > secondSet.size()) {
+      interval = MAX_VALUE / firstSet.size();
+    } else {
+      interval = MAX_VALUE / secondSet.size();
+    }
+    return interval;
+  }
+
+
+}

Added: ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/NameMatcher.java
URL: http://svn.apache.org/viewcvs/ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/NameMatcher.java?rev=371173&view=auto
==============================================================================
--- ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/NameMatcher.java (added)
+++ ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/NameMatcher.java Sat Jan 21 19:13:39 2006
@@ -0,0 +1,90 @@
+package com.ibatis.sqlmap.engine.mapper;
+
+import java.util.*;
+
+public class NameMatcher {
+
+  public Map matchNames(String[] properties, String[] fields) {
+    Map propertyMap = invertMap(buildCanonicalMap(properties));
+    Map fieldMap = invertMap(buildCanonicalMap(fields));
+
+    String[] canonicalProperties = setToStringArray(propertyMap.keySet());
+    String[] canonicalFields = setToStringArray(fieldMap.keySet());
+
+    // consider building in both directions
+    // consider tracking which fields have already been mapped to avoid duplicate mappings
+    List matchList = buildMatchList(canonicalProperties, canonicalFields);
+
+    Map matchedNames = new HashMap();
+    Iterator matches = matchList.iterator();
+    while(matches.hasNext()) {
+      Match match = (Match)matches.next();
+      String property = (String) propertyMap.get(match.getProperty());
+      String field = (String) fieldMap.get(match.getField());
+      matchedNames.put(property, field);
+    }
+    return matchedNames;
+  }
+
+  private Map buildCanonicalMap(String[] properties) {
+    return new Canonicalizer().buildCanonicalMap(properties);
+  }
+
+  private String[] setToStringArray(Set set) {
+    return (String[]) set.toArray(new String[set.size()]);
+  }
+
+  private List buildMatchList(String[] canonicalProperties, String[] canonicalFields) {
+    List matchList = new LinkedList();
+    MatchCalculator calc = new MatchCalculator();
+    for (int i = 0; i < canonicalProperties.length; i++) {
+      for (int j = 0; j < canonicalFields.length; j++) {
+        String prop = canonicalProperties[i];
+        String field = canonicalFields[j];
+        double score = calc.calculateMatch(prop, field);
+        Match match = new Match(prop, field, score);
+        matchList.add(match);
+      }
+    }
+    sortMatches(matchList);
+    removeDuplicatesAndLowScores (matchList);
+    return matchList;
+  }
+
+  private void removeDuplicatesAndLowScores(List matchList) {
+    Set usedProperties = new HashSet();
+    Set usedFields = new HashSet();
+    Iterator i = matchList.iterator();
+    while (i.hasNext()) {
+      Match m = (Match)i.next();
+      if (usedProperties.contains(m.getProperty()) || usedFields.contains(m.getField()) || m.getMatchScore() < 0.40) {
+        i.remove();
+      } else {
+        usedProperties.add(m.getProperty());
+        usedFields.add(m.getField());
+      }
+    }
+
+  }
+
+  private void sortMatches(List matchList) {
+    Collections.sort(matchList, new Comparator () {
+      public int compare(Object o1, Object o2) {
+        Match m1 = (Match) o1;
+        Match m2 = (Match) o2;
+        return m1.getMatchScore() < m2.getMatchScore() ? 1 : m1.getMatchScore() > m2.getMatchScore() ? -1 : 0;
+      }
+    });
+  }
+
+  private Map invertMap(Map original) {
+    Map inverse = new HashMap();
+    Iterator keys = original.keySet().iterator();
+    while (keys.hasNext()) {
+      Object key = keys.next();
+      Object value = original.get(key);
+      inverse.put(value, key);
+    }
+    return inverse;
+  }
+}

Added: ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/metadata/Column.java
URL: http://svn.apache.org/viewcvs/ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/metadata/Column.java?rev=371173&view=auto
==============================================================================
--- ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/metadata/Column.java (added)
+++ ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/metadata/Column.java Sat Jan 21 19:13:39 2006
@@ -0,0 +1,40 @@
+package com.ibatis.sqlmap.engine.mapper.metadata;
+
+public class Column {
+
+  private String name;
+  private int type;
+
+  public Column(String name, int type) {
+    this.name = name;
+    this.type = type;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public int getType() {
+    return type;
+  }
+
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+
+    final Column column = (Column) o;
+
+    if (type != column.type) return false;
+    if (name != null ? !name.equals(column.name) : column.name != null) return false;
+
+    return true;
+  }
+
+  public int hashCode() {
+    int result;
+    result = (name != null ? name.hashCode() : 0);
+    result = 29 * result + type;
+    return result;
+  }
+
+}

Added: ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/metadata/Database.java
URL: http://svn.apache.org/viewcvs/ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/metadata/Database.java?rev=371173&view=auto
==============================================================================
--- ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/metadata/Database.java (added)
+++ ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/metadata/Database.java Sat Jan 21 19:13:39 2006
@@ -0,0 +1,57 @@
+package com.ibatis.sqlmap.engine.mapper.metadata;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class Database {
+
+  private String catalog;
+  private String schema;
+
+  private Map tables = new HashMap();
+
+  public Database(String catalog, String schema) {
+    this.catalog = catalog;
+    this.schema = schema;
+  }
+
+  public String getCatalog() {
+    return catalog;
+  }
+
+  public String getSchema() {
+    return schema;
+  }
+
+  public void addTable(Table table) {
+    tables.put(table.getName(), table);
+  }
+
+  public Table getTable(String name) {
+    return (Table)tables.get(name);
+  }
+
+  public String[] getTableNames() {
+    return (String[])tables.keySet().toArray(new String[tables.size()]);
+  }
+  
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+
+    final Database database = (Database) o;
+
+    if (catalog != null ? !catalog.equals(database.catalog) : database.catalog != null) return false;
+    if (schema != null ? !schema.equals(database.schema) : database.schema != null) return false;
+
+    return true;
+  }
+
+  public int hashCode() {
+    int result;
+    result = (catalog != null ? catalog.hashCode() : 0);
+    result = 29 * result + (schema != null ? schema.hashCode() : 0);
+    return result;
+  }
+
+}

Added: ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/metadata/DatabaseFactory.java
URL: http://svn.apache.org/viewcvs/ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/metadata/DatabaseFactory.java?rev=371173&view=auto
==============================================================================
--- ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/metadata/DatabaseFactory.java (added)
+++ ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/metadata/DatabaseFactory.java Sat Jan 21 19:13:39 2006
@@ -0,0 +1,42 @@
+package com.ibatis.sqlmap.engine.mapper.metadata;
+
+import javax.sql.DataSource;
+import java.sql.SQLException;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.DatabaseMetaData;
+
+public class DatabaseFactory {
+
+  private DatabaseFactory() {
+  }
+
+  public static Database newDatabase (DataSource dataSource, String catalog, String schema) throws SQLException {
+    Database database = new Database (catalog, schema);
+    Connection conn = dataSource.getConnection();
+    ResultSet rs = null;
+    try {
+      DatabaseMetaData dbmd = conn.getMetaData();
+      rs = dbmd.getColumns(catalog, schema, null, null);
+      while (rs.next()) {
+        String tableName = rs.getString ("TABLE_NAME");
+        String columnName = rs.getString ("COLUMN_NAME");
+        int dataType = Integer.parseInt(rs.getString ("DATA_TYPE"));
+        Table table = database.getTable(tableName);
+        if (table == null) {
+          table = new Table(tableName);
+          database.addTable(table);
+        }
+        table.addColumn(new Column(columnName, dataType));
+      }
+    } finally {
+      try {
+        if (rs != null) rs.close();
+      } finally {
+        conn.close();
+      }
+    }
+    return database;
+  }
+
+}

Added: ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/metadata/Table.java
URL: http://svn.apache.org/viewcvs/ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/metadata/Table.java?rev=371173&view=auto
==============================================================================
--- ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/metadata/Table.java (added)
+++ ibatis/trunk/java/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapper/metadata/Table.java Sat Jan 21 19:13:39 2006
@@ -0,0 +1,47 @@
+package com.ibatis.sqlmap.engine.mapper.metadata;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class Table {
+
+  private String name;
+
+  private Map columns = new HashMap();
+
+  public Table(String name) {
+    this.name = name;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public void addColumn (Column col) {
+    columns.put(col.getName(), col);
+  }
+
+  public Column getColumn (String name) {
+    return (Column) columns.get(name);
+  }
+
+  public String[] getColumnNames () {
+    return (String[])columns.keySet().toArray(new String[columns.size()]);
+  }
+
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+
+    final Table table = (Table) o;
+
+    if (name != null ? !name.equals(table.name) : table.name != null) return false;
+
+    return true;
+  }
+
+  public int hashCode() {
+    return (name != null ? name.hashCode() : 0);
+  }
+
+}

Added: ibatis/trunk/java/mapper/mapper2/test/com/ibatis/sqlmap/mapper/CanonicalizerTest.java
URL: http://svn.apache.org/viewcvs/ibatis/trunk/java/mapper/mapper2/test/com/ibatis/sqlmap/mapper/CanonicalizerTest.java?rev=371173&view=auto
==============================================================================
--- ibatis/trunk/java/mapper/mapper2/test/com/ibatis/sqlmap/mapper/CanonicalizerTest.java (added)
+++ ibatis/trunk/java/mapper/mapper2/test/com/ibatis/sqlmap/mapper/CanonicalizerTest.java Sat Jan 21 19:13:39 2006
@@ -0,0 +1,104 @@
+package com.ibatis.sqlmap.mapper;
+
+import junit.framework.TestCase;
+import com.ibatis.sqlmap.engine.mapper.Canonicalizer;
+
+import java.util.Map;
+
+public class CanonicalizerTest extends TestCase {
+
+  public void testShouldRemoveUnderscoresFromName () {
+    Canonicalizer matcher = new Canonicalizer();
+    String original = "ACC_FIRST_NAME";
+    String expected = "ACCFIRSTNAME";
+    Map map = matcher.buildCanonicalMap (new String[]{original});
+    String canonical = (String) map.get(original);
+    assertEquals(expected, canonical);
+  }
+
+  public void testShouldUppercase () {
+    Canonicalizer matcher = new Canonicalizer();
+    String original = "AccFirstName";
+    String expected = "ACCFIRSTNAME";
+    Map map = matcher.buildCanonicalMap (new String[]{original});
+    String canonical = (String) map.get(original);
+    assertEquals(expected, canonical);
+  }
+
+  public void testShouldRemovePluralization () {
+    Canonicalizer matcher = new Canonicalizer();
+    String original = "ACCFIRSTNAMES";
+    String expected = "ACCFIRSTNAME";
+    Map map = matcher.buildCanonicalMap (new String[]{original});
+    String canonical = (String) map.get(original);
+    assertEquals(expected, canonical);
+  }
+
+  public void testShouldRemovePrefix () {
+    Canonicalizer matcher = new Canonicalizer();
+    String[] originals = new String[]{"ACC_FIRSTNAME", "ACC_LASTNAME", "ACC_BIRTHDATE", "ACC_LEVEL"};
+    String[] expected = new String[]{"FIRSTNAME", "LASTNAME", "BIRTHDATE", "LEVEL"};
+    Map map = matcher.buildCanonicalMap (originals);
+    for (int i=0; i < originals.length; i++) {
+      String canonical = (String) map.get(originals[i]);
+      assertEquals(expected[i], canonical);
+    }
+  }
+
+  public void testShouldRemoveSuffix () {
+    Canonicalizer matcher = new Canonicalizer();
+    String[] originals = new String[]{"FIRSTNAME_ACC", "LASTNAME_ACC", "BIRTHDATE_ACC", "LEVEL_ACC"};
+    String[] expected = new String[]{"FIRSTNAME", "LASTNAME", "BIRTHDATE", "LEVEL"};
+    Map map = matcher.buildCanonicalMap (originals);
+    for (int i=0; i < originals.length; i++) {
+      String canonical = (String) map.get(originals[i]);
+      assertEquals(expected[i], canonical);
+    }
+  }
+
+  public void testShouldRemovePKFK () {
+    Canonicalizer matcher = new Canonicalizer();
+    String[] originals = new String[]{"PK_FIRSTNAME", "LASTNAME_PK", "BIRTHDATE_FK", "FK_LEVEL"};
+    String[] expected = new String[]{"FIRSTNAME", "LASTNAME", "BIRTHDATE", "LEVEL"};
+    Map map = matcher.buildCanonicalMap (originals);
+    for (int i=0; i < originals.length; i++) {
+      String canonical = (String) map.get(originals[i]);
+      assertEquals(expected[i], canonical);
+    }
+  }
+
+  public void testShouldRemoveParentName () {
+    Canonicalizer matcher = new Canonicalizer();
+    String parentName = "Person";
+    String[] originals = new String[]{"Person_ID, FIRSTNAME", "LASTNAME", "BIRTHDATE", "LEVEL_Person"};
+    String[] expected = new String[]{"ID, FIRSTNAME", "LASTNAME", "BIRTHDATE", "LEVEL"};
+    Map map = matcher.buildCanonicalMap (originals, parentName);
+    for (int i=0; i < originals.length; i++) {
+      String canonical = (String) map.get(originals[i]);
+      assertEquals(expected[i], canonical);
+    }
+  }
+
+  public void testShouldNotRemovePrefixWhenOnlyColumn () {
+    Canonicalizer matcher = new Canonicalizer();
+    String[] originals = new String[]{"ACCFIRSTNAME"};
+    String[] expected = new String[]{"ACCFIRSTNAME"};
+    Map map = matcher.buildCanonicalMap (originals);
+    for (int i=0; i < originals.length; i++) {
+      String canonical = (String) map.get(originals[i]);
+      assertEquals(expected[i], canonical);
+    }
+  }
+
+  public void testShouldNotRemovePrefixWhenSomePrefixesDoNotMatch () {
+    Canonicalizer matcher = new Canonicalizer();
+    String[] originals = new String[]{"ACCFIRSTNAME", "ACCLASTNAME", "ACCBIRTHDATE", "XACCLEVEL"};
+    String[] expected = new String[]{"ACCFIRSTNAME", "ACCLASTNAME", "ACCBIRTHDATE", "XACCLEVEL"};
+    Map map = matcher.buildCanonicalMap (originals);
+    for (int i=0; i < originals.length; i++) {
+      String canonical = (String) map.get(originals[i]);
+      assertEquals(expected[i], canonical);
+    }
+  }
+
+}

Added: ibatis/trunk/java/mapper/mapper2/test/com/ibatis/sqlmap/mapper/DatabaseMetadataTest.java
URL: http://svn.apache.org/viewcvs/ibatis/trunk/java/mapper/mapper2/test/com/ibatis/sqlmap/mapper/DatabaseMetadataTest.java?rev=371173&view=auto
==============================================================================
--- ibatis/trunk/java/mapper/mapper2/test/com/ibatis/sqlmap/mapper/DatabaseMetadataTest.java (added)
+++ ibatis/trunk/java/mapper/mapper2/test/com/ibatis/sqlmap/mapper/DatabaseMetadataTest.java Sat Jan 21 19:13:39 2006
@@ -0,0 +1,37 @@
+package com.ibatis.sqlmap.mapper;
+
+import com.ibatis.sqlmap.engine.mapper.metadata.Database;
+import com.ibatis.sqlmap.engine.mapper.metadata.DatabaseFactory;
+import com.ibatis.sqlmap.engine.mapper.metadata.Table;
+import com.ibatis.sqlmap.BaseSqlMapTest;
+
+public class DatabaseMetadataTest extends BaseSqlMapTest {
+
+  protected void setUp() throws Exception {
+    initSqlMap("com/ibatis/sqlmap/maps/SqlMapConfig.xml", null);
+    initScript("scripts/account-init.sql");
+  }
+
+  public void testDatabaseMetaData() throws Exception {
+    Database db = DatabaseFactory.newDatabase(sqlMap.getDataSource(), null, null);
+
+    Table table = db.getTable("ACCOUNT");
+
+    assertNotNull(table);
+    assertEquals("ACCOUNT", table.getName());
+
+    String[] columnNames = table.getColumnNames();
+
+    assertEquals(8, columnNames.length);
+
+    assertNotNull(table.getColumn("ACC_BANNER_OPTION"));
+    assertNotNull(table.getColumn("ACC_EMAIL"));
+    assertNotNull(table.getColumn("ACC_FIRST_NAME"));
+    assertNotNull(table.getColumn("ACC_LAST_NAME"));
+    assertNotNull(table.getColumn("ACC_ID"));
+    assertNotNull(table.getColumn("ACC_DATE_ADDED"));
+    assertNotNull(table.getColumn("ACC_AGE"));
+    assertNotNull(table.getColumn("ACC_CART_OPTION"));
+  }
+
+}

Added: ibatis/trunk/java/mapper/mapper2/test/com/ibatis/sqlmap/mapper/MatchCalculatorTest.java
URL: http://svn.apache.org/viewcvs/ibatis/trunk/java/mapper/mapper2/test/com/ibatis/sqlmap/mapper/MatchCalculatorTest.java?rev=371173&view=auto
==============================================================================
--- ibatis/trunk/java/mapper/mapper2/test/com/ibatis/sqlmap/mapper/MatchCalculatorTest.java (added)
+++ ibatis/trunk/java/mapper/mapper2/test/com/ibatis/sqlmap/mapper/MatchCalculatorTest.java Sat Jan 21 19:13:39 2006
@@ -0,0 +1,36 @@
+package com.ibatis.sqlmap.mapper;
+
+import junit.framework.TestCase;
+import com.ibatis.sqlmap.engine.mapper.MatchCalculator;
+
+public class MatchCalculatorTest extends TestCase {
+
+  public void testWillNotFindMatchesCorrectlyWithoutMoreContext() {
+    assertTrue(isMatch("firstName", "LASTNAME"));
+    assertFalse(isMatch("lastName", "ACC_LAST_NAME"));
+    assertFalse(isMatch("lastName", "ACC_LAST_NAME_FK"));
+  }
+
+  public void testShouldFindMatchesWithinAGivenThreshold() {
+    assertTrue(isMatch("firstName", "FIRSTNAME"));
+    assertTrue(isMatch("firstName", "ACC_FIRSTNAME"));
+    assertTrue(isMatch("firstName", "ACC_FIRST_NAME"));
+    assertTrue(isMatch("firstName", "ACC_FIRST_NAME_FK"));
+    assertTrue(isMatch("id", "id"));
+
+    assertTrue(isMatch("lastName", "LASTNAME"));
+    assertTrue(isMatch("lastName", "ACC_LASTNAME"));
+
+    assertFalse(isMatch("firstName", "ACC_LASTNAME"));
+    assertFalse(isMatch("firstName", "ACC_LAST_NAME"));
+    assertFalse(isMatch("firstName", "ACC_LAST_NAME_FK"));
+    assertFalse(isMatch("id", "PersonId"));
+  }
+
+  private boolean isMatch(String first, String second) {
+    double matchBar = 0.55;
+    return new MatchCalculator().calculateMatch(first, second) >= matchBar;
+  }
+
+
+}

Added: ibatis/trunk/java/mapper/mapper2/test/com/ibatis/sqlmap/mapper/NameMatcherTest.java
URL: http://svn.apache.org/viewcvs/ibatis/trunk/java/mapper/mapper2/test/com/ibatis/sqlmap/mapper/NameMatcherTest.java?rev=371173&view=auto
==============================================================================
--- ibatis/trunk/java/mapper/mapper2/test/com/ibatis/sqlmap/mapper/NameMatcherTest.java (added)
+++ ibatis/trunk/java/mapper/mapper2/test/com/ibatis/sqlmap/mapper/NameMatcherTest.java Sat Jan 21 19:13:39 2006
@@ -0,0 +1,52 @@
+package com.ibatis.sqlmap.mapper;
+
+import com.ibatis.sqlmap.engine.mapper.NameMatcher;
+import junit.framework.TestCase;
+
+import java.util.Map;
+
+public class NameMatcherTest extends TestCase {
+
+  public void testShouldCorrectlyMatchMostOfTheseFairlyComplexNamePairs() {
+    String[] properties = new String[]{"id", "username", "orderDate", "shipAddress1", "shipAddress2", "shipCity", "shipState", "shipZip", "shipCountry", "billAddress1", "billAddress2", "billCity", "billState", "billZip", "billCountry", "courier", "totalPrice", "billToFirstName", "billToLastName", "shipToFirstName", "shipToLastName", "creditCard", "expiryDate", "cardType", "locale", "status"};
+    String[] fields = new String[]{"orderid", "userid", "orderdate", "shipaddr1", "shipaddr2", "shipcity", "shipstate", "shipzip", "shipcountry", "billaddr1", "billaddr2", "billcity", "billstate", "billzip", "billcountry", "courier", "totalprice", "billtofirstname", "billtolastname", "shiptofirstname", "shiptolastname", "creditcard", "exprdate", "cardtype", "locale"};
+    Map map = new NameMatcher().matchNames(fields, properties);
+
+    // unmatched
+    assertNull(map.get("orderid"));
+
+    // match collision, userid matches id better than orderid
+    assertEquals("id", map.get("userid"));
+
+    // good matches
+    assertEquals("cardType", map.get("cardtype"));
+    assertEquals("creditCard", map.get("creditcard"));
+    assertEquals("expiryDate", map.get("exprdate"));
+
+    assertEquals("totalPrice", map.get("totalprice"));
+    assertEquals("courier", map.get("courier"));
+    assertEquals("orderDate", map.get("orderdate"));
+    assertEquals("locale", map.get("locale"));
+
+    assertEquals("billToFirstName", map.get("billtofirstname"));
+    assertEquals("billToLastName", map.get("billtolastname"));
+    assertEquals("billAddress1", map.get("billaddr1"));
+    assertEquals("billAddress2", map.get("billaddr2"));
+    assertEquals("billCity", map.get("billcity"));
+    assertEquals("billState", map.get("billstate"));
+    assertEquals("billCountry", map.get("billcountry"));
+    assertEquals("billZip", map.get("billzip"));
+
+    assertEquals("shipToFirstName", map.get("shiptofirstname"));
+    assertEquals("shipToLastName", map.get("shiptolastname"));
+    assertEquals("shipAddress1", map.get("shipaddr1"));
+    assertEquals("shipAddress2", map.get("shipaddr2"));
+    assertEquals("shipCountry", map.get("shipcountry"));
+    assertEquals("shipZip", map.get("shipzip"));
+    assertEquals("shipCity", map.get("shipcity"));
+    assertEquals("shipState", map.get("shipstate"));
+  }
+
+}
+
+