diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java index e9c0f2d5b..9b1ecbf39 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java @@ -164,11 +164,17 @@ public class JPQLExpressionBuilder if (c != null) cmd = repos.getMetaData(c, loader, assertValid); else if (assertValid) - cmd = repos.getMetaData(alias, loader, true); + cmd = repos.getMetaData(alias, loader, false); - if (cmd == null && assertValid) - throw parseException(EX_USER, "not-schema-name", - new Object[]{ alias }, null); + if (cmd == null && assertValid) { + String close = repos.getClosestAliasName(alias); + if (close != null) + throw parseException(EX_USER, "not-schema-name-hint", + new Object[]{ alias, close, repos.getAliasNames() }, null); + else + throw parseException(EX_USER, "not-schema-name", + new Object[]{ alias, repos.getAliasNames() }, null); + } return cmd; } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataRepository.java b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataRepository.java index 9483a35b2..80938fb64 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataRepository.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataRepository.java @@ -47,6 +47,7 @@ import org.apache.openjpa.lib.log.Log; import org.apache.openjpa.lib.util.Closeable; import org.apache.openjpa.lib.util.J2DoPrivHelper; import org.apache.openjpa.lib.util.Localizer; +import org.apache.openjpa.lib.util.StringDistance; import org.apache.openjpa.util.ImplHelper; import org.apache.openjpa.util.InternalException; import org.apache.openjpa.util.MetaDataException; @@ -360,8 +361,7 @@ public class MetaDataRepository // maybe this is some type we've seen but just isn't valid if (_aliases.containsKey(alias)) { if (mustExist) - throw new MetaDataException(_loc.get("no-alias-meta", alias, - _aliases.toString())); + throwNoRegisteredAlias(alias); return null; } @@ -370,7 +370,43 @@ public class MetaDataRepository if (!mustExist) return null; - throw new MetaDataException(_loc.get("no-alias-meta", alias, _aliases)); + return throwNoRegisteredAlias(alias); + } + + private ClassMetaData throwNoRegisteredAlias(String alias) { + String close = getClosestAliasName(alias); + if (close != null) + throw new MetaDataException( + _loc.get("no-alias-meta-hint", alias, _aliases, close)); + else + throw new MetaDataException( + _loc.get("no-alias-meta", alias, _aliases)); + } + + /** + * @return the nearest match to the specified alias name + * @since 1.1.0 + */ + public String getClosestAliasName(String alias) { + Collection aliases = getAliasNames(); + return StringDistance.getClosestLevenshteinDistance(alias, aliases); + } + + /** + * @return the registered alias names + * @since 1.1.0 + */ + public Collection getAliasNames() { + Collection aliases = new HashSet(); + synchronized (_aliases) { + for (Iterator iter = _aliases.entrySet().iterator(); + iter.hasNext(); ) { + Map.Entry e = (Map.Entry) iter.next(); + if (e.getValue() != null) + aliases.add(e.getKey()); + } + } + return aliases; } /** diff --git a/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/jpql/localizer.properties b/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/jpql/localizer.properties index ddfcd7ec0..f014bd20c 100644 --- a/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/jpql/localizer.properties +++ b/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/jpql/localizer.properties @@ -36,7 +36,10 @@ unknown-comp: Unknown comparison operator "{0}". wrong-child-count: Wrong number of arguments to expression \ of type "{1}": should have been {0}, but the following arguments \ were specified: "{2}". -not-schema-name: The name "{0}" is not a recognized entity or identifier. +not-schema-name: The name "{0}" is not a recognized entity or identifier. \ + Known entity names: {1} +not-schema-name-hint: The name "{0}" is not a recognized entity or identifier. \ + Perhaps you meant {1}, which is a close match. Known entity names: {2} duplicate-alias: Alias "{0}" was declared twice. unknown-type: Cannot determine the type of field "{0}". unexpected-var: The variable "{0}" was found where a constant or \ diff --git a/openjpa-kernel/src/main/resources/org/apache/openjpa/meta/localizer.properties b/openjpa-kernel/src/main/resources/org/apache/openjpa/meta/localizer.properties index 19f11a95a..14f585ce4 100644 --- a/openjpa-kernel/src/main/resources/org/apache/openjpa/meta/localizer.properties +++ b/openjpa-kernel/src/main/resources/org/apache/openjpa/meta/localizer.properties @@ -142,9 +142,15 @@ no-meta: No metadata was found for type "{0}". Ensure that the class is \ if you list your persistent classes, the class is included in your list. no-oid-meta: Could not locate metadata for the class using oid "{0}" of \ type "{1}". Registered oid type mappings: "{2}" -no-alias-meta: Could not locate metadata for the class using alias "{0}". This \ - could mean that the OpenJPA enhancer or load-time weaver was not run on \ - the type whose alias is "{0}". Registered alias mappings: "{1}" +no-alias-meta: Could not locate metadata for the class using alias "{0}". \ + This could mean that you have mis-spelled the alias, or that OpenJPA failed\ + to properly load the metadata for the type whose alias is "{0}". \ + Registered alias mappings: "{1}" +no-alias-meta-hint: Could not locate metadata for the class using alias "{0}". \ + Perhaps you meant {2}, which is a close match. \ + This could mean that you have mis-spelled the alias, or that OpenJPA failed\ + to properly load the metadata for the type whose alias is "{0}". \ + Registered alias mappings: "{1}" error-registered: An error occurred while processing registered class "{0}". failed-registered: A potentially serious error occurred while processing \ registered class "{0}". Deferring processing of this class until next \ diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/xml/TestSimpleXmlEntity.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/xml/TestSimpleXmlEntity.java index 18a5f0374..8c1e65a83 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/xml/TestSimpleXmlEntity.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/xml/TestSimpleXmlEntity.java @@ -53,7 +53,8 @@ public class TestSimpleXmlEntity fail("should not be able to execute query using short class name " + "for entity that has an entity name specified"); } catch (ArgumentException ae) { - // expected + assertTrue( + ae.getMessage().indexOf("Perhaps you meant SimpleXml,") != -1); } } @@ -67,7 +68,8 @@ public class TestSimpleXmlEntity fail("should not be able to execute query using short class name " + "for entity that has an entity name specified"); } catch (ArgumentException ae) { - // expected + assertTrue( + ae.getMessage().indexOf("Perhaps you meant SimpleXml,") != -1); } } }