Author: max...@jb...
Date: 2006-05-19 10:44:38 -0400 (Fri, 19 May 2006)
New Revision: 9938
Added:
trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/
trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/AntlrSimpleHQLLexer.java
trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/CompletionHelper.java
trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/ConfigurationCompletion.java
trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/EntityNameReference.java
trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/HQLAnalyzer.java
trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/HQLCodeAssist.java
trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/HQLCompletionProposal.java
trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/IHQLCodeAssist.java
trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/IHQLCompletionRequestor.java
trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/SimpleHQLLexer.java
trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/SimpleLexerException.java
trunk/HibernateExt/tools/src/test/org/hibernate/tool/ide/completion/
trunk/HibernateExt/tools/src/test/org/hibernate/tool/ide/completion/CompletionHelperTest.java
trunk/HibernateExt/tools/src/test/org/hibernate/tool/ide/completion/HQLCompletionProposalComparator.java
trunk/HibernateExt/tools/src/test/org/hibernate/tool/ide/completion/HqlAnalyzerTest.java
trunk/HibernateExt/tools/src/test/org/hibernate/tool/ide/completion/Model.java
trunk/HibernateExt/tools/src/test/org/hibernate/tool/ide/completion/ModelCompletionTest.java
trunk/HibernateExt/tools/src/test/org/hibernate/tool/ide/completion/Product.hbm.xml
trunk/HibernateExt/tools/src/test/org/hibernate/tool/ide/completion/Product.java
trunk/HibernateExt/tools/src/test/org/hibernate/tool/ide/completion/ProductOwner.java
trunk/HibernateExt/tools/src/test/org/hibernate/tool/ide/completion/ProductOwnerAddress.hbm.xml
trunk/HibernateExt/tools/src/test/org/hibernate/tool/ide/completion/ProductOwnerAddress.java
trunk/HibernateExt/tools/src/test/org/hibernate/tool/ide/completion/Store.hbm.xml
trunk/HibernateExt/tools/src/test/org/hibernate/tool/ide/completion/Store.java
trunk/HibernateExt/tools/src/test/org/hibernate/tool/ide/completion/StoreCity.hbm.xml
trunk/HibernateExt/tools/src/test/org/hibernate/tool/ide/completion/StoreCity.java
Log:
hql code completion api
Added: trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/AntlrSimpleHQLLexer.java
===================================================================
--- trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/AntlrSimpleHQLLexer.java 2006-05-19 14:44:33 UTC (rev 9937)
+++ trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/AntlrSimpleHQLLexer.java 2006-05-19 14:44:38 UTC (rev 9938)
@@ -0,0 +1,59 @@
+package org.hibernate.tool.ide.completion;
+
+import java.io.CharArrayReader;
+
+import org.hibernate.hql.antlr.HqlBaseLexer;
+
+import antlr.Token;
+import antlr.TokenStreamException;
+
+/**
+ * A lexer implemented on top of the Antlr grammer implemented in core.
+ *
+ * @author Max Rydahl Andersen
+ *
+ */
+public class AntlrSimpleHQLLexer implements SimpleHQLLexer {
+
+ private HqlBaseLexer lexer;
+ private Token token;
+
+ public AntlrSimpleHQLLexer(char[] cs, int length) {
+ lexer = new HqlBaseLexer(new CharArrayReader(cs, 0, length)) {
+ public void newline() {
+ //super.newline();
+ }
+
+ public int getColumn() {
+ return super.getColumn()-1;
+ }
+
+
+ };
+ }
+
+ public int getTokenLength() {
+ if(token.getText()==null) {
+ return 0;
+ }
+ return token.getText().length();
+ }
+
+ public int getTokenOffset() {
+ return token.getColumn()-1;
+ }
+
+ public int nextTokenId() {
+ try {
+ token = lexer.nextToken();
+ if(token==null) {
+ System.out.println(token);
+ }
+ }
+ catch (TokenStreamException e) {
+ throw new SimpleLexerException(e);
+ }
+ return token.getType();
+ }
+
+}
Added: trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/CompletionHelper.java
===================================================================
--- trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/CompletionHelper.java 2006-05-19 14:44:33 UTC (rev 9937)
+++ trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/CompletionHelper.java 2006-05-19 14:44:38 UTC (rev 9938)
@@ -0,0 +1,74 @@
+package org.hibernate.tool.ide.completion;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Helper class for completion.
+ * Package private, not to be used externally.
+ *
+ * @author leon, max...@jb...
+ */
+class CompletionHelper {
+
+ private CompletionHelper() {
+ }
+
+ public static String getCanonicalPath(List qts, String name) {
+ Map alias2Type = new HashMap();
+ for (Iterator iter = qts.iterator(); iter.hasNext();) {
+ EntityNameReference qt = (EntityNameReference) iter.next();
+ alias2Type.put(qt.getAlias(), qt.getEntityName());
+ }
+ if (qts.size() == 1) {
+ EntityNameReference visible = (EntityNameReference) qts.get(0);
+ String alias = visible.getAlias();
+ if (name.equals(alias)) {
+ return visible.getEntityName();
+ } else if (alias == null || alias.length() == 0 || alias.equals(visible.getEntityName())) {
+ return visible.getEntityName() + "/" + name;
+ }
+ }
+ return getCanonicalPath(new HashSet(), alias2Type, name);
+ }
+
+
+ private static String getCanonicalPath(Set resolved, Map alias2Type, String name) {
+ if (resolved.contains(name)) {
+ // To prevent a stack overflow
+ return name;
+ }
+ resolved.add(name);
+ String type = (String) alias2Type.get(name);
+ if (type != null) {
+ return name.equals(type) ? name : getCanonicalPath(resolved, alias2Type, type);
+ }
+ int idx = name.lastIndexOf('.');
+ if (idx == -1) {
+ return type != null ? type : name;
+ }
+ String baseName = name.substring(0, idx);
+ String prop = name.substring(idx + 1);
+ if (isAliasNown(alias2Type, baseName)) {
+ return getCanonicalPath(resolved, alias2Type, baseName) + "/" + prop;
+ } else {
+ return name;
+ }
+ }
+
+ private static boolean isAliasNown(Map alias2Type, String alias) {
+ if (alias2Type.containsKey(alias)) {
+ return true;
+ }
+ int idx = alias.lastIndexOf('.');
+ if (idx == -1) {
+ return false;
+ }
+ return isAliasNown(alias2Type, alias.substring(0, idx));
+ }
+
+}
Added: trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/ConfigurationCompletion.java
===================================================================
--- trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/ConfigurationCompletion.java 2006-05-19 14:44:33 UTC (rev 9937)
+++ trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/ConfigurationCompletion.java 2006-05-19 14:44:38 UTC (rev 9938)
@@ -0,0 +1,341 @@
+package org.hibernate.tool.ide.completion;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+import org.hibernate.cfg.Configuration;
+import org.hibernate.mapping.Collection;
+import org.hibernate.mapping.Component;
+import org.hibernate.mapping.OneToMany;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Property;
+import org.hibernate.mapping.ToOne;
+import org.hibernate.mapping.Value;
+import org.hibernate.tool.hbm2x.Cfg2JavaTool;
+import org.hibernate.tool.hbm2x.pojo.EntityPOJOClass;
+
+/**
+ * Completion based on a Configuration.
+ * package protected for now - not meant to be used externally.
+ *
+ * @author Max Rydahl Andersen
+ *
+ */
+class ConfigurationCompletion {
+
+ private final Configuration cfg;
+
+ public ConfigurationCompletion(Configuration cfg) {
+ this.cfg = cfg;
+
+ }
+
+ public void getMatchingImports(String prefix , IHQLCompletionRequestor collector) {
+ getMatchingImports( prefix, prefix.length() , collector );
+ }
+
+ public void getMatchingImports(String prefix, int cursorPosition, IHQLCompletionRequestor collector) {
+ Iterator iterator = cfg.getImports().entrySet().iterator();
+ while ( iterator.hasNext() ) {
+ Map.Entry entry = (Entry) iterator.next();
+ String entityImport = (String) entry.getKey();
+ String entityName = (String) entry.getValue();
+
+ if(entityImport.startsWith(prefix)) {
+ String remaining = entityImport.substring( prefix.length() );
+ HQLCompletionProposal proposal = new HQLCompletionProposal(
+ HQLCompletionProposal.ENTITY_NAME,
+ cursorPosition);
+ proposal.setCompletion( remaining );
+ proposal.setSimpleName( entityImport );
+ proposal.setReplaceStart( cursorPosition );
+ proposal.setReplaceEnd( cursorPosition+0 ); // we don't replace anything here
+
+ proposal.setShortEntityName( entityImport );
+ proposal.setEntityName( entityName );
+ collector.accept(proposal);
+
+ }
+ }
+ }
+
+ public void getMatchingKeywords(String prefix, int cursorPosition, IHQLCompletionRequestor collector) {
+ findMatchingWords( cursorPosition, prefix, HQLAnalyzer.getHQLKeywords(), HQLCompletionProposal.KEYWORD, collector);
+ }
+
+ public void getMatchingFunctions(String prefix, int cursorPosition, IHQLCompletionRequestor collector) {
+ findMatchingWords( cursorPosition, prefix, HQLAnalyzer.getHQLFunctionNames(), HQLCompletionProposal.FUNCTION, collector);
+ }
+
+ public void getMatchingProperties(String path, String prefix, IHQLCompletionRequestor hcc) {
+ getMatchingProperties( path, prefix, prefix.length(), hcc );
+ }
+
+ public void getMatchingProperties(String path, String prefix, int cursorPosition, IHQLCompletionRequestor hcc) {
+ int idx = path.indexOf('/');
+ if (idx == -1) { // root name
+ PersistentClass cmd = getPersistentClass(path);
+ if (cmd == null) {
+ return;
+ }
+ addPropertiesToList(cmd, prefix, cursorPosition, hcc);
+ } else {
+ String baseEntityName = path.substring(0, idx);
+ String propertyPath = path.substring(idx + 1);
+ Value value = getNextAttributeType(baseEntityName, propertyPath);
+ if (value == null) {
+ return;
+ }
+
+ // Go to the next property (get the y of x/y/z when root is x)
+ idx = propertyPath.indexOf('/');
+ if (idx == -1) {
+ path = "";
+ } else {
+ path = propertyPath.substring(idx + 1);
+ }
+ if (path.length() == 0) {
+ // No properties left
+ if (value instanceof Component) {
+ addPropertiesToList((Component) value, prefix, cursorPosition, hcc);
+ } else {
+ addPropertiesToList(getPersistentClass( getReferencedEntityName( value ) ), prefix, cursorPosition, hcc);
+ }
+ } else {
+ // Nested properties
+ if (value instanceof Component) {
+ // We need to find the first non-component type
+ while (value instanceof Component && path.length() > 0) {
+ value = getNextAttributeType((Component) value, path);
+ if (value != null) {
+ // Consume part of the canonical path
+ idx = path.indexOf('/');
+ if (idx != -1) {
+ path = path.substring(idx + 1);
+ } else {
+ path = "";
+ }
+ }
+ }
+ if (value instanceof Component) {
+ addPropertiesToList((Component) value, prefix, cursorPosition, hcc);
+ } else if (value != null) {
+ if (path.length() > 0) {
+ path = getReferencedEntityName( value ) + "/" + path;
+ } else {
+ path = getReferencedEntityName( value );
+ }
+ getMatchingProperties( path, prefix, cursorPosition, hcc );
+ }
+ } else {
+ // Just call the method recursively to add our new type
+ getMatchingProperties(getReferencedEntityName( value ) + "/" + path, prefix, cursorPosition, hcc);
+ }
+ }
+ }
+ }
+
+ private String getReferencedEntityName(Value value) {
+ if(value instanceof ToOne) {
+ return ((ToOne)value).getReferencedEntityName();
+ }
+ if ( value instanceof Collection ) {
+ Collection collection = ((Collection)value);
+ Value element = collection.getElement();
+ String elementType = getReferencedEntityName( element );
+ if(collection.isIndexed()) {
+ //TODO..list/map
+ /*IndexedCollection idxCol = (IndexedCollection) collection;
+ if(!idxCol.isList()) {
+ Value idxElement = idxCol.getIndex();
+ String indexType = getReferencedEntityName( value );
+ genericDecl = indexType + "," + elementType;
+ }*/
+ }
+ return elementType;
+ }
+
+ if(value instanceof OneToMany) {
+ return ((OneToMany)value).getReferencedEntityName();
+ }
+
+ return null;
+ }
+
+ private void addPropertiesToList(PersistentClass cmd, String prefix, int cursorPosition, IHQLCompletionRequestor hcc) {
+ if (cmd == null) {
+ return;
+ }
+ if (prefix == null) {
+ prefix = "";
+ }
+
+ EntityPOJOClass pc = new EntityPOJOClass(cmd, new Cfg2JavaTool()); // TODO: we should extract the needed functionallity from this hbm2java class.
+
+ Iterator allPropertiesIterator = pc.getAllPropertiesIterator();
+ while ( allPropertiesIterator.hasNext() ) {
+ Property property = (Property) allPropertiesIterator.next();
+ String candidate = property.getName();
+ if (prefix.length() == 0 || candidate.startsWith(prefix)) {
+ HQLCompletionProposal proposal = createStartWithCompletionProposal( prefix, cursorPosition, HQLCompletionProposal.PROPERTY, candidate );
+ proposal.setEntityName( cmd.getEntityName() );
+ proposal.setPropertyName( candidate );
+ hcc.accept( proposal);
+ }
+ }
+ }
+
+ private HQLCompletionProposal createStartWithCompletionProposal(String prefix, int cursorPosition, int kind, String candidate) {
+ HQLCompletionProposal proposal = new HQLCompletionProposal(kind, cursorPosition);
+ proposal.setCompletion( candidate.substring( prefix.length() ) );
+ proposal.setSimpleName( candidate );
+ proposal.setReplaceEnd( cursorPosition );
+ proposal.setReplaceStart( cursorPosition );
+ return proposal;
+ }
+
+ /** returns PersistentClass for path. Can be null if path is an imported non-mapped class */
+ private PersistentClass getPersistentClass(String path) {
+ if(path==null) return null;
+ String entityName = (String) cfg.getImports().get( path );
+ if(entityName==null) {
+ return null;
+ } else {
+ return cfg.getClassMapping( entityName );
+ }
+ }
+
+ public String getCanonicalPath(List qts, String name) {
+ Map alias2Type = new HashMap();
+ for (Iterator iter = qts.iterator(); iter.hasNext();) {
+ EntityNameReference qt = (EntityNameReference) iter.next();
+ alias2Type.put(qt.getAlias(), qt.getEntityName());
+ }
+ if (qts.size() == 1) {
+ EntityNameReference visible = (EntityNameReference) qts.get(0);
+ String alias = visible.getAlias();
+ if (name.equals(alias)) {
+ return visible.getEntityName();
+ } else if (alias == null || alias.length() == 0 || alias.equals(visible.getEntityName())) {
+ return visible.getEntityName() + "/" + name;
+ }
+ }
+ return getCanonicalPath(new HashSet(), alias2Type, name);
+ }
+
+ private String getCanonicalPath(Set resolved, Map alias2Type, String name) {
+ if (resolved.contains(name)) {
+ // To prevent a stack overflow
+ return name;
+ }
+ resolved.add(name);
+ String type = (String) alias2Type.get(name);
+ if (type != null) {
+ return name.equals(type) ? name : getCanonicalPath(resolved, alias2Type, type);
+ }
+ int idx = name.lastIndexOf('.');
+ if (idx == -1) {
+ return type != null ? type : name;
+ }
+ String baseName = name.substring(0, idx);
+ String prop = name.substring(idx + 1);
+ if (isAliasKnown(alias2Type, baseName)) {
+ return getCanonicalPath(resolved, alias2Type, baseName) + "/" + prop;
+ } else {
+ return name;
+ }
+ }
+
+ private static boolean isAliasKnown(Map alias2Type, String alias) {
+ if (alias2Type.containsKey(alias)) {
+ return true;
+ }
+ int idx = alias.lastIndexOf('.');
+ if (idx == -1) {
+ return false;
+ }
+ return isAliasKnown(alias2Type, alias.substring(0, idx));
+ }
+
+ private Value getNextAttributeType(String type, String attributePath) {
+ PersistentClass cmd = getPersistentClass( type );
+ if (cmd == null) {
+ return null;
+ }
+ String attribute;
+ int idx = attributePath.indexOf('/');
+ if (idx == -1) {
+ attribute = attributePath;
+ } else {
+ attribute = attributePath.substring(0, idx);
+ }
+
+ String idName = cmd.getIdentifierProperty()==null?null:cmd.getIdentifierProperty().getName();
+ if (attribute.equals(idName)) {
+ return cmd.getIdentifierProperty().getValue();
+ }
+ Property property = cmd.getProperty( attribute );
+ return property==null?null:property.getValue();
+ }
+
+ private Value getNextAttributeType(Component t, String attributeName) {
+ int idx = attributeName.indexOf('/');
+ if (idx != -1) {
+ attributeName = attributeName.substring(0, idx);
+ }
+ Iterator names = t.getPropertyIterator();
+ int i = 0;
+ while ( names.hasNext() ) {
+ Property element = (Property) names.next();
+ String name = element.getName();
+ if (attributeName.equals(name)) {
+ return element.getValue();
+ }
+ i++;
+ }
+ return null;
+ }
+
+ void addPropertiesToList(Component t, String prefix, int cursorPosition, IHQLCompletionRequestor hcc) {
+ if (t == null) {
+ return;
+ }
+ Iterator props = t.getPropertyIterator();
+ int i = 0;
+ while ( props.hasNext() ) {
+ Property element = (Property) props.next();
+ String candidate = element.getName();
+ if (candidate.startsWith(prefix)) {
+ HQLCompletionProposal proposal = createStartWithCompletionProposal( prefix, cursorPosition, HQLCompletionProposal.PROPERTY, candidate );
+ //proposal.setEntityName( cmd.getEntityName() ); ...we don't know here..TODO: pass in the "path"
+ proposal.setPropertyName( candidate );
+ hcc.accept( proposal);
+ }
+ i++;
+ }
+ }
+
+ private void findMatchingWords(int cursorPosition, String prefix, String[] words, int kind, IHQLCompletionRequestor hcc) {
+ int i = Arrays.binarySearch(words, prefix);
+ if(i<0) {
+ i = Math.abs(i+1);
+ }
+
+ for (int cnt = i; cnt < words.length; cnt++) {
+ String word = words[cnt];
+ if(word.startsWith(prefix)) {
+ HQLCompletionProposal proposal = createStartWithCompletionProposal( prefix, cursorPosition, kind, word );
+ hcc.accept( proposal);
+ } else {
+ break;
+ }
+ }
+ }
+
+}
Added: trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/EntityNameReference.java
===================================================================
--- trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/EntityNameReference.java 2006-05-19 14:44:33 UTC (rev 9937)
+++ trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/EntityNameReference.java 2006-05-19 14:44:38 UTC (rev 9938)
@@ -0,0 +1,43 @@
+package org.hibernate.tool.ide.completion;
+
+/**
+ * Class that represents an alias to some entityname in a HQL statement. e.g. "Product as p" or "Product p"
+ *
+ * Should not be used by external clients.
+ *
+ * @author leon, Max Rydahl Andersen
+ */
+public class EntityNameReference {
+
+ private String alias;
+
+ private String entityName;
+
+ public EntityNameReference(String type, String alias) {
+ this.entityName = type;
+ this.alias = alias;
+ }
+
+ /**
+ *
+ * @return The alias, the "p" in "Product as p"
+ */
+ public String getAlias() {
+ return alias;
+ }
+
+ /**
+ *
+ * @return the entityname, the "Product" in "Product as b"
+ */
+ public String getEntityName() {
+ return entityName;
+ }
+
+ public String toString() {
+ return alias + ":" + entityName;
+ }
+
+
+
+}
Added: trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/HQLAnalyzer.java
===================================================================
--- trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/HQLAnalyzer.java 2006-05-19 14:44:33 UTC (rev 9937)
+++ trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/HQLAnalyzer.java 2006-05-19 14:44:38 UTC (rev 9938)
@@ -0,0 +1,388 @@
+package org.hibernate.tool.ide.completion;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import org.hibernate.hql.antlr.HqlSqlTokenTypes;
+
+/**
+ * The HQLAnalyzer can answer certain questions about a HQL String.
+ *
+ * @author leon, max...@jb...
+ */
+public class HQLAnalyzer {
+
+ /** Defines the HQL keywords. Based on hql.g antlr grammer in 2005 ;) */
+ private static String[] hqlKeywords = { "between", "class", "delete",
+ "desc", "distinct", "elements", "escape", "exists", "false",
+ "fetch", "from", "full", "group", "having", "in", "indices",
+ "inner", "insert", "into", "is", "join", "left", "like", "new",
+ "not", "null", "or", "order", "outer", "properties", "right",
+ "select", "set", "some", "true", "union", "update", "versioned",
+ "where", "and", "or", "as","on", "with",
+
+ // -- SQL tokens --
+ // These aren't part of HQL, but recognized by the lexer. Could be
+ // usefull for having SQL in the editor..but for now we keep them out
+ // "case", "end", "else", "then", "when",
+
+
+ // -- EJBQL tokens --
+ "both", "empty", "leading", "member", "object", "of", "trailing",
+ };
+
+
+ /**
+ * built-in function names. Various normal builtin functions in SQL/HQL.
+ * Maybe sShould try and do this dynamically based on dialect or
+ * sqlfunctionregistry
+ */
+ private static String[] builtInFunctions = {
+ // standard sql92 functions
+ "substring", "locate", "trim", "length", "bit_length", "coalesce",
+ "nullif", "abs", "mod", "sqrt",
+ "upper",
+ "lower",
+ "cast",
+ "extract",
+
+ // time functions mapped to ansi extract
+ "second", "minute", "hour", "day",
+ "month",
+ "year",
+
+ "str",
+
+ // misc functions - based on oracle dialect
+ "sign", "acos", "asin", "atan", "cos", "cosh", "exp", "ln", "sin",
+ "sinh", "stddev", "sqrt", "tan", "tanh", "variance",
+
+ "round", "trunc", "ceil", "floor",
+
+ "chr", "initcap", "lower", "ltrim", "rtrim", "soundex", "upper",
+ "ascii", "length", "to_char", "to_date",
+
+ "current_date", "current_time", "current_timestamp", "lastday",
+ "sysday", "systimestamp", "uid", "user",
+
+ "rowid", "rownum",
+
+ "concat", "instr", "instrb", "lpad", "replace", "rpad", "substr",
+ "substrb", "translate",
+
+ "substring", "locate", "bit_length", "coalesce",
+
+ "atan2", "log", "mod", "nvl", "nvl2", "power",
+
+ "add_months", "months_between", "next_day",
+
+ "max", "min", };
+
+ static {
+ // to allow binary search
+ Arrays.sort(builtInFunctions);
+ Arrays.sort(hqlKeywords);
+ }
+
+ protected SimpleHQLLexer getLexer(char chars[], int end) {
+ return new AntlrSimpleHQLLexer(chars,end);
+ }
+
+ protected SimpleHQLLexer getLexer(char chars[]) {
+ return new AntlrSimpleHQLLexer(chars,chars.length);
+ }
+
+ /**
+ * Returns true if the position is at a location where an entityname makes sense.
+ * e.g. "from Pr| where x"
+ * @param query
+ * @param cursorPosition
+ * @return
+ */
+ public boolean shouldShowEntityNames(String query, int cursorPosition) {
+ return shouldShowEntityNames( query.toCharArray(), cursorPosition );
+ }
+
+ public boolean shouldShowEntityNames(char chars[], int cursorPosition) {
+ SimpleHQLLexer lexer = getLexer( chars, cursorPosition );
+ int tokenId = -1;
+ boolean show = false;
+ while ((tokenId = lexer.nextTokenId()) != HqlSqlTokenTypes.EOF) {
+ if ((tokenId == HqlSqlTokenTypes.FROM ||
+ tokenId == HqlSqlTokenTypes.DELETE ||
+ tokenId == HqlSqlTokenTypes.UPDATE) &&
+ (lexer.getTokenOffset() + lexer.getTokenLength()) < cursorPosition) {
+ show = true;
+ } else if (tokenId != HqlSqlTokenTypes.DOT && tokenId != HqlSqlTokenTypes.AS && tokenId != HqlSqlTokenTypes.COMMA && tokenId != HqlSqlTokenTypes.IDENT && tokenId != HqlSqlTokenTypes.WS) {
+ show = false;
+ }
+ }
+ return show;
+ }
+
+ public List getVisibleSubQueries(char[] chars, int position) {
+ SubQueryList sqList = getSubQueries(chars, position);
+ List visible = new ArrayList();
+ for (Iterator iter = sqList.subQueries.iterator(); iter.hasNext();) {
+ SubQuery sq = (SubQuery) iter.next();
+ if (sqList.caretDepth >= sq.depth && (sq.startOffset <= position || sq.endOffset >= position)) {
+ visible.add(sq);
+ }
+ }
+ return visible;
+ }
+
+ public List getVisibleEntityNames(char[] chars, int position) {
+ List sqs = getVisibleSubQueries(chars, position);
+ List entityReferences = new ArrayList();
+ for (Iterator iter = sqs.iterator(); iter.hasNext();) {
+ SubQuery sq = (SubQuery) iter.next();
+ entityReferences.addAll(sq.getEntityNames());
+ }
+ return entityReferences;
+ }
+
+ protected SubQueryList getSubQueries(char[] query, int position) {
+ SimpleHQLLexer syntax = getLexer( query );
+ int numericId = -1;
+ List subQueries = new ArrayList();
+ int depth = 0;
+ int caretDepth = 0;
+ Map level2SubQuery = new HashMap();
+ SubQuery current = null;
+ while ((numericId = syntax.nextTokenId()) != HqlSqlTokenTypes.EOF) {
+ boolean tokenAdded = false;
+ if (numericId == HqlSqlTokenTypes.OPEN) {
+ depth++;
+ if (position > syntax.getTokenOffset()) {
+ caretDepth = depth;
+ }
+ } else if (numericId == HqlSqlTokenTypes.CLOSE) {
+ SubQuery currentDepthQuery = (SubQuery) level2SubQuery.get(new Integer(depth));
+ // We check if we have a query on the current depth.
+ // If yes, we'll have to close it
+ if (currentDepthQuery != null && currentDepthQuery.depth == depth) {
+ currentDepthQuery.endOffset = syntax.getTokenOffset();
+ currentDepthQuery.tokenIds.add(new Integer(numericId));
+ currentDepthQuery.tokenText.add(String.valueOf(query, syntax.getTokenOffset(), syntax.getTokenLength()));
+ subQueries.add(currentDepthQuery);
+ level2SubQuery.remove(new Integer(depth));
+ tokenAdded = true;
+ }
+ depth--;
+ if (position > syntax.getTokenOffset()) {
+ caretDepth = depth;
+ }
+ }
+ switch (numericId) {
+ case HqlSqlTokenTypes.FROM:
+ case HqlSqlTokenTypes.UPDATE:
+ case HqlSqlTokenTypes.DELETE:
+ case HqlSqlTokenTypes.SELECT:
+ if (!level2SubQuery.containsKey(new Integer(depth))) {
+ current = new SubQuery();
+ current.depth = depth;
+ current.startOffset = syntax.getTokenOffset();
+ level2SubQuery.put(new Integer(depth), current);
+ }
+ current.tokenIds.add(new Integer(numericId));
+ current.tokenText.add(String.valueOf(query, syntax.getTokenOffset(), syntax.getTokenLength()));
+ break;
+ default:
+ if (!tokenAdded) {
+ SubQuery sq = (SubQuery) level2SubQuery.get(new Integer(depth));
+ int i = depth;
+ while (sq == null && i >= 0) {
+ sq = (SubQuery) level2SubQuery.get(new Integer(i--));
+ }
+ if (sq != null) {
+ sq.tokenIds.add(new Integer(numericId));
+ sq.tokenText.add(String.valueOf(query, syntax.getTokenOffset(), syntax.getTokenLength()));
+ }
+ }
+ }
+ }
+ for (Iterator iter = level2SubQuery.values().iterator(); iter.hasNext();) {
+ SubQuery sq = (SubQuery) iter.next();
+ sq.endOffset = syntax.getTokenOffset() + syntax.getTokenLength();
+ subQueries.add(sq);
+ }
+ Collections.sort(subQueries);
+ SubQueryList sql = new SubQueryList();
+ sql.caretDepth = caretDepth;
+ sql.subQueries = subQueries;
+ return sql;
+ }
+
+
+ /** Returns reference name found from position and backwards in the array.
+ **/
+ public static String getEntityNamePrefix(char[] chars, int position) {
+ StringBuffer buff = new StringBuffer();
+ for (int i = position - 1; i >= 0; i--) {
+ char c = chars[i];
+ if (c == '.' || Character.isJavaIdentifierPart(c)) {
+ buff.insert(0, c);
+ } else {
+ break;
+ }
+ }
+ return buff.toString();
+ }
+
+ public static class SubQuery implements Comparable {
+
+ public int compareTo(Object s) {
+ return startOffset - ((SubQuery)s).startOffset;
+ }
+
+ private List tokenIds = new ArrayList();
+
+ private List tokenText = new ArrayList();
+
+ private int startOffset;
+
+ private int endOffset;
+
+ private int depth;
+
+ public int getTokenCount() {
+ return tokenIds.size();
+ }
+
+ public int getToken(int i) {
+ return ((Number)tokenIds.get(i)).intValue();
+ }
+
+ public String getTokenText(int i) {
+ return (String) tokenText.get(i);
+ }
+
+ public List getEntityNames() {
+ boolean afterFrom = false;
+ boolean afterJoin = false;
+ StringBuffer tableNames = new StringBuffer();
+ StringBuffer joins = new StringBuffer();
+ int i = 0;
+ boolean cont = true;
+ int lastToken = HqlSqlTokenTypes.EOF;
+ for (Iterator iter = tokenIds.iterator(); iter.hasNext();) {
+ Integer typeInteger = (Integer) iter.next();
+ int type = typeInteger.intValue();
+ if (!cont) {
+ break;
+ }
+ if (!afterFrom &&
+ (type == HqlSqlTokenTypes.FROM ||
+ type == HqlSqlTokenTypes.UPDATE ||
+ type == HqlSqlTokenTypes.DELETE)) {
+ afterFrom = true;
+ } else if (afterJoin) {
+ switch (type) {
+ case HqlSqlTokenTypes.ORDER:
+ case HqlSqlTokenTypes.WHERE:
+ case HqlSqlTokenTypes.GROUP:
+ case HqlSqlTokenTypes.HAVING:
+ cont = false;
+ break;
+ case HqlSqlTokenTypes.INNER:
+ case HqlSqlTokenTypes.OUTER:
+ case HqlSqlTokenTypes.LEFT:
+ case HqlSqlTokenTypes.RIGHT:
+ case HqlSqlTokenTypes.JOIN:
+ joins.append(",");
+ break;
+ case HqlSqlTokenTypes.COMMA:
+ joins.append(","); //TODO: we should detect this and create the list directly instead of relying on the tokenizer
+ break;
+ case HqlSqlTokenTypes.DOT:
+ joins.append(".");
+ break;
+ case HqlSqlTokenTypes.IDENT:
+ if(lastToken!=HqlSqlTokenTypes.DOT) {
+ joins.append(" ");
+ }
+ joins.append(tokenText.get(i));
+ break;
+ }
+ } else if (afterFrom) {
+ switch (type) {
+ case HqlSqlTokenTypes.ORDER:
+ case HqlSqlTokenTypes.WHERE:
+ case HqlSqlTokenTypes.GROUP:
+ case HqlSqlTokenTypes.HAVING:
+ case HqlSqlTokenTypes.SET:
+ cont = false;
+ break;
+ case HqlSqlTokenTypes.COMMA:
+ tableNames.append(","); //TODO: we should detect this and create the list directly instead of relying on the tokenizer
+ break;
+ case HqlSqlTokenTypes.DOT:
+ tableNames.append(".");
+ break;
+ case HqlSqlTokenTypes.IDENT:
+ if(lastToken!=HqlSqlTokenTypes.DOT) {
+ tableNames.append(" ");
+ }
+ tableNames.append(tokenText.get(i));
+ break;
+ case HqlSqlTokenTypes.JOIN:
+ tableNames.append(",");
+ afterJoin = true;
+ break;
+ default:
+ break;
+ }
+ }
+ i++;
+ lastToken = type;
+ }
+ List tables = new ArrayList();
+ addEntityReferences(tables, tableNames);
+ addEntityReferences(tables, joins);
+ return tables;
+ }
+
+ private void addEntityReferences(final List tables, final StringBuffer tableNames) {
+ StringTokenizer tableTokenizer = new StringTokenizer(tableNames.toString(), ",");
+ while (tableTokenizer.hasMoreTokens()) {
+ String table = tableTokenizer.nextToken().trim();
+ if (table.indexOf(' ') == -1 && table.length() > 0) {
+ tables.add(new EntityNameReference(table, table));
+ } else {
+ StringTokenizer aliasTokenizer = new StringTokenizer(table, " ");
+ if (aliasTokenizer.countTokens() >= 2) {
+ String type = aliasTokenizer.nextToken().trim();
+ String alias = aliasTokenizer.nextToken().trim();
+ if (type.length() > 0 && alias.length() > 0) {
+ tables.add(new EntityNameReference(type, alias));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ static class SubQueryList {
+
+ int caretDepth;
+
+ List subQueries;
+ }
+
+
+ static String[] getHQLKeywords() {
+ return hqlKeywords;
+ }
+
+ static String[] getHQLFunctionNames() {
+ return builtInFunctions;
+ }
+
+}
Added: trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/HQLCodeAssist.java
===================================================================
--- trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/HQLCodeAssist.java 2006-05-19 14:44:33 UTC (rev 9937)
+++ trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/HQLCodeAssist.java 2006-05-19 14:44:38 UTC (rev 9938)
@@ -0,0 +1,103 @@
+package org.hibernate.tool.ide.completion;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.hibernate.cfg.Configuration;
+
+public class HQLCodeAssist implements IHQLCodeAssist {
+
+ private Configuration configuration;
+ private ConfigurationCompletion completion;
+
+ public HQLCodeAssist(Configuration configuration) {
+ this.configuration = configuration;
+ completion = new ConfigurationCompletion(configuration);
+ }
+
+ public void codeComplete(String query, int position, IHQLCompletionRequestor collector) {
+
+ int prefixStart = findNearestWhiteSpace(query, position);
+ String prefix = query.substring( prefixStart, position );
+
+ boolean showEntityNames;
+ try {
+ showEntityNames = new HQLAnalyzer().shouldShowEntityNames( query, position );
+
+ if(showEntityNames) {
+ if(hasConfiguration()) {
+ completion.getMatchingImports( prefix, position, collector );
+ } else {
+ collector.completionFailure("Configuration not available nor open");
+ }
+ } else {
+ List visible = new HQLAnalyzer().getVisibleEntityNames( query.toCharArray(), position );
+ int dotIndex = prefix.lastIndexOf(".");
+ if (dotIndex == -1) {
+ // It's a simple path, not a dot separated one (find aliases that matches)
+ for (Iterator iter = visible.iterator(); iter.hasNext();) {
+ EntityNameReference qt = (EntityNameReference) iter.next();
+ String alias = qt.getAlias();
+ if (alias.startsWith(prefix)) {
+ HQLCompletionProposal completionProposal = new HQLCompletionProposal(HQLCompletionProposal.ALIAS_REF, position);
+ completionProposal.setCompletion( alias.substring( prefix.length() ) );
+ completionProposal.setReplaceStart( position );
+ completionProposal.setReplaceEnd( position+0 );
+ completionProposal.setSimpleName( alias );
+ completionProposal.setShortEntityName( qt.getEntityName() );
+ if(hasConfiguration()) {
+ String importedName = (String) getConfiguration().getImports().get( qt.getEntityName() );
+ completionProposal.setEntityName( importedName );
+ }
+ collector.accept( completionProposal );
+ }
+ }
+ } else {
+ if(hasConfiguration()) {
+ String path = CompletionHelper.getCanonicalPath(visible, prefix.substring(0, dotIndex));
+ String propertyPrefix = prefix.substring(dotIndex + 1);
+ completion.getMatchingProperties( path, propertyPrefix, position, collector );
+ } else {
+ collector.completionFailure("Configuration not available nor open");
+ }
+ }
+
+ completion.getMatchingFunctions( prefix, position, collector );
+ completion.getMatchingKeywords( prefix, position, collector );
+
+
+ }
+ } catch(SimpleLexerException sle) {
+ collector.completionFailure( "Syntax error: " + sle.getMessage() );
+ }
+
+ }
+
+ private boolean hasConfiguration() {
+ return configuration!=null;
+ }
+
+ private Configuration getConfiguration() {
+ return configuration;
+ }
+
+ public int findNearestWhiteSpace( CharSequence doc, int start ) {
+ boolean loop = true;
+
+ int offset = 0;
+
+ int tmpOffset = start - 1;
+ while (loop && tmpOffset >= 0) {
+ char c = doc.charAt(tmpOffset);
+ if(Character.isWhitespace(c)) {
+ loop = false;
+ } else {
+ tmpOffset--;
+ }
+ }
+ offset = tmpOffset + 1;
+
+ return offset;
+ }
+
+}
Added: trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/HQLCompletionProposal.java
===================================================================
--- trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/HQLCompletionProposal.java 2006-05-19 14:44:33 UTC (rev 9937)
+++ trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/HQLCompletionProposal.java 2006-05-19 14:44:38 UTC (rev 9938)
@@ -0,0 +1,193 @@
+package org.hibernate.tool.ide.completion;
+
+
+
+public class HQLCompletionProposal {
+
+ static final char[] NO_CHAR = new char[0];
+
+ public static final int ENTITY_NAME = 1;
+ public static final int PROPERTY = 2;
+ public static final int KEYWORD = 3;
+ public static final int FUNCTION = 4;
+ public static final int ALIAS_REF = 5; // ref to an alias name, e.g. "bar" in "from Bar as bar where b|"
+
+ protected static final int FIRST_KIND = ENTITY_NAME;
+ protected static final int LAST_KIND = ALIAS_REF;
+
+ /**
+ * Kind of completion request.
+ */
+ private int completionKind;
+
+ /**
+ * Offset in original buffer where ICodeAssist.codeComplete() was
+ * requested.
+ */
+ private int completionLocation;
+
+ /**
+ * Completion string; defaults to empty string.
+ */
+ private String completion = "";
+
+ /**
+ * Start position (inclusive) of source range in original buffer
+ * to be replaced by completion string;
+ * defaults to empty subrange at [0,0).
+ */
+ private int replaceStart = 0;
+
+ /**
+ * End position (exclusive) of source range in original buffer
+ * to be replaced by completion string;
+ * defaults to empty subrange at [0,0).
+ */
+ private int replaceEnd = 0;
+
+ /**
+ * Relevance rating; positive; higher means better;
+ * defaults to minimum rating.
+ */
+ private int relevance = 1;
+
+ /** The default name for the entityname, keyword, property etc. */
+ private String simpleName = "";
+
+ /** The full related entity name, the resolved shortEntityName. Can be null */
+ private String entityName = null;
+
+ /**
+ * A short entity name. e.g. the imported name.
+ * e.g. "Product" instead of "org.hibernate.model.Product"
+ * (note: a imported name can also be the long version)
+ **/
+ private String shortEntityName = null;
+
+ /**
+ * The propertyName, can be null.
+ */
+ private String propertyName = null;
+
+ public String getCompletion() {
+ return completion;
+ }
+
+ public void setCompletion(String completion) {
+ this.completion = completion;
+ }
+
+ public int getCompletionKind() {
+ return completionKind;
+ }
+
+ public void setCompletionKind(int completionKind) {
+ this.completionKind = completionKind;
+ }
+
+ public int getCompletionLocation() {
+ return completionLocation;
+ }
+
+ public void setCompletionLocation(int completionLocation) {
+ this.completionLocation = completionLocation;
+ }
+
+ public int getRelevance() {
+ return relevance;
+ }
+
+ public void setRelevance(int relevance) {
+ this.relevance = relevance;
+ }
+
+ public int getReplaceEnd() {
+ return replaceEnd;
+ }
+
+ public void setReplaceEnd(int replaceEnd) {
+ this.replaceEnd = replaceEnd;
+ }
+
+ public int getReplaceStart() {
+ return replaceStart;
+ }
+
+ public void setReplaceStart(int replaceStart) {
+ this.replaceStart = replaceStart;
+ }
+
+ public HQLCompletionProposal(int kind, int cursorPosition) {
+ this.completionKind = kind;
+ this.completionLocation = cursorPosition;
+ }
+
+ public String getSimpleName() {
+ return simpleName;
+ }
+
+ public void setSimpleName(String simpleName) {
+ this.simpleName = simpleName;
+ }
+
+ public String toString() {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append('[');
+ switch(this.completionKind) {
+ case ENTITY_NAME :
+ buffer.append("ENTITY_NAME");
+ break;
+ case PROPERTY:
+ buffer.append("PROPERTY");
+ break;
+ case KEYWORD:
+ buffer.append("KEYWORD");
+ break;
+ default :
+ buffer.append("<Unknown type>");
+ break;
+
+ }
+ buffer.append("]{completion:"); //$NON-NLS-1$
+ if (this.completion != null) buffer.append(this.completion);
+ buffer.append(", simpleName:"); //$NON-NLS-1$
+ if (this.simpleName != null) buffer.append(this.simpleName);
+ buffer.append(", ["); //$NON-NLS-1$
+ buffer.append(this.replaceStart);
+ buffer.append(',');
+ buffer.append(this.replaceEnd);
+ buffer.append("], relevance="); //$NON-NLS-1$
+ buffer.append(this.relevance);
+ buffer.append('}');
+ return buffer.toString();
+ }
+
+ public String getEntityName() {
+ return entityName;
+ }
+
+ public void setEntityName(String entityName) {
+ this.entityName = entityName;
+ }
+
+ public String getShortEntityName() {
+ return shortEntityName;
+ }
+
+ public void setShortEntityName(String shortEntityName) {
+ this.shortEntityName = shortEntityName;
+ }
+
+ public String getPropertyName() {
+ return propertyName;
+ }
+
+ public void setPropertyName(String propertyName) {
+ this.propertyName = propertyName;
+ }
+
+
+
+
+
+}
Added: trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/IHQLCodeAssist.java
===================================================================
--- trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/IHQLCodeAssist.java 2006-05-19 14:44:33 UTC (rev 9937)
+++ trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/IHQLCodeAssist.java 2006-05-19 14:44:38 UTC (rev 9938)
@@ -0,0 +1,19 @@
+package org.hibernate.tool.ide.completion;
+
+/**
+ * Interface for code assist on HQL strings.
+ *
+ * @author Max Rydahl Andersen
+ *
+ */
+public interface IHQLCodeAssist {
+
+ /**
+ *
+ * @param query the query string (full or partial)
+ * @param position the cursor position inside the query string
+ * @param requestor requestor on which the codeassist will call methods with information about proposals.
+ */
+ void codeComplete(String query, int position, IHQLCompletionRequestor requestor);
+
+}
Added: trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/IHQLCompletionRequestor.java
===================================================================
--- trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/IHQLCompletionRequestor.java 2006-05-19 14:44:33 UTC (rev 9937)
+++ trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/IHQLCompletionRequestor.java 2006-05-19 14:44:38 UTC (rev 9938)
@@ -0,0 +1,16 @@
+package org.hibernate.tool.ide.completion;
+
+
+/**
+ * The interface to implement to collect completion proposals.
+ *
+ * @author Max Rydahl Andersen
+ *
+ */
+public interface IHQLCompletionRequestor {
+
+ boolean accept(HQLCompletionProposal proposal);
+
+ void completionFailure(String errorMessage);
+
+}
Added: trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/SimpleHQLLexer.java
===================================================================
--- trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/SimpleHQLLexer.java 2006-05-19 14:44:33 UTC (rev 9937)
+++ trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/SimpleHQLLexer.java 2006-05-19 14:44:38 UTC (rev 9938)
@@ -0,0 +1,17 @@
+package org.hibernate.tool.ide.completion;
+
+/**
+ * Minimal lexer interface that allows HqlAnalyzer to work.
+ *
+ * @author Max Rydahl Andersen
+ */
+public interface SimpleHQLLexer {
+
+
+ int nextTokenId() throws SimpleLexerException;
+
+ int getTokenOffset();
+
+ int getTokenLength();
+
+}
Added: trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/SimpleLexerException.java
===================================================================
--- trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/SimpleLexerException.java 2006-05-19 14:44:33 UTC (rev 9937)
+++ trunk/HibernateExt/tools/src/java/org/hibernate/tool/ide/completion/SimpleLexerException.java 2006-05-19 14:44:38 UTC (rev 9938)
@@ -0,0 +1,27 @@
+package org.hibernate.tool.ide.completion;
+
+/**
+ * Exception that can be thrown when the lexer encounters errors (such as syntax errors etc.)
+ *
+ * @author Max Rydahl Andersen
+ *
+ */
+public class SimpleLexerException extends RuntimeException {
+
+ public SimpleLexerException() {
+ super();
+ }
+
+ public SimpleLexerException(String message, Throwable cause) {
+ super( message, cause );
+ }
+
+ public SimpleLexerException(String message) {
+ super( message );
+ }
+
+ public SimpleLexerException(Throwable cause) {
+ super( cause );
+ }
+
+}
Added: trunk/HibernateExt/tools/src/test/org/hibernate/tool/ide/completion/CompletionHelperTest.java
===================================================================
--- trunk/HibernateExt/tools/src/test/org/hibernate/tool/ide/completion/CompletionHelperTest.java 2006-05-19 14:44:33 UTC (rev 9937)
+++ trunk/HibernateExt/tools/src/test/org/hibernate/tool/ide/completion/CompletionHelperTest.java 2006-05-19 14:44:38 UTC (rev 9938)
@@ -0,0 +1,60 @@
+/*
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.hibernate.tool.ide.completion;
+
+import java.util.ArrayList;
+import java.util.List;
+import junit.framework.TestCase;
+
+/**
+ * @author leon
+ */
+public class CompletionHelperTest extends TestCase {
+
+ public CompletionHelperTest() {
+ }
+
+ public void testGetCanonicalPath() {
+ List qts = new ArrayList();
+ qts.add(new EntityNameReference("Article", "art"));
+ qts.add(new EntityNameReference("art.descriptions", "descr"));
+ qts.add(new EntityNameReference("descr.name", "n"));
+ assertEquals("Invalid path", "Article/descriptions/name/locale", CompletionHelper.getCanonicalPath(qts, "n.locale"));
+ assertEquals("Invalid path", "Article/descriptions", CompletionHelper.getCanonicalPath(qts, "descr"));
+ //
+ qts.clear();
+ qts.add(new EntityNameReference("com.company.Clazz", "clz"));
+ qts.add(new EntityNameReference("clz.attr", "a"));
+ assertEquals("Invalid path", "com.company.Clazz/attr", CompletionHelper.getCanonicalPath(qts, "a"));
+ //
+ qts.clear();
+ qts.add(new EntityNameReference("Agga", "a"));
+ assertEquals("Invalid path", "Agga", CompletionHelper.getCanonicalPath(qts, "a"));
+ }
+
+ public void testStackOverflowInGetCanonicalPath() {
+ List qts = new ArrayList();
+ qts.add(new EntityNameReference("Article", "art"));
+ qts.add(new EntityNameReference("art.stores", "store"));
+ qts.add(new EntityNameReference("store.articles", "art"));
+ // This should not result in a stack overflow
+ CompletionHelper.getCanonicalPath(qts, "art");
+ }
+
+
+}
Added: trunk/HibernateExt/tools/src/test/org/hibernate/tool/ide/completion/HQLCompletionProposalComparator.java
===================================================================
--- trunk/HibernateExt/tools/src/test/org/hibernate/tool/ide/completion/HQLCompletionProposalComparator.java 2006-05-19 14:44:33 UTC (rev 9937)
+++ trunk/HibernateExt/tools/src/test/org/hibernate/tool/ide/completion/HQLCompletionProposalComparator.java 2006-05-19 14:44:38 UTC (rev 9938)
@@ -0,0 +1,13 @@
+package org.hibernate.tool.ide.completion;
+
+import java.util.Comparator;
+
+public class HQLCompletionProposalComparator implements Comparator {
+
+ public int compare(Object o1, Object o2) {
+ HQLCompletionProposal p1 = (HQLCompletionProposal) o1;
+ HQLCompletionProposal p2 = (HQLCompletionProposal) o2;
+ return p1.getSimpleName().compareTo( p2.getSimpleName() );
+ }
+
+}
Added: trunk/HibernateExt/tools/src/test/org/hibernate/tool/ide/completion/HqlAnalyzerTest.java
===================================================================
--- trunk/HibernateExt/tools/src/test/org/hibernate/tool/ide/completion/HqlAnalyzerTest.java 2006-05-19 14:44:33 UTC (rev 9937)
+++ trunk/HibernateExt/tools/src/test/org/hibernate/tool/ide/completion/HqlAnalyzerTest.java 2006-05-19 14:44:38 UTC (rev 9938)
@@ -0,0 +1,260 @@
+/*
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.hibernate.tool.ide.completion;
+
+import java.util.Iterator;
+import java.util.List;
+import junit.framework.TestCase;
+
+/**
+ * @author leon
+ */
+public class HqlAnalyzerTest extends TestCase {
+
+ public HqlAnalyzerTest() {
+ }
+
+ public void testShouldShowTables() {
+ String query = "select | from";
+ doTestShouldShowTables(query, false);
+ query = "select art from | Article1, Article2";
+ doTestShouldShowTables(query, true);
+ query = "from Article1, | Article2";
+ doTestShouldShowTables(query, true);
+ query = "select a, b, c | from Article a";
+ doTestShouldShowTables(query, false);
+ query = "select a, b, c from Article a where a in (select | from";
+ doTestShouldShowTables(query, false);
+ query = "select a, b, c from Article a where a in (select a from |";
+ doTestShouldShowTables(query, true);
+ query = "select a, b, c from Article a where a in (select a from C c where c.id in (select t from | G";
+ doTestShouldShowTables(query, true);
+ query = "select a from|";
+ doTestShouldShowTables(query, false);
+ query = "\n\nfrom Article art where art.|";
+ doTestShouldShowTables(query, false);
+ query = "update |";
+ doTestShouldShowTables(query, true);
+ query = "delete |";
+ doTestShouldShowTables(query, true);
+ query = "select new map(item.id as id, item.description as d, bid.amount as a) from |Item item join item.bids bid\r\n" +
+ " where bid.amount > 100";
+ doTestShouldShowTables( query, true );
+
+ query = "select new map(item.id| as id, item.description as d, bid.amount as a) from |Item item join item.bids bid\r\n" +
+ " where bid.amount > 100";
+ doTestShouldShowTables( query, false );
+
+ query = "select new map(item.id as id, item.description as d, bid.amount as a) from Item item join item|.bids bid\r\n" +
+ " where bid.amount > 100";
+ doTestShouldShowTables( query, false );
+
+ query = "from org.|hibernate";
+ doTestShouldShowTables( query, true );
+
+ query = "from \n\r\r\n" +
+ "org.|hibernate";
+ doTestShouldShowTables( query, true );
+
+ query = "from \n\r\r\n" +
+ "org.hibernate \n\r where |";
+ doTestShouldShowTables( query, false );
+
+ query = "from \n\r\r\n" +
+ "org.hibernate \n\r | where ";
+ doTestShouldShowTables( query, true );
+
+ }
+
+ public void testTableNamePrefix() {
+ doTestPrefix("select a fromtable.substring(0, i0) Art|, Bart", "Art");
+ doTestPrefix("from |", "");
+ doTestPrefix("select a, b, c from Art,|", "");
+ doTestPrefix("select u from | Garga", "");
+ doTestPrefix("select t from A.B.C.D.|", "A.B.C.D.");
+ doTestPrefix("from Goro|boro, Zoroor", "Goro");
+ }
+
+ public void testSubQueries() {
+ doTestSubQueries("select a", 1);
+ doTestSubQueries("fr", 0);
+ doTestSubQueries("from Article a", 1);
+ doTestSubQueries("select a from A, B, C", 1);
+ doTestSubQueries("select a from T a where a.id in ( select c from C c)", 2);
+ doTestSubQueries("select c where c.id in (select D from D D)", 2);
+ doTestSubQueries("select d from D d where d.id in (select a.id from A a where a.id in (select b.id from B b", 3);
+ }
+
+ public void testVisibleSubQueries() {
+ doTestVisibleSubQueries("select | from A a join a.b b", 1);
+ doTestVisibleSubQueries("select | from A a join a.b b where b.id in (select c.id from C c)", 1);
+ doTestVisibleSubQueries("select a from A a join a.b b where b.id in (select c.id from | C c)", 2);
+ doTestVisibleSubQueries("select a from A a join a.b b where b.id in (select c.id from C c) and b.| > 2", 1);
+ doTestVisibleSubQueries("select a from A a where | a.id in (select b.id from B b where b.id in (select c.id", 1);
+ doTestVisibleSubQueries("select a from A a where a.id in (select | b.id from B b where b.id in (select c.id", 2);
+ doTestVisibleSubQueries("select a from A a where a.id in (select b.id from B b where b.id in (select c.id |", 3);
+ }
+
+ public void doTestVisibleSubQueries(String query, int size) {
+ char[] cs = query.replaceAll("\\|", "").toCharArray();
+ List visible = new HQLAnalyzer().getVisibleSubQueries(cs, query.indexOf("|"));
+ assertEquals("Invalid visible query size", size, visible.size());
+ }
+
+ private void doTestSubQueries(String query, int size) {
+ char[] cs = query.toCharArray();
+ List l = new HQLAnalyzer().getSubQueries(query.toCharArray(), 0).subQueries;
+ assertEquals("Incorrent subqueries count", size, l.size());
+ }
+
+ private void doTestPrefix(String query, String prefix) {
+ assertEquals(prefix, HQLAnalyzer.getEntityNamePrefix(query.toCharArray(), query.indexOf("|")));
+ }
+
+ private void doTestShouldShowTables(String query, boolean expectedValue) {
+ char[] ch = query.replaceAll("\\|", "").toCharArray();
+ if (expectedValue) {
+ assertTrue(new HQLAnalyzer().shouldShowEntityNames(ch, getCaretPosition(query)));
+ } else {
+ assertFalse(new HQLAnalyzer().shouldShowEntityNames(ch, getCaretPosition(query)...
[truncated message content] |