001: package net.sourceforge.squirrel_sql.plugins.hibernate.completion;
002:
003: import net.sourceforge.squirrel_sql.fw.completion.util.CompletionParser;
004: import net.sourceforge.squirrel_sql.fw.completion.CompletionInfo;
005: import net.sourceforge.squirrel_sql.fw.completion.CompletionCandidates;
006: import net.sourceforge.squirrel_sql.plugins.hibernate.HibernateConnection;
007: import net.sourceforge.squirrel_sql.plugins.hibernate.mapping.MappedClassInfo;
008: import net.sourceforge.squirrel_sql.plugins.hibernate.mapping.PropertyInfo;
009: import net.sourceforge.squirrel_sql.client.session.ISyntaxHighlightTokenMatcher;
010: import net.sourceforge.squirrel_sql.client.session.SQLTokenListener;
011:
012: import java.util.ArrayList;
013: import java.util.HashMap;
014:
015: public class HQLCompletionInfoCollection implements MappingInfoProvider {
016: private ArrayList<MappedClassInfo> _mappedClassInfos;
017: private ArrayList<SimpleHQLCompletionInfo> _simpleInfos;
018:
019: /**
020: * Hint in case troubles arise: Migth need to be synchronized.
021: */
022: private ArrayList<AliasInfo> _currentAliasInfos = new ArrayList<AliasInfo>();
023:
024: private HashMap<String, MappedClassInfo> _mappedClassInfoByClassName = new HashMap<String, MappedClassInfo>();
025: private HashMap<String, MappedClassInfo> _mappedClassInfoBySimpleClassName = new HashMap<String, MappedClassInfo>();
026: private HashMap<String, SimpleHQLCompletionInfo> _simpleInfosByName = new HashMap<String, SimpleHQLCompletionInfo>();
027: private HashMap<String, String> _attributeNames = new HashMap<String, String>();
028: private HqlSyntaxHighlightTokenMatcher _hqlSyntaxHighlightTokenMatcher = new HqlSyntaxHighlightTokenMatcher(
029: this );
030:
031: private MappedClassInfo _lastFoundMappedClassInfo;
032:
033: public HQLCompletionInfoCollection(HibernateConnection con) {
034: _mappedClassInfos = con.getMappedClassInfos();
035:
036: for (MappedClassInfo mappedClassInfo : _mappedClassInfos) {
037: _mappedClassInfoByClassName.put(mappedClassInfo
038: .getClassName(), mappedClassInfo);
039: _mappedClassInfoBySimpleClassName.put(mappedClassInfo
040: .getSimpleClassName(), mappedClassInfo);
041:
042: for (String attrName : mappedClassInfo.getAttributeNames()) {
043: _attributeNames.put(attrName, attrName);
044: }
045: }
046:
047: _simpleInfos = new ArrayList<SimpleHQLCompletionInfo>();
048: _simpleInfos.addAll(HQLKeywordInfo.createInfos());
049: _simpleInfos.addAll(HQLFunctionInfo.createInfos());
050:
051: for (SimpleHQLCompletionInfo simpleInfo : _simpleInfos) {
052: _simpleInfosByName.put(simpleInfo.getCompareString(),
053: simpleInfo);
054: }
055:
056: _hqlSyntaxHighlightTokenMatcher
057: .addSQLTokenListener(new SQLTokenListener() {
058: public void tableOrViewFound(String name) {
059: onTableOrViewFound(name);
060: }
061: });
062:
063: for (MappedClassInfo mappedClassInfo : _mappedClassInfos) {
064: mappedClassInfo.initAttributesWithClassInfo(this );
065: }
066:
067: }
068:
069: public CompletionCandidates getInfosStartingWith(
070: CompletionParser parser) {
071:
072: // Tricky alias and chaining completion examples
073: //
074: // au to auftr in
075: // from Kv auftr where au
076: //
077: // au to auftr in
078: // from Kv.positionen.kv auftr where au
079: //
080: // positionen. to fields of Kv aggregate positionen in
081: // from Kv auftr where auftr.positionen.
082: //
083: // positionen. to fields of Kv aggregate positionen in
084: // from Kv where positionen.
085:
086: ArrayList<CompletionInfo> ciClasses = new ArrayList<CompletionInfo>();
087: ArrayList<CompletionInfo> ciAttrs = new ArrayList<CompletionInfo>();
088:
089: for (AliasInfo aliasInfo : _currentAliasInfos) {
090: if (aliasInfo.matches(parser)) {
091: ciClasses.add(aliasInfo);
092: }
093: ciAttrs.addAll(aliasInfo
094: .getQualifiedMatchingAttributes(parser));
095:
096: }
097:
098: for (MappedClassInfo mappedClassInfo : _mappedClassInfos) {
099: if (mappedClassInfo.matches(parser)) {
100: ciClasses.add(mappedClassInfo);
101: }
102:
103: ciAttrs.addAll(mappedClassInfo
104: .getQualifiedMatchingAttributes(parser));
105: }
106:
107: ArrayList<CompletionInfo> ret = new ArrayList<CompletionInfo>();
108: MappedClassInfo lastFoundBuf = _lastFoundMappedClassInfo;
109: if (null != lastFoundBuf) {
110: if (1 == parser.size()) {
111: ret.addAll(lastFoundBuf.getMatchingAttributes(parser));
112: } else {
113:
114: for (PropertyInfo propertyInfo : lastFoundBuf
115: .getAttributes()) {
116: if (propertyInfo.getHibernatePropertyInfo()
117: .getPropertyName().equals(
118: parser.getToken(0))) {
119: MappedClassInfo mappedClassInfo = propertyInfo
120: .getMappedClassInfo();
121: CompletionParser simpleAttrFakeParser = new CompletionParser(
122: mappedClassInfo.getClassName() + "."
123: + parser.getAllButFirst());
124: ArrayList<PropertyInfo> matchingAttributes = mappedClassInfo
125: .getQualifiedMatchingAttributes(simpleAttrFakeParser);
126: ret.addAll(matchingAttributes);
127: }
128: }
129: }
130: }
131:
132: int replacementStart;
133: String stringToReplace;
134: if (0 < ciClasses.size()) {
135: // We assume that classes and attributes won't be in the same completion list.
136: // Classes will be completed fully qualified when the user works with fully qualified class names ...
137: ret.addAll(ciClasses);
138: replacementStart = parser.getReplacementStart();
139: stringToReplace = parser.getStringToReplace();
140: } else {
141: // ... while attributes used in qualified expressions will not be completed qualified.
142: // That means for pack.Foo. the completion popup will be placed behind the last dot.
143: ret.addAll(ciAttrs);
144: replacementStart = parser.getTextTillCarret().length()
145: - parser.getLastToken().length();
146: stringToReplace = parser.getLastToken();
147: }
148:
149: for (SimpleHQLCompletionInfo simpleInfo : _simpleInfos) {
150: if (simpleInfo.matches(parser)) {
151: ret.add(simpleInfo);
152: }
153: }
154:
155: return new CompletionCandidates(ret
156: .toArray(new CompletionInfo[ret.size()]),
157: replacementStart, stringToReplace);
158:
159: }
160:
161: private void onTableOrViewFound(String name) {
162: MappedClassInfo mappedClassInfo = _mappedClassInfoBySimpleClassName
163: .get(name);
164:
165: if (null != mappedClassInfo) {
166: _lastFoundMappedClassInfo = mappedClassInfo;
167: } else {
168: _lastFoundMappedClassInfo = _mappedClassInfoByClassName
169: .get(name);
170: }
171: }
172:
173: public void setCurrentAliasInfos(ArrayList<AliasInfo> aliasInfos) {
174: _currentAliasInfos = aliasInfos;
175: }
176:
177: public MappedClassInfo getMappedClassInfoFor(String token) {
178: // Example for this code:
179: // Completion should
180: // from Kv k inner join fetch k.positionen as posses where posses.artNr = 'sdfsdf'
181:
182: CompletionParser cp = new CompletionParser(token);
183:
184: if (2 > cp.size()) {
185: return getMappedClassInfoForNonAliasedToken(cp);
186: }
187:
188: String aliasCandidate = cp.getToken(0);
189:
190: // We need this buffer because this method may be called asynchronously to the event dispatch thread
191: // What could happen is, that _currentAliasInfos ist changed.
192:
193: ArrayList<AliasInfo> buf = _currentAliasInfos;
194:
195: for (AliasInfo currentAliasInfo : buf) {
196: if (currentAliasInfo.getCompareString().equals(
197: aliasCandidate)) {
198: ArrayList<PropertyInfo> matchingAttributes = currentAliasInfo
199: .getQualifiedMatchingAttributes(cp);
200: for (PropertyInfo matchingAttribute : matchingAttributes) {
201: if (matchingAttribute.getHibernatePropertyInfo()
202: .getPropertyName()
203: .equals(cp.getLastToken())) {
204: return matchingAttribute.getMappedClassInfo();
205: }
206: }
207: }
208: }
209:
210: return getMappedClassInfoForNonAliasedToken(cp);
211: }
212:
213: private MappedClassInfo getMappedClassInfoForNonAliasedToken(
214: CompletionParser cp) {
215: for (MappedClassInfo mappedClassInfo : _mappedClassInfos) {
216: if (mappedClassInfo.matches(cp)) {
217: return mappedClassInfo;
218: }
219:
220: if (cp.getStringToParse().startsWith(
221: mappedClassInfo.getClassName())
222: || cp.getStringToParse().startsWith(
223: mappedClassInfo.getSimpleClassName())) {
224: ArrayList<PropertyInfo> matchingAttributes = mappedClassInfo
225: .getQualifiedMatchingAttributes(cp);
226: for (PropertyInfo matchingAttribute : matchingAttributes) {
227: if (matchingAttribute.getHibernatePropertyInfo()
228: .getPropertyName()
229: .equals(cp.getLastToken())) {
230: return matchingAttribute.getMappedClassInfo();
231: }
232: }
233: }
234: }
235:
236: return null;
237: }
238:
239: public boolean mayBeClassOrAliasName(String token) {
240: if (0 == token.length()) {
241: return false;
242: }
243:
244: if (false == Character.isJavaIdentifierStart(token.charAt(0))) {
245: return false;
246: }
247:
248: if (_simpleInfosByName.containsKey(token)) {
249: return false;
250: }
251:
252: for (int i = 1; i < token.length(); i++) {
253: char c = token.charAt(i);
254: if (false == Character.isJavaIdentifierPart(c) && '.' != c) {
255: return false;
256: }
257: }
258:
259: return true;
260:
261: }
262:
263: public ISyntaxHighlightTokenMatcher getHqlSyntaxHighlightTokenMatcher() {
264: return _hqlSyntaxHighlightTokenMatcher;
265: }
266:
267: public boolean isMappeadClass(String name) {
268: return _mappedClassInfoByClassName.containsKey(name)
269: || _mappedClassInfoBySimpleClassName.containsKey(name);
270: }
271:
272: public boolean isFunction(String name) {
273: SimpleHQLCompletionInfo completionInfo = _simpleInfosByName
274: .get(name);
275: return null != completionInfo
276: && completionInfo instanceof HQLFunctionInfo;
277: }
278:
279: public boolean isKeyword(String name) {
280: SimpleHQLCompletionInfo completionInfo = _simpleInfosByName
281: .get(name);
282: return null != completionInfo
283: && completionInfo instanceof HQLKeywordInfo;
284: }
285:
286: public boolean isMappedAttribute(String name) {
287: return _attributeNames.containsKey(name);
288: }
289: }
|