[hibernate-commits] Hibernate SVN: r10573 - in trunk/HibernateExt/tools/src: java/org/hibernate/cfg/reveng java/org/hibernate/cfg/reveng/dialect java/org/hibernate/tool java/org/hibernate/tool/ant java/org/hibernate/tool/hbm2x java/org/hibernate/tool/hbm2x/pojo java/org/hibernate/tool/hbmlint java/org/hibernate/tool/hbmlint/detector templates/lint test/org/hibernate/tool test/org/hibernate/tool/ant test/org/hibernate/tool/hbm2x testsupport

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Thu Oct 12 13:24:29 EDT 2006


Author: max.andersen at jboss.com
Date: 2006-10-12 13:23:59 -0400 (Thu, 12 Oct 2006)
New Revision: 10573

Added:
   trunk/HibernateExt/tools/src/java/org/hibernate/tool/ant/HbmLintExporterTask.java
   trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbm2x/HbmLintExporter.java
   trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/
   trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/Detector.java
   trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/HbmLint.java
   trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/Issue.java
   trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/IssueCollector.java
   trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/detector/
   trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/detector/BadCachingDetector.java
   trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/detector/EntityModelDetector.java
   trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/detector/InstrumentationDetector.java
   trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/detector/RelationalModelDetector.java
   trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/detector/SchemaByMetaDataDetector.java
   trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/detector/ShadowedIdentifierDetector.java
   trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/detector/TableSelectorStrategy.java
   trunk/HibernateExt/tools/src/templates/lint/text-report.ftl
   trunk/HibernateExt/tools/src/test/org/hibernate/tool/hbm2x/IncrementalSchemaReadingTest.java
   trunk/HibernateExt/tools/src/testsupport/SchemaIssues.hbm.xml
Modified:
   trunk/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/JDBCReader.java
   trunk/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/SchemaSelection.java
   trunk/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/dialect/JDBCMetaDataDialect.java
   trunk/HibernateExt/tools/src/java/org/hibernate/tool/ant/Hbm2CfgXmlExporterTask.java
   trunk/HibernateExt/tools/src/java/org/hibernate/tool/ant/Hbm2DocExporterTask.java
   trunk/HibernateExt/tools/src/java/org/hibernate/tool/ant/HibernateToolTask.java
   trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbm2x/GenericExporter.java
   trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbm2x/pojo/EntityPOJOClass.java
   trunk/HibernateExt/tools/src/test/org/hibernate/tool/JDBCMetaDataBinderTestCase.java
   trunk/HibernateExt/tools/src/test/org/hibernate/tool/ToolAllTests.java
   trunk/HibernateExt/tools/src/test/org/hibernate/tool/ant/HibernateToolTest.java
   trunk/HibernateExt/tools/src/testsupport/anttest-build.xml
Log:


Modified: trunk/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/JDBCReader.java
===================================================================
--- trunk/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/JDBCReader.java	2006-10-12 17:23:52 UTC (rev 10572)
+++ trunk/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/JDBCReader.java	2006-10-12 17:23:59 UTC (rev 10573)
@@ -1,8 +1,12 @@
 package org.hibernate.cfg.reveng;
 
+import java.sql.Connection;
 import java.sql.DatabaseMetaData;
+import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.sql.Statement;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -19,6 +23,7 @@
 import org.hibernate.cfg.JDBCBinderException;
 import org.hibernate.cfg.reveng.dialect.MetaDataDialect;
 import org.hibernate.connection.ConnectionProvider;
+import org.hibernate.dialect.Dialect;
 import org.hibernate.exception.SQLExceptionConverter;
 import org.hibernate.mapping.Column;
 import org.hibernate.mapping.ForeignKey;
@@ -55,7 +60,7 @@
 		}
 	}
 		
-	public DatabaseCollector readDatabaseSchema(DatabaseCollector dbs, String catalog, String schema, ProgressListener progress) {
+	public List readDatabaseSchema(DatabaseCollector dbs, String catalog, String schema, ProgressListener progress) {
 		try {
 			getMetaDataDialect().configure(provider, sec);
 			revengStrategy.configure(provider, sec);
@@ -63,16 +68,17 @@
 			Set hasIndices = new HashSet();
 			
 			List schemaSelectors = revengStrategy.getSchemaSelections();
+			List foundTables = new ArrayList();
 			if(schemaSelectors==null) {
-				processTables(dbs, new SchemaSelection(catalog, schema), hasIndices, progress);
+				foundTables.addAll( processTables(dbs, new SchemaSelection(catalog, schema), hasIndices, progress) );
 			} else {
 				for (Iterator iter = schemaSelectors.iterator(); iter.hasNext();) {
 					SchemaSelection selection = (SchemaSelection) iter.next();
-					processTables(dbs, selection, hasIndices, progress);
+					foundTables.addAll( processTables(dbs, selection, hasIndices, progress) );
 				}
 			}
 			
-			Iterator tables = dbs.iterateTables();
+			Iterator tables = foundTables.iterator(); // not dbs.iterateTables() to avoid "double-read" of columns etc.
 			while ( tables.hasNext() ) {
 				Table table = (Table) tables.next();
 				processBasicColumns(table, progress);
@@ -82,33 +88,45 @@
 				}
 			}
 			
-			tables = dbs.iterateTables();
-			List fks = new ArrayList();
-			while ( tables.hasNext() ) {
-				Table table = (Table) tables.next();
-				// Done here after the basic process of collections as we might not have touched 
-				// all referenced tables (this ensure the columns are the same instances througout the basic JDBC derived model.
-				// after this stage it should be "ok" to divert from keeping columns in sync as it can be required if the same 
-				//column is used with different aliases in the ORM mapping.
-				ForeignKeysInfo foreignKeys = processForeignKeys(dbs, table, progress);
-				fks.add( foreignKeys );				  	   
-			}
+			tables = foundTables.iterator(); //dbs.iterateTables();
+			Map oneToManyCandidates = resolveForeignKeys( dbs, tables, progress );
 			
-			Map oneToManyCandidates = new HashMap();			
-			for (Iterator iter = fks.iterator(); iter.hasNext();) {
-				ForeignKeysInfo element = (ForeignKeysInfo) iter.next();
-				Map map = element.process( revengStrategy );
-				mergeMultiMap( oneToManyCandidates, map );
-			}
-			
 			dbs.setOneToManyCandidates(oneToManyCandidates);
 			
-			return dbs;
+			return foundTables;
 		} finally {
 			getMetaDataDialect().close();
 			revengStrategy.close();
 		}
 	}
+
+	/**
+	 * Iterates the tables and find all the foreignkeys that refers to something that is available inside the DatabaseCollector.
+	 * @param dbs
+	 * @param progress
+	 * @param tables
+	 * @return
+	 */
+	private Map resolveForeignKeys(DatabaseCollector dbs, Iterator tables, ProgressListener progress) {
+		List fks = new ArrayList();
+		while ( tables.hasNext() ) {
+			Table table = (Table) tables.next();
+			// Done here after the basic process of collections as we might not have touched 
+			// all referenced tables (this ensure the columns are the same instances througout the basic JDBC derived model.
+			// after this stage it should be "ok" to divert from keeping columns in sync as it can be required if the same 
+			//column is used with different aliases in the ORM mapping.
+			ForeignKeysInfo foreignKeys = processForeignKeys(dbs, table, progress);
+			fks.add( foreignKeys );				  	   
+		}
+		
+		Map oneToManyCandidates = new HashMap();			
+		for (Iterator iter = fks.iterator(); iter.hasNext();) {
+			ForeignKeysInfo element = (ForeignKeysInfo) iter.next();
+			Map map = element.process( revengStrategy ); // the actual foreignkey is created here.
+			mergeMultiMap( oneToManyCandidates, map );
+		}
+		return oneToManyCandidates;
+	}
 	
 	static class ForeignKeysInfo {
 		
@@ -416,7 +434,7 @@
 		return value.equals(tf);
 	}
 
-	private void processTables(DatabaseCollector dbs, SchemaSelection schemaSelection, Set hasIndices, ProgressListener progress) {
+	private Collection processTables(DatabaseCollector dbs, SchemaSelection schemaSelection, Set hasIndices, ProgressListener progress) {
 		Map tableRs = null;
 		Iterator tableIterator = null;
 		List tables = new ArrayList();
@@ -465,6 +483,7 @@
 			  }
 		  }
 		  
+		  List processedTables = new ArrayList();
 		  tableIterator = tables.iterator();
 		  while (tableIterator.hasNext() ) {
 			  tableRs = (Map) tableIterator.next();
@@ -503,6 +522,7 @@
 					  if(tableType.equals("TABLE")) {
 						  hasIndices.add(table);
 					  }
+					  processedTables.add( table );
 				  }
 				  else {
 					  log.debug("Ignoring table " + tableName + " of type " + tableType);
@@ -510,6 +530,7 @@
 			  }
 		  }
 		  
+		  return processedTables;
 	}
 
 	private void processBasicColumns(Table table, ProgressListener progress) {
@@ -803,7 +824,7 @@
 			}
 		}
 		
-		public DatabaseCollector readDatabaseSchema(DatabaseCollector dbs, String catalog, String schema) {
+		public List readDatabaseSchema(DatabaseCollector dbs, String catalog, String schema) {
 			return readDatabaseSchema(dbs, catalog, schema, new NoopProgressListener());
 		}
 		
@@ -827,5 +848,44 @@
 
 		protected String getSchemaForDBLookup(String schema) {
 			return schema==null?defaultSchema:schema;
-		}		
-}
+		}
+
+		public Set readSequences(String sql) {
+			Set sequences = new HashSet();
+			if (sql!=null) {
+				Connection connection = null;
+				try {
+				
+					connection = provider.getConnection();
+					Statement statement = null;
+					ResultSet rs = null;
+					try {
+						statement = connection.createStatement();
+						rs = statement.executeQuery(sql);
+
+						while ( rs.next() ) {
+							sequences.add( rs.getString(1).toLowerCase().trim() );
+						}
+					}
+					finally {
+						if (rs!=null) rs.close();
+						if (statement!=null) statement.close();
+					}
+
+				} catch (SQLException e) {
+					sec.convert(e, "Problem while closing connection", null);
+				}
+				finally {
+					if(connection!=null)
+						try {
+							provider.closeConnection( connection );
+						}
+						catch (SQLException e) {
+							sec.convert(e, "Problem while closing connection", null);
+						}
+				} 
+			}
+			return sequences;
+		}
+}		
+

Modified: trunk/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/SchemaSelection.java
===================================================================
--- trunk/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/SchemaSelection.java	2006-10-12 17:23:52 UTC (rev 10572)
+++ trunk/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/SchemaSelection.java	2006-10-12 17:23:59 UTC (rev 10573)
@@ -5,16 +5,20 @@
 	String matchCatalog;
 	String matchSchema;
 	String matchTable;
-	
-	public SchemaSelection(String catalog, String schema) {
+
+	public SchemaSelection(String catalog, String schema, String table) {
 		matchCatalog = catalog;
 		matchSchema = schema;
+		matchTable = table;
 	}
 
-	public SchemaSelection() {
-		
+	public SchemaSelection(String catalog, String schema) {
+		this(catalog, schema, null);
 	}
 
+	public SchemaSelection() {	}
+
+
 	public String getMatchCatalog() {
 		return matchCatalog;
 	}

Modified: trunk/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/dialect/JDBCMetaDataDialect.java
===================================================================
--- trunk/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/dialect/JDBCMetaDataDialect.java	2006-10-12 17:23:52 UTC (rev 10572)
+++ trunk/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/dialect/JDBCMetaDataDialect.java	2006-10-12 17:23:59 UTC (rev 10573)
@@ -7,9 +7,7 @@
 import java.util.Iterator;
 import java.util.Map;
 
-import org.hibernate.cfg.JDBCBinderException;
 import org.hibernate.mapping.Table;
-import org.hibernate.util.StringHelper;
 
 /**
  * MetaData dialect that uses standard JDBC for reading metadata.

Modified: trunk/HibernateExt/tools/src/java/org/hibernate/tool/ant/Hbm2CfgXmlExporterTask.java
===================================================================
--- trunk/HibernateExt/tools/src/java/org/hibernate/tool/ant/Hbm2CfgXmlExporterTask.java	2006-10-12 17:23:52 UTC (rev 10572)
+++ trunk/HibernateExt/tools/src/java/org/hibernate/tool/ant/Hbm2CfgXmlExporterTask.java	2006-10-12 17:23:59 UTC (rev 10573)
@@ -24,7 +24,7 @@
 	}
 	
 	public String getName() {
-		return "cfg2cfgxml (Generates hibernate.cfg.xml)";
+		return "hbm2cfgxml (Generates hibernate.cfg.xml)";
 	}
 	
 	protected Exporter configureExporter(Exporter exporter) {

Modified: trunk/HibernateExt/tools/src/java/org/hibernate/tool/ant/Hbm2DocExporterTask.java
===================================================================
--- trunk/HibernateExt/tools/src/java/org/hibernate/tool/ant/Hbm2DocExporterTask.java	2006-10-12 17:23:52 UTC (rev 10572)
+++ trunk/HibernateExt/tools/src/java/org/hibernate/tool/ant/Hbm2DocExporterTask.java	2006-10-12 17:23:59 UTC (rev 10573)
@@ -14,7 +14,7 @@
 	}
 
 	public String getName() {
-		return "cfg2doc (Generates html schema documentation)";
+		return "hbm2doc (Generates html schema documentation)";
 	}
 
 	protected Exporter createExporter() {

Added: trunk/HibernateExt/tools/src/java/org/hibernate/tool/ant/HbmLintExporterTask.java
===================================================================
--- trunk/HibernateExt/tools/src/java/org/hibernate/tool/ant/HbmLintExporterTask.java	2006-10-12 17:23:52 UTC (rev 10572)
+++ trunk/HibernateExt/tools/src/java/org/hibernate/tool/ant/HbmLintExporterTask.java	2006-10-12 17:23:59 UTC (rev 10573)
@@ -0,0 +1,21 @@
+package org.hibernate.tool.ant;
+
+import org.hibernate.tool.hbm2x.Exporter;
+import org.hibernate.tool.hbm2x.HbmLintExporter;
+
+public class HbmLintExporterTask extends ExporterTask {
+
+	public HbmLintExporterTask(HibernateToolTask parent) {
+		super( parent );
+	}
+
+	protected Exporter createExporter() {
+		return new HbmLintExporter();
+	}
+		
+
+	String getName() {
+		return "hbmlint (scans mapping for errors)";
+	}
+
+}

Modified: trunk/HibernateExt/tools/src/java/org/hibernate/tool/ant/HibernateToolTask.java
===================================================================
--- trunk/HibernateExt/tools/src/java/org/hibernate/tool/ant/HibernateToolTask.java	2006-10-12 17:23:52 UTC (rev 10572)
+++ trunk/HibernateExt/tools/src/java/org/hibernate/tool/ant/HibernateToolTask.java	2006-10-12 17:23:59 UTC (rev 10573)
@@ -129,6 +129,13 @@
 		return generator;
 	}
 	
+	public HbmLintExporterTask createHbmLint() {
+		HbmLintExporterTask generator = new HbmLintExporterTask(this);
+		generators.add(generator);
+		return generator;
+	}
+	
+	
 	/**
      * Set the classpath to be used when running the Java class
      *

Modified: trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbm2x/GenericExporter.java
===================================================================
--- trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbm2x/GenericExporter.java	2006-10-12 17:23:52 UTC (rev 10572)
+++ trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbm2x/GenericExporter.java	2006-10-12 17:23:59 UTC (rev 10573)
@@ -35,7 +35,7 @@
 	protected void doStart() {
 				
 		if(filePattern==null) throw new ExporterException("File pattern not set on " + this.getClass());
-		if(templateName==null) throw new ExporterException("Template pattern not set on " + this.getClass());
+		if(templateName==null) throw new ExporterException("Template name not set on " + this.getClass());
 		
 		if(filePattern.indexOf("{class-name}")>=0) {				
 			exportClasses();

Added: trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbm2x/HbmLintExporter.java
===================================================================
--- trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbm2x/HbmLintExporter.java	2006-10-12 17:23:52 UTC (rev 10572)
+++ trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbm2x/HbmLintExporter.java	2006-10-12 17:23:59 UTC (rev 10573)
@@ -0,0 +1,38 @@
+package org.hibernate.tool.hbm2x;
+
+import java.io.File;
+
+import org.hibernate.cfg.Configuration;
+import org.hibernate.tool.hbmlint.HbmLint;
+
+public class HbmLintExporter extends GenericExporter {
+
+    private static final String TEXT_REPORT_FTL = "lint/text-report.ftl";
+    
+    public HbmLintExporter() {
+    }
+    
+    public HbmLintExporter(Configuration cfg, File outputdir) {
+        super(cfg, outputdir);
+        
+    }
+
+    public void start() {
+    	// TODO: make non-hardwired 
+    	setFilePattern( "hbmlint-result.txt" );
+		setTemplateName( TEXT_REPORT_FTL );		
+    	super.start();
+    }
+	protected void setupContext() {
+		HbmLint hbmlint = HbmLint.createInstance();
+		hbmlint.analyze( getConfiguration() );
+		getProperties().put("lintissues", hbmlint.getResults());
+		super.setupContext();		
+	}
+	
+	public String getName() {
+		return "hbmlint";
+	}
+
+
+}

Modified: trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbm2x/pojo/EntityPOJOClass.java
===================================================================
--- trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbm2x/pojo/EntityPOJOClass.java	2006-10-12 17:23:52 UTC (rev 10572)
+++ trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbm2x/pojo/EntityPOJOClass.java	2006-10-12 17:23:59 UTC (rev 10573)
@@ -505,38 +505,7 @@
 						.append(", fetch=").append( getFetchType( property ) );
 				if ( collection.isInverse() ) {
 					annotation.append(", mappedBy=\"");
-					Iterator joinColumnsIt = collection.getKey().getColumnIterator();
-					Set joinColumns = new HashSet();
-					while ( joinColumnsIt.hasNext() ) {
-						joinColumns.add( joinColumnsIt.next() );
-					}
-					OneToMany oneToMany = (OneToMany) collection.getElement();
-					PersistentClass pc = cfg.getClassMapping( oneToMany.getReferencedEntityName() );
-					Iterator properties = pc.getPropertyClosureIterator();
-					//TODO we should check the table too
-					boolean isOtherSide = false;
-					mappedBy = "unresolved";
-					while ( ! isOtherSide && properties.hasNext() ) {
-						Property manyProperty = (Property) properties.next();
-						Value manyValue = manyProperty.getValue();
-						if ( manyValue != null && manyValue instanceof ManyToOne ) {
-							if ( joinColumns.size() == manyValue.getColumnSpan() ) {
-								isOtherSide = true;
-								Iterator it = manyValue.getColumnIterator();
-								while ( it.hasNext() ) {
-									Object column = it.next();
-									if (! joinColumns.contains( column ) ) {
-										isOtherSide = false;
-										break;
-									}
-								}
-								if (isOtherSide) {
-									mappedBy = manyProperty.getName();
-								}
-							}
-
-						}
-					}
+					mappedBy = getOneToManyMappedBy( cfg, collection );
 					annotation.append( mappedBy ).append("\"");
 				}
 				annotation.append(")");
@@ -551,40 +520,7 @@
 						.append(", fetch=").append( getFetchType( property ) );
 				if ( collection.isInverse() ) {
 					annotation.append(", mappedBy=\"");
-					Iterator joinColumnsIt = collection.getKey().getColumnIterator();
-					Set joinColumns = new HashSet();
-					while ( joinColumnsIt.hasNext() ) {
-						joinColumns.add( joinColumnsIt.next() );
-					}
-					ManyToOne manyToOne = (ManyToOne) collection.getElement();
-					PersistentClass pc = cfg.getClassMapping( manyToOne.getReferencedEntityName() );
-					Iterator properties = pc.getPropertyClosureIterator();
-					//TODO we should check the table too
-					boolean isOtherSide = false;
-					mappedBy = "unresolved";
-					while ( ! isOtherSide && properties.hasNext() ) {
-						Property collectionProperty = (Property) properties.next();
-						Value collectionValue = collectionProperty.getValue();
-						if ( collectionValue != null && collectionValue instanceof Collection ) {
-							Collection realCollectionValue = (Collection) collectionValue;
-							if ( ! realCollectionValue.isOneToMany() ) {
-								if ( joinColumns.size() == realCollectionValue.getElement().getColumnSpan() ) {
-									isOtherSide = true;
-									Iterator it = realCollectionValue.getElement().getColumnIterator();
-									while ( it.hasNext() ) {
-										Object column = it.next();
-										if (! joinColumns.contains( column ) ) {
-											isOtherSide = false;
-											break;
-										}
-									}
-									if (isOtherSide) {
-										mappedBy = collectionProperty.getName();
-									}
-								}
-							}
-						}
-					}
+					mappedBy = getManyToManyMappedBy( cfg, collection );
 					annotation.append( mappedBy ).append("\"");
 				}
 				annotation.append(")");
@@ -630,6 +566,82 @@
 		}
 		return annotation.toString();
 	}
+
+	private String getManyToManyMappedBy(Configuration cfg, Collection collection) {
+		String mappedBy;
+		Iterator joinColumnsIt = collection.getKey().getColumnIterator();
+		Set joinColumns = new HashSet();
+		while ( joinColumnsIt.hasNext() ) {
+			joinColumns.add( joinColumnsIt.next() );
+		}
+		ManyToOne manyToOne = (ManyToOne) collection.getElement();
+		PersistentClass pc = cfg.getClassMapping( manyToOne.getReferencedEntityName() );
+		Iterator properties = pc.getPropertyClosureIterator();
+		//TODO we should check the table too
+		boolean isOtherSide = false;
+		mappedBy = "unresolved";
+		while ( ! isOtherSide && properties.hasNext() ) {
+			Property collectionProperty = (Property) properties.next();
+			Value collectionValue = collectionProperty.getValue();
+			if ( collectionValue != null && collectionValue instanceof Collection ) {
+				Collection realCollectionValue = (Collection) collectionValue;
+				if ( ! realCollectionValue.isOneToMany() ) {
+					if ( joinColumns.size() == realCollectionValue.getElement().getColumnSpan() ) {
+						isOtherSide = true;
+						Iterator it = realCollectionValue.getElement().getColumnIterator();
+						while ( it.hasNext() ) {
+							Object column = it.next();
+							if (! joinColumns.contains( column ) ) {
+								isOtherSide = false;
+								break;
+							}
+						}
+						if (isOtherSide) {
+							mappedBy = collectionProperty.getName();
+						}
+					}
+				}
+			}
+		}
+		return mappedBy;
+	}
+
+	private String getOneToManyMappedBy(Configuration cfg, Collection collection) {
+		String mappedBy;
+		Iterator joinColumnsIt = collection.getKey().getColumnIterator();
+		Set joinColumns = new HashSet();
+		while ( joinColumnsIt.hasNext() ) {
+			joinColumns.add( joinColumnsIt.next() );
+		}
+		OneToMany oneToMany = (OneToMany) collection.getElement();
+		PersistentClass pc = cfg.getClassMapping( oneToMany.getReferencedEntityName() );
+		Iterator properties = pc.getPropertyClosureIterator();
+		//TODO we should check the table too
+		boolean isOtherSide = false;
+		mappedBy = "unresolved";
+		while ( ! isOtherSide && properties.hasNext() ) {
+			Property manyProperty = (Property) properties.next();
+			Value manyValue = manyProperty.getValue();
+			if ( manyValue != null && manyValue instanceof ManyToOne ) {
+				if ( joinColumns.size() == manyValue.getColumnSpan() ) {
+					isOtherSide = true;
+					Iterator it = manyValue.getColumnIterator();
+					while ( it.hasNext() ) {
+						Object column = it.next();
+						if (! joinColumns.contains( column ) ) {
+							isOtherSide = false;
+							break;
+						}
+					}
+					if (isOtherSide) {
+						mappedBy = manyProperty.getName();
+					}
+				}
+
+			}
+		}
+		return mappedBy;
+	}
 	
 	public boolean isSubclass() {
 		return clazz.getSuperclass()!=null; 

Added: trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/Detector.java
===================================================================
--- trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/Detector.java	2006-10-12 17:23:52 UTC (rev 10572)
+++ trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/Detector.java	2006-10-12 17:23:59 UTC (rev 10573)
@@ -0,0 +1,29 @@
+package org.hibernate.tool.hbmlint;
+
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Settings;
+
+public abstract class Detector {
+
+	private Configuration cfg;
+	private Settings settings;
+
+	public void initialize(Configuration cfg, Settings settings) {
+		this.cfg = cfg;
+		this.settings = settings;		
+	}
+	
+	protected Settings getSettings() {
+		return settings;
+	}
+	
+	protected Configuration getConfiguration() {
+		return cfg;
+	}
+
+	public void visit(Configuration cfg, IssueCollector collector) {
+		
+	}
+	
+	abstract public String getName();
+}

Added: trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/HbmLint.java
===================================================================
--- trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/HbmLint.java	2006-10-12 17:23:52 UTC (rev 10572)
+++ trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/HbmLint.java	2006-10-12 17:23:59 UTC (rev 10573)
@@ -0,0 +1,57 @@
+package org.hibernate.tool.hbmlint;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Settings;
+import org.hibernate.tool.hbmlint.detector.BadCachingDetector;
+import org.hibernate.tool.hbmlint.detector.InstrumentationDetector;
+import org.hibernate.tool.hbmlint.detector.SchemaByMetaDataDetector;
+import org.hibernate.tool.hbmlint.detector.ShadowedIdentifierDetector;
+
+public class HbmLint implements IssueCollector {
+
+	
+	final Detector[] detectors;
+	
+	public HbmLint(Detector[] detectors) {
+		this.detectors = detectors;
+	}
+	
+	List results = new ArrayList();
+	
+	public void analyze(Configuration cfg) {
+		
+		Settings settings = cfg.buildSettings();
+		
+		for (int i = 0; i < detectors.length; i++) {
+			detectors[i].initialize( cfg, settings );
+			detectors[i].visit(cfg, this);
+		}
+					
+	}
+	
+	/* (non-Javadoc)
+	 * @see org.hibernate.tool.hbmlint.IssueCollector#reportProblem(org.hibernate.tool.hbmlint.Issue)
+	 */
+	public void reportIssue(Issue analyze) {
+		results.add(analyze);
+	}
+	
+	public List getResults() {
+		return results;	
+	}
+
+	public static HbmLint createInstance() {
+		return new HbmLint( 
+			new Detector[] {
+					new BadCachingDetector(),
+					new InstrumentationDetector(),
+					new ShadowedIdentifierDetector(),
+					new SchemaByMetaDataDetector()
+			});
+		
+	}
+
+}

Added: trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/Issue.java
===================================================================
--- trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/Issue.java	2006-10-12 17:23:52 UTC (rev 10572)
+++ trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/Issue.java	2006-10-12 17:23:59 UTC (rev 10573)
@@ -0,0 +1,27 @@
+package org.hibernate.tool.hbmlint;
+
+public class Issue {
+
+	public static final int HIGH_PRIORITY = 100;
+	public static final int NORMAL_PRIORITY = 50;
+	public static final int LOW_PRIORITY = 0;
+
+	private final String type;
+	private final int priority;
+	
+	private final String description;
+
+	public Issue(String type, int priority, String description) {
+		this.description = description;
+		this.priority = priority;
+		this.type = type;
+	}
+	
+	public String toString() {
+		return type + ":" + description;
+	}
+
+	public String getDescription() {
+		return description;
+	}
+}

Added: trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/IssueCollector.java
===================================================================
--- trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/IssueCollector.java	2006-10-12 17:23:52 UTC (rev 10572)
+++ trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/IssueCollector.java	2006-10-12 17:23:59 UTC (rev 10573)
@@ -0,0 +1,7 @@
+package org.hibernate.tool.hbmlint;
+
+public interface IssueCollector {
+
+	public abstract void reportIssue(Issue analyze);
+
+}
\ No newline at end of file

Added: trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/detector/BadCachingDetector.java
===================================================================
--- trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/detector/BadCachingDetector.java	2006-10-12 17:23:52 UTC (rev 10572)
+++ trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/detector/BadCachingDetector.java	2006-10-12 17:23:59 UTC (rev 10573)
@@ -0,0 +1,36 @@
+package org.hibernate.tool.hbmlint.detector;
+
+import org.hibernate.cfg.Configuration;
+import org.hibernate.mapping.Collection;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Property;
+import org.hibernate.mapping.Value;
+import org.hibernate.tool.hbm2x.visitor.EntityNameFromValueVisitor;
+import org.hibernate.tool.hbmlint.Issue;
+import org.hibernate.tool.hbmlint.IssueCollector;
+
+public class BadCachingDetector extends EntityModelDetector {
+	
+	public String getName() {
+		return "cache";
+	}
+	public void visitProperty(Configuration configuration, PersistentClass clazz, Property property, IssueCollector collector) {
+		Value value = property.getValue();
+		
+		if(value instanceof Collection) {
+			Collection col = (Collection) value;
+			if(col.getCacheConcurrencyStrategy()!=null) { // caching is enabled
+				if (!col.getElement().isSimpleValue()) {
+					String entityName = (String) col.getElement().accept( new EntityNameFromValueVisitor() );
+
+					if(entityName!=null) {
+						PersistentClass classMapping = configuration.getClassMapping( entityName );
+						if(classMapping.getCacheConcurrencyStrategy()==null) {
+							collector.reportIssue( new Issue("CACHE_COLLECTION_NONCACHABLE_TARGET", Issue.HIGH_PRIORITY, "Entity '" + classMapping.getEntityName() +"' is referenced from the cache-enabled collection '" + col.getRole() + "' without the entity being cachable"));
+						}
+					}
+				}
+			}
+		}	
+	}
+}

Added: trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/detector/EntityModelDetector.java
===================================================================
--- trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/detector/EntityModelDetector.java	2006-10-12 17:23:52 UTC (rev 10572)
+++ trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/detector/EntityModelDetector.java	2006-10-12 17:23:59 UTC (rev 10573)
@@ -0,0 +1,39 @@
+package org.hibernate.tool.hbmlint.detector;
+
+import java.util.Iterator;
+
+import org.hibernate.cfg.Configuration;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Property;
+import org.hibernate.tool.hbmlint.Detector;
+import org.hibernate.tool.hbmlint.IssueCollector;
+
+public abstract class EntityModelDetector extends Detector {
+
+	public void visit(Configuration cfg, IssueCollector collector) {
+		for (Iterator iter = cfg.getClassMappings(); iter.hasNext();) {
+			PersistentClass clazz = (PersistentClass) iter.next();
+			this.visit(cfg, clazz, collector);				
+		}
+	}
+	
+	public void visit(Configuration cfg, PersistentClass clazz, IssueCollector collector) {
+		visitProperties( cfg, clazz, collector );
+	}
+
+	public void visitProperties(Configuration cfg, PersistentClass clazz, IssueCollector collector) {
+		if(clazz.hasIdentifierProperty()) {
+			this.visitProperty(getConfiguration(), clazz, clazz.getIdentifierProperty(), collector);								
+		}
+		Iterator propertyIterator = clazz.getPropertyIterator();
+		while ( propertyIterator.hasNext() ) {
+			Property property = (Property) propertyIterator.next();
+			this.visitProperty(getConfiguration(), clazz, property, collector);					
+			
+		}
+	}
+
+	public void visitProperty(Configuration configuration, PersistentClass clazz, Property property, IssueCollector collector) {
+		
+	}
+}

Added: trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/detector/InstrumentationDetector.java
===================================================================
--- trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/detector/InstrumentationDetector.java	2006-10-12 17:23:52 UTC (rev 10572)
+++ trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/detector/InstrumentationDetector.java	2006-10-12 17:23:59 UTC (rev 10573)
@@ -0,0 +1,81 @@
+package org.hibernate.tool.hbmlint.detector;
+
+import org.hibernate.MappingException;
+import org.hibernate.bytecode.cglib.BytecodeProviderImpl;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.cfg.Settings;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.tool.hbmlint.Issue;
+import org.hibernate.tool.hbmlint.IssueCollector;
+
+public class InstrumentationDetector extends EntityModelDetector {
+	
+	public String getName() {
+		return "instrument";
+	}
+	private boolean cglibEnabled;
+	private boolean javassistEnabled;
+	
+	public void initialize(Configuration cfg, Settings settings) {
+		super.initialize( cfg, settings );
+		
+		cglibEnabled = false;
+		javassistEnabled = false;
+		
+		if(Environment.getBytecodeProvider() instanceof BytecodeProviderImpl) {
+			cglibEnabled = true;
+		} else if(Environment.getBytecodeProvider() instanceof BytecodeProviderImpl) {
+			javassistEnabled = true;
+		}		
+	}
+	
+	public void visit(Configuration cfg, PersistentClass clazz, IssueCollector collector) {
+		Class mappedClass;
+
+		
+		try {
+			mappedClass = clazz.getMappedClass();
+		} catch(MappingException me) {
+			// ignore
+			return;
+		}
+
+		if(clazz.isLazy()) {
+			try {
+				mappedClass.getConstructor( new Class[0] );
+			}
+			catch (SecurityException e) {
+				// ignore
+			}
+			catch (NoSuchMethodException e) {
+				collector.reportIssue(new Issue("LAZY_NO_DEFAULT_CONSTRUCTOR",Issue.NORMAL_PRIORITY, "lazy='true' set for '" + clazz.getEntityName() +"', but class has no default constructor." ));
+				return;
+			}
+
+		} else if(cglibEnabled || javassistEnabled){
+			Class[] interfaces = mappedClass.getInterfaces();
+			boolean cglib = false;
+			boolean javaassist = false;
+			for (int i = 0; i < interfaces.length; i++) {
+				Class intface = interfaces[i];				
+				if(intface.getName().equals( "net.sf.cglib.transform.impl.InterceptFieldEnabled" )) {
+					cglib = true;
+				} else if(javassistEnabled && !intface.getName().equals( "org.hibernate.bytecode.javassist.FieldHandled" )) {
+					javaassist = true;
+				} 							
+			}
+			
+			if(cglibEnabled && !cglib) {
+				collector.reportIssue( new Issue("LAZY_NOT_INSTRUMENTED", Issue.HIGH_PRIORITY, "'" + clazz.getEntityName() + "' has lazy='false', but its class '" + mappedClass.getName() + "' has not been instrumented with cglib") );
+				return;
+			} else if (javassistEnabled && !javaassist) {
+				collector.reportIssue( new Issue("LAZY_NOT_INSTRUMENTED", Issue.HIGH_PRIORITY, "'" + clazz.getEntityName() + "' has lazy='false', but its class '" + mappedClass.getName() + "' has not been instrumented with javaassist") );
+				return;
+			} else {
+				// unknown bytecodeprovider...can't really check for that.
+			}
+			
+		}
+	}
+}

Added: trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/detector/RelationalModelDetector.java
===================================================================
--- trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/detector/RelationalModelDetector.java	2006-10-12 17:23:52 UTC (rev 10572)
+++ trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/detector/RelationalModelDetector.java	2006-10-12 17:23:59 UTC (rev 10573)
@@ -0,0 +1,41 @@
+package org.hibernate.tool.hbmlint.detector;
+
+import java.util.Iterator;
+
+import org.hibernate.cfg.Configuration;
+import org.hibernate.mapping.Column;
+import org.hibernate.mapping.Table;
+import org.hibernate.tool.hbmlint.Detector;
+import org.hibernate.tool.hbmlint.IssueCollector;
+
+public abstract class RelationalModelDetector extends Detector {
+
+	public void visit(Configuration cfg, IssueCollector collector) {
+		for (Iterator iter = cfg.getTableMappings(); iter.hasNext();) {
+			Table table = (Table) iter.next();
+			this.visit(cfg, table, collector);
+		}					
+	}
+
+
+	protected void visit(Configuration cfg, Table table, Column col, IssueCollector collector) {
+				
+	}
+
+	protected void visitColumns(Configuration cfg, Table table, IssueCollector collector) {
+		Iterator columnIter = table.getColumnIterator();
+		while ( columnIter.hasNext() ) {
+			Column col = ( Column ) columnIter.next();
+			this.visit( cfg, table, col, collector );
+		}		
+	}
+
+	/**
+	 * @return true if visit should continue down through the columns 
+	 */
+	protected void visit(Configuration cfg, Table table, IssueCollector collector) {
+		visitColumns(cfg, table, collector);
+	}
+	
+}
+

Added: trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/detector/SchemaByMetaDataDetector.java
===================================================================
--- trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/detector/SchemaByMetaDataDetector.java	2006-10-12 17:23:52 UTC (rev 10572)
+++ trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/detector/SchemaByMetaDataDetector.java	2006-10-12 17:23:59 UTC (rev 10573)
@@ -0,0 +1,266 @@
+package org.hibernate.tool.hbmlint.detector;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeMap;
+
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.cfg.JDBCReaderFactory;
+import org.hibernate.cfg.Settings;
+import org.hibernate.cfg.reveng.DefaultDatabaseCollector;
+import org.hibernate.cfg.reveng.DefaultReverseEngineeringStrategy;
+import org.hibernate.cfg.reveng.JDBCReader;
+import org.hibernate.cfg.reveng.JDBCToHibernateTypeHelper;
+import org.hibernate.cfg.reveng.SchemaSelection;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.Mapping;
+import org.hibernate.id.IdentifierGenerator;
+import org.hibernate.id.PersistentIdentifierGenerator;
+import org.hibernate.mapping.Collection;
+import org.hibernate.mapping.Column;
+import org.hibernate.mapping.IdentifierCollection;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.RootClass;
+import org.hibernate.mapping.Table;
+import org.hibernate.tool.hbmlint.Issue;
+import org.hibernate.tool.hbmlint.IssueCollector;
+import org.hibernate.util.StringHelper;
+
+public class SchemaByMetaDataDetector extends RelationalModelDetector {
+
+	public String getName() {
+		return "schema";
+	}
+	
+	JDBCReader reader;
+
+	private TableSelectorStrategy tableSelector;
+
+	private DefaultDatabaseCollector dbc;
+
+	private Dialect dialect;
+
+	private Mapping mapping;
+
+	/** current table as read from the database */
+	Table currentDbTable = null;
+
+	public SchemaByMetaDataDetector() {
+
+	}
+
+	public void initialize(Configuration cfg, Settings settings) {
+		super.initialize( cfg, settings );
+		dialect = settings.getDialect();
+
+		tableSelector = new TableSelectorStrategy(
+				new DefaultReverseEngineeringStrategy() );
+		reader = JDBCReaderFactory.newJDBCReader( cfg.getProperties(),
+				settings, tableSelector );
+		dbc = new DefaultDatabaseCollector();
+		mapping = cfg.buildMapping();
+	}
+
+	public void visit(Configuration cfg, IssueCollector collector) {
+		super.visit( cfg, collector );
+		
+		visitGenerators(cfg, collector);
+				
+	}
+	
+	public void visitGenerators(Configuration cfg, IssueCollector collector) {
+		Iterator iter = iterateGenerators(cfg);
+		
+		Set sequences = Collections.EMPTY_SET;
+		if(dialect.supportsSequences()) {
+			sequences = reader.readSequences(dialect.getQuerySequencesString());
+		}
+
+		// TODO: move this check into something that could check per class or collection instead.
+		while ( iter.hasNext() ) {
+			PersistentIdentifierGenerator generator = (PersistentIdentifierGenerator) iter.next();
+			Object key = generator.generatorKey();
+			if ( !isSequence(key, sequences) && !isTable( key ) ) {
+				collector.reportIssue( new Issue( "MISSING_ID_GENERATOR", Issue.HIGH_PRIORITY, "Missing sequence or table: " + key));
+			}
+		}
+
+		
+	}
+
+	private boolean isSequence(Object key, Set sequences) {
+		if(key instanceof String) {
+			if ( sequences.contains( key ) ) {
+				return true;
+			} else {
+				String[] strings = StringHelper.split(".", (String) key);
+				if(strings.length==3) {
+					return sequences.contains(strings[2]);
+				} else if (strings.length==2) {
+					return sequences.contains(strings[1]);
+				}
+			}
+		}
+		return false;
+	}
+
+	private boolean isTable(Object key) throws HibernateException {
+		// BIG HACK - should probably utilize the table cache before going to the jdbcreader :(
+		if(key instanceof String) {
+			String[] strings = StringHelper.split(".", (String) key);
+			if(strings.length==1) {
+				tableSelector.clearSchemaSelections();
+				tableSelector.addSchemaSelection( new SchemaSelection(null,null, strings[0]) );
+				List list = reader.readDatabaseSchema( dbc, null, null );
+				return !list.isEmpty();
+			} else if(strings.length==3) {
+				tableSelector.clearSchemaSelections();
+				tableSelector.addSchemaSelection( new SchemaSelection(strings[0],strings[1], strings[2]) );
+				List list = reader.readDatabaseSchema( dbc, null, null );
+				return !list.isEmpty();
+			} else if (strings.length==2) {
+				tableSelector.clearSchemaSelections();
+				tableSelector.addSchemaSelection( new SchemaSelection(null,strings[0], strings[1]) );
+				List list = reader.readDatabaseSchema( dbc, null, null );
+				return !list.isEmpty();
+			}
+		}
+		return false;
+	}
+	
+	public void visit(Configuration cfg, Table table, IssueCollector pc) {
+
+		if ( table.isPhysicalTable() ) {
+			setSchemaSelection( table );
+
+			List list = reader.readDatabaseSchema( dbc, null, null );
+
+			if ( list.isEmpty() ) {
+				pc.reportIssue( new Issue( "SCHEMA_TABLE_MISSING",
+						Issue.HIGH_PRIORITY, "Missing table "
+								+ Table.qualify( table.getCatalog(), table
+										.getSchema(), table.getName() ) ) );
+				return;
+			}
+			else if ( list.size() > 1 ) {
+				pc.reportIssue( new Issue( "SCHEMA_TABLE_MISSING",
+						Issue.NORMAL_PRIORITY, "Found "
+								+ list.size()
+								+ " tables for "
+								+ Table.qualify( table.getCatalog(), table
+										.getSchema(), table.getName() ) ) );
+				return;
+			}
+			else {
+				currentDbTable = (Table) list.get( 0 );
+				visitColumns(cfg,table,pc);				
+			}
+		}
+		else {
+			// log?			
+		}
+	}
+
+	String table(Table t) {
+		return Table.qualify( t.getCatalog(), t.getSchema(), t.getName() );
+	}
+	
+	public void visit(Configuration cfg, Table table, Column col,
+			IssueCollector pc) {
+		if ( currentDbTable == null ) {
+			return;
+		}
+
+		Column dbColumn = currentDbTable
+				.getColumn( new Column( col.getName() ) );
+
+		if ( dbColumn == null ) {
+			pc.reportIssue( new Issue( "SCHEMA_COLUMN_MISSING",
+					Issue.HIGH_PRIORITY, table(table) + " is missing column: " + col.getName() ) );
+		}
+		else {
+			//TODO: this needs to be able to know if a type is truly compatible or not. Right now it requires an exact match.
+			String sqlType = col.getSqlType( dialect, mapping );
+			int dbTypeCode = dbColumn.getSqlTypeCode().intValue();
+			int modelTypeCode = col
+								.getSqlTypeCode( mapping );
+			// TODO: sqltype name string
+			if ( !(dbTypeCode == modelTypeCode ) ) {
+				pc.reportIssue( new Issue( "SCHEMA_COLUMN_TYPE_MISMATCH",
+						Issue.NORMAL_PRIORITY, table(table) + " has a wrong column type for "
+								+ col.getName() + ", expected: "
+								+ JDBCToHibernateTypeHelper.getJDBCTypeName(modelTypeCode) + " but was " + JDBCToHibernateTypeHelper.getJDBCTypeName(dbTypeCode) + " in db") );
+			}
+		}
+	}
+
+	private void setSchemaSelection(Table table) {
+		tableSelector.clearSchemaSelections();
+		tableSelector.addSchemaSelection( new SchemaSelection( table
+				.getCatalog(), table.getSchema(), table.getName() ) );
+
+	}
+
+	/**
+	 * 
+	 * @param cfg 
+	 * @return iterator over all the IdentifierGenerator's found in the entitymodel and return a list of unique IdentifierGenerators
+	 * @throws MappingException
+	 */
+	private Iterator iterateGenerators(Configuration cfg) throws MappingException {
+
+		TreeMap generators = new TreeMap();
+		String defaultCatalog = getSettings().getDefaultCatalogName();
+		String defaultSchema = getSettings().getDefaultSchemaName();
+
+		Iterator iter = cfg.getClassMappings();
+		while ( iter.hasNext() ) {
+			PersistentClass pc = (PersistentClass) iter.next();
+
+			if ( !pc.isInherited() ) {
+
+				IdentifierGenerator ig = pc.getIdentifier()
+						.createIdentifierGenerator(
+								dialect,
+								defaultCatalog,
+								defaultSchema,
+								(RootClass) pc
+							);
+
+				if ( ig instanceof PersistentIdentifierGenerator ) {
+					generators.put( ( (PersistentIdentifierGenerator) ig ).generatorKey(), ig );
+				}
+
+			}
+		}
+
+		iter = getConfiguration().getCollectionMappings();
+		while ( iter.hasNext() ) {
+			Collection collection = (Collection) iter.next();
+
+			if ( collection.isIdentified() ) {
+
+				IdentifierGenerator ig = ( (IdentifierCollection) collection ).getIdentifier()
+						.createIdentifierGenerator(
+								dialect,
+								defaultCatalog,
+								defaultSchema,
+								null
+							);
+
+				if ( ig instanceof PersistentIdentifierGenerator ) {
+					generators.put( ( (PersistentIdentifierGenerator) ig ).generatorKey(), ig );
+				}
+
+			}
+		}
+
+		return generators.values().iterator();
+	}
+
+}

Added: trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/detector/ShadowedIdentifierDetector.java
===================================================================
--- trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/detector/ShadowedIdentifierDetector.java	2006-10-12 17:23:52 UTC (rev 10572)
+++ trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/detector/ShadowedIdentifierDetector.java	2006-10-12 17:23:59 UTC (rev 10573)
@@ -0,0 +1,22 @@
+package org.hibernate.tool.hbmlint.detector;
+
+import org.hibernate.cfg.Configuration;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Property;
+import org.hibernate.tool.hbmlint.Issue;
+import org.hibernate.tool.hbmlint.IssueCollector;
+
+public class ShadowedIdentifierDetector extends EntityModelDetector {
+	
+	public String getName() {
+		return "shadow-id";
+	}
+	
+	public void visitProperty(Configuration configuration, PersistentClass clazz, Property property, IssueCollector collector) {
+		if(property.getName().equals("id")) {
+			if (property != property.getPersistentClass().getIdentifierProperty()) {
+				collector.reportIssue(new Issue("ID_SHADOWED", Issue.LOW_PRIORITY, property.getPersistentClass().getEntityName() + " has a normal property named 'id'. This can cause issues since HQL queries will always interpret 'id' as the identifier and not the concrete property"));
+			}
+		}
+	}
+}

Added: trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/detector/TableSelectorStrategy.java
===================================================================
--- trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/detector/TableSelectorStrategy.java	2006-10-12 17:23:52 UTC (rev 10572)
+++ trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbmlint/detector/TableSelectorStrategy.java	2006-10-12 17:23:59 UTC (rev 10573)
@@ -0,0 +1,33 @@
+/**
+ * 
+ */
+package org.hibernate.tool.hbmlint.detector;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.hibernate.cfg.reveng.DelegatingReverseEngineeringStrategy;
+import org.hibernate.cfg.reveng.ReverseEngineeringStrategy;
+import org.hibernate.cfg.reveng.SchemaSelection;
+
+public class TableSelectorStrategy extends DelegatingReverseEngineeringStrategy {
+	
+	List selections = new ArrayList();
+	
+	public TableSelectorStrategy(ReverseEngineeringStrategy res) {
+		super(res);
+	}
+	
+	public List getSchemaSelections() {
+		return selections;
+	}
+	
+
+	public void clearSchemaSelections() {
+		selections.clear();
+	}
+	
+	public void addSchemaSelection(SchemaSelection selection) {
+		selections.add(selection);
+	}	
+}
\ No newline at end of file

Added: trunk/HibernateExt/tools/src/templates/lint/text-report.ftl
===================================================================
--- trunk/HibernateExt/tools/src/templates/lint/text-report.ftl	2006-10-12 17:23:52 UTC (rev 10572)
+++ trunk/HibernateExt/tools/src/templates/lint/text-report.ftl	2006-10-12 17:23:59 UTC (rev 10573)
@@ -0,0 +1,3 @@
+<#foreach issue in lintissues>
+${issue}
+</#foreach>
\ No newline at end of file

Modified: trunk/HibernateExt/tools/src/test/org/hibernate/tool/JDBCMetaDataBinderTestCase.java
===================================================================
--- trunk/HibernateExt/tools/src/test/org/hibernate/tool/JDBCMetaDataBinderTestCase.java	2006-10-12 17:23:52 UTC (rev 10572)
+++ trunk/HibernateExt/tools/src/test/org/hibernate/tool/JDBCMetaDataBinderTestCase.java	2006-10-12 17:23:59 UTC (rev 10573)
@@ -139,7 +139,7 @@
 
    protected void tearDown() throws Exception {
         executeDDL(getDropSQL(), false);
-        assertNoTables();
+        
         super.tearDown();
     }
 	/**

Modified: trunk/HibernateExt/tools/src/test/org/hibernate/tool/ToolAllTests.java
===================================================================
--- trunk/HibernateExt/tools/src/test/org/hibernate/tool/ToolAllTests.java	2006-10-12 17:23:52 UTC (rev 10572)
+++ trunk/HibernateExt/tools/src/test/org/hibernate/tool/ToolAllTests.java	2006-10-12 17:23:59 UTC (rev 10573)
@@ -1,6 +1,7 @@
 package org.hibernate.tool;
 
 import org.hibernate.tool.hbm2x.query.QueryAllTests;
+import org.hibernate.tool.hbmlint.HbmLintAllTests;
 import org.hibernate.tool.ide.completion.CompletionAllTests;
 
 import junit.framework.Test;
@@ -17,6 +18,6 @@
 	suite.addTest(org.hibernate.tool.hbm2x.Hbm2XAllTests.suite() );
 	suite.addTest(CompletionAllTests.suite() );
 	suite.addTest(QueryAllTests.suite() );
-	
+	suite.addTest(HbmLintAllTests.suite() );
 	return suite;
 }}

Modified: trunk/HibernateExt/tools/src/test/org/hibernate/tool/ant/HibernateToolTest.java
===================================================================
--- trunk/HibernateExt/tools/src/test/org/hibernate/tool/ant/HibernateToolTest.java	2006-10-12 17:23:52 UTC (rev 10572)
+++ trunk/HibernateExt/tools/src/test/org/hibernate/tool/ant/HibernateToolTest.java	2006-10-12 17:23:59 UTC (rev 10573)
@@ -178,6 +178,15 @@
 		
 	}
 	
+	public void testHbmLint() {
+		executeTarget("testhbmlint");
+		
+		File baseDir = new File(project.getProperty("build.dir"), "linttest");
+		
+		assertTrue(new File(baseDir, "hbmlint-result.txt").exists());
+		
+	}
+	
 	public void testNoConfig() {
 		try {
 			executeTarget("noconfig-shoulderror");

Added: trunk/HibernateExt/tools/src/test/org/hibernate/tool/hbm2x/IncrementalSchemaReadingTest.java
===================================================================
--- trunk/HibernateExt/tools/src/test/org/hibernate/tool/hbm2x/IncrementalSchemaReadingTest.java	2006-10-12 17:23:52 UTC (rev 10572)
+++ trunk/HibernateExt/tools/src/test/org/hibernate/tool/hbm2x/IncrementalSchemaReadingTest.java	2006-10-12 17:23:59 UTC (rev 10573)
@@ -0,0 +1,121 @@
+/*
+ * Created on 2004-12-01
+ *
+ */
+package org.hibernate.tool.hbm2x;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.hibernate.cfg.Environment;
+import org.hibernate.cfg.JDBCMetaDataConfiguration;
+import org.hibernate.cfg.JDBCReaderFactory;
+import org.hibernate.cfg.Settings;
+import org.hibernate.cfg.reveng.DatabaseCollector;
+import org.hibernate.cfg.reveng.DefaultDatabaseCollector;
+import org.hibernate.cfg.reveng.DefaultReverseEngineeringStrategy;
+import org.hibernate.cfg.reveng.JDBCReader;
+import org.hibernate.cfg.reveng.OverrideRepository;
+import org.hibernate.cfg.reveng.SchemaSelection;
+import org.hibernate.cfg.reveng.TableIdentifier;
+import org.hibernate.cfg.reveng.dialect.JDBCMetaDataDialect;
+import org.hibernate.mapping.Table;
+import org.hibernate.tool.JDBCMetaDataBinderTestCase;
+import org.hibernate.tool.hbmlint.detector.TableSelectorStrategy;
+
+
+
+/**
+ * @author max
+ *
+ */
+public class IncrementalSchemaReadingTest extends JDBCMetaDataBinderTestCase {
+	
+	
+	protected void configure(JDBCMetaDataConfiguration cfg) {
+		super.configure( cfg );
+	}
+	
+	public class MockedMetaDataDialect extends JDBCMetaDataDialect {
+		List gottenTables = new ArrayList();
+		public Iterator getTables(String catalog, String schema, String table) {
+			gottenTables.add(table);
+			return super.getTables( catalog, schema, table );
+		}
+				
+	}
+	
+	public void testReadSchemaIncremental() {
+		Settings buildSettings = cfg.buildSettings();
+		TableSelectorStrategy tss = new TableSelectorStrategy(new DefaultReverseEngineeringStrategy());
+		MockedMetaDataDialect mockedMetaDataDialect = new MockedMetaDataDialect();
+		JDBCReader reader = JDBCReaderFactory.newJDBCReader( buildSettings, tss, mockedMetaDataDialect );
+		
+		tss.addSchemaSelection( new SchemaSelection(null,null, "CHILD") );
+		
+		DatabaseCollector dc = new DefaultDatabaseCollector();
+		reader.readDatabaseSchema( dc, null, null );
+		
+		assertEquals(mockedMetaDataDialect.gottenTables.size(),1);
+		assertEquals(mockedMetaDataDialect.gottenTables.get(0),"CHILD");
+		
+		Iterator iterator = dc.iterateTables();
+		Table firstChild = (Table) iterator.next();
+		assertEquals(firstChild.getName(), "CHILD");
+		assertFalse(iterator.hasNext());
+		
+		assertFalse("should not record foreignkey to table it doesn't know about yet",firstChild.getForeignKeyIterator().hasNext());
+		
+		tss.clearSchemaSelections();
+		tss.addSchemaSelection( new SchemaSelection(null, null, "MASTER") );
+		
+		mockedMetaDataDialect.gottenTables.clear();
+		reader.readDatabaseSchema( dc, null, null );
+		
+		assertEquals(mockedMetaDataDialect.gottenTables.size(),1);
+		assertEquals(mockedMetaDataDialect.gottenTables.get(0),"MASTER");
+		
+		
+		iterator = dc.iterateTables();
+		assertNotNull(iterator.next());
+		assertNotNull(iterator.next());
+		assertFalse(iterator.hasNext());
+		
+		Table table = dc.getTable( null, null, "CHILD" );
+		assertSame( firstChild, table );
+		
+		assertHasNext("should have recorded one foreignkey to child table", 1, firstChild.getForeignKeyIterator() );		
+		
+		
+		tss.clearSchemaSelections();		
+		reader.readDatabaseSchema( dc, null, null );
+		
+		Table finalMaster = dc.getTable( null, null, "MASTER" );
+		
+		assertSame(firstChild, dc.getTable( null, null, "CHILD" ));
+		assertHasNext( 1, firstChild.getForeignKeyIterator() );
+		assertHasNext( 0, finalMaster.getForeignKeyIterator() );
+		
+				
+	}
+
+	protected String[] getCreateSQL() {
+		
+		return new String[] {
+				"create table master ( id char not null, name varchar(20), primary key (id) )",
+				"create table child  ( childid char not null, masterref char, primary key (childid), foreign key (masterref) references master(id) )",				
+		};
+	}
+
+	protected String[] getDropSQL() {
+		
+		return new String[]  {
+				"drop table child",
+				"drop table master",				
+		};
+	}	
+	
+}

Added: trunk/HibernateExt/tools/src/testsupport/SchemaIssues.hbm.xml
===================================================================
--- trunk/HibernateExt/tools/src/testsupport/SchemaIssues.hbm.xml	2006-10-12 17:23:52 UTC (rev 10572)
+++ trunk/HibernateExt/tools/src/testsupport/SchemaIssues.hbm.xml	2006-10-12 17:23:59 UTC (rev 10573)
@@ -0,0 +1,37 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC 
+	"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+	"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.tool.hbmlint">
+
+    <class name="Category">
+    
+    	<id name="id" type="int">
+    		<generator class="sequence">
+    			<param name="sequence">should_be_there</param>
+    		</generator>
+    	</id>
+		
+		<property name="name" type="string"/>
+		
+	</class>
+
+    <class name="BadType">
+    
+    	<id name="id" type="int">
+    		<generator class="hilo"/>
+    	</id>
+		
+		<property name="name" type="text"/>
+		
+	</class>
+
+	<class name="MissingTable">
+		<id name="id" type="long">
+    		<generator class="hilo">
+    			<param name="table">hilo_table</param>
+    		</generator>
+    	</id>		
+	</class>	
+</hibernate-mapping>

Modified: trunk/HibernateExt/tools/src/testsupport/anttest-build.xml
===================================================================
--- trunk/HibernateExt/tools/src/testsupport/anttest-build.xml	2006-10-12 17:23:52 UTC (rev 10572)
+++ trunk/HibernateExt/tools/src/testsupport/anttest-build.xml	2006-10-12 17:23:59 UTC (rev 10573)
@@ -436,6 +436,20 @@
 		<antcall target="afterCfg2hbm" />
 	</target>
 	
+	<target name="testhbmlint" depends="prepareCfg2hbm" description="test the query tasks">
+		<taskdef name="hibernatetool" classname="org.hibernate.tool.ant.HibernateToolTask" classpathref="tasks.classpath" />
+		
+		<hibernatetool destdir="${build.dir}/linttest">
+			<configuration propertyfile="../../etc/hibernate.properties" configurationfile="querytest-hibernate.cfg.xml">
+				<fileset dir="../test">
+					<include name="**/SchemaIssues.hbm.xml"/>
+				</fileset>
+			</configuration>
+			<hbmlint/>
+		</hibernatetool>		
+		
+		<antcall target="afterCfg2hbm" />
+	</target>
 
 	<target name="noconfig-shoulderror">
 		<taskdef name="hibernatetool" classname="org.hibernate.tool.ant.HibernateToolTask" classpathref="tasks.classpath" />




More information about the hibernate-commits mailing list