001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.api.languages.database;
042:
043: import java.util.ArrayList;
044: import java.util.Collection;
045: import java.util.Collections;
046: import java.util.Comparator;
047: import java.util.HashMap;
048: import java.util.Iterator;
049: import java.util.List;
050: import java.util.Map;
051:
052: import org.netbeans.api.languages.ASTItem;
053: import org.netbeans.api.languages.ASTToken;
054:
055: /**
056: *
057: * @author Jan Jancura
058: * @author Caoyuan Deng
059: */
060: public class DatabaseContext extends DatabaseItem {
061:
062: private DatabaseContext parent;
063: private String type;
064: private List<DatabaseUsage> usages;
065: private List<DatabaseContext> contexts;
066: private List<DatabaseDefinition> definitions;
067: private boolean usagesSorted = false;
068: private boolean contextsSorted = false;
069: private boolean definitionsSorted = false;
070:
071: public DatabaseContext(DatabaseContext parent, String type,
072: int offset, int endOffset) {
073: super (offset, endOffset);
074: this .parent = parent;
075: this .type = type;
076: }
077:
078: protected void setParent(DatabaseContext parent) {
079: this .parent = parent;
080: }
081:
082: public DatabaseContext getParent() {
083: return parent;
084: }
085:
086: public String getType() {
087: return type;
088: }
089:
090: public void addDefinition(DatabaseDefinition definition) {
091: definitionsCache = null;
092: if (definitions == null)
093: definitions = new ArrayList<DatabaseDefinition>();
094: definitions.add(definition);
095: definitionsSorted = false;
096: }
097:
098: public void addUsage(DatabaseUsage usage) {
099: if (usages == null)
100: usages = new ArrayList<DatabaseUsage>();
101: usages.add(usage);
102: usagesSorted = false;
103: }
104:
105: public void addUsage(ASTItem item, DatabaseDefinition definition) {
106: DatabaseUsage usage = new DatabaseUsage("", item.getOffset(),
107: item.getEndOffset());
108: definition.addUsage(usage);
109: usage.setDatabaseDefinition(definition);
110: addUsage(usage);
111: }
112:
113: public void addContext(ASTItem item, DatabaseContext context) {
114: if (contexts == null)
115: contexts = new ArrayList<DatabaseContext>();
116: contexts.add(context);
117: contextsSorted = false;
118: }
119:
120: private void addItem(DatabaseItem databaseItem) {
121: if (databaseItem instanceof DatabaseUsage) {
122: addUsage((DatabaseUsage) databaseItem);
123: } else if (databaseItem instanceof DatabaseDefinition) {
124: addDefinition((DatabaseDefinition) databaseItem);
125: } else if (databaseItem instanceof DatabaseContext) {
126: addContext(null, (DatabaseContext) databaseItem);
127: }
128: }
129:
130: private List<DatabaseDefinition> definitionsCache;
131:
132: /**
133: * All definitions from this context. Not cached - slow!!!
134: */
135: public List<DatabaseDefinition> getDefinitions() {
136: if (definitionsCache == null) {
137: if (definitions == null)
138: definitionsCache = Collections
139: .<DatabaseDefinition> emptyList();
140: else {
141: definitionsCache = new ArrayList<DatabaseDefinition>();
142: Iterator<DatabaseDefinition> it = definitions
143: .iterator();
144: while (it.hasNext()) {
145: definitionsCache.add(it.next());
146: }
147: }
148: }
149: return definitionsCache;
150: }
151:
152: public List<DatabaseDefinition> getAllVisibleDefinitions(int offset) {
153: Map<String, DatabaseDefinition> map = new HashMap<String, DatabaseDefinition>();
154: addDefinitions(map, offset);
155: return new ArrayList<DatabaseDefinition>(map.values());
156: }
157:
158: /**
159: * Returns map of all JSItem from all contexts containing given offset.
160: */
161: private void addDefinitions(Map<String, DatabaseDefinition> map,
162: int offset) {
163: if (definitions != null) {
164: Iterator<DatabaseDefinition> it = definitions.iterator();
165: while (it.hasNext()) {
166: DatabaseDefinition definition = it.next();
167: map.put(definition.getName(), definition);
168: }
169: }
170: if (contexts != null) {
171: Iterator<DatabaseContext> it = contexts.iterator();
172: while (it.hasNext()) {
173: DatabaseContext context = it.next();
174: if (context.getOffset() <= offset
175: && offset < context.getEndOffset())
176: context.addDefinitions(map, offset);
177: }
178: }
179: }
180:
181: public DatabaseItem getDatabaseItem(int offset) {
182: if (definitions != null) {
183: if (!definitionsSorted) {
184: Collections.sort(definitions, new ItemsComparator());
185: definitionsSorted = true;
186: }
187: int low = 0;
188: int high = definitions.size() - 1;
189: while (low <= high) {
190: int mid = (low + high) >> 1;
191: DatabaseDefinition middle = definitions.get(mid);
192: if (offset < middle.getOffset())
193: high = mid - 1;
194: else if (offset >= middle.getEndOffset())
195: low = mid + 1;
196: else
197: return middle;
198: }
199: }
200:
201: if (usages != null) {
202: if (!usagesSorted) {
203: Collections.sort(usages, new ItemsComparator());
204: usagesSorted = true;
205: }
206: int low = 0;
207: int high = usages.size() - 1;
208: while (low <= high) {
209: int mid = (low + high) >> 1;
210: DatabaseUsage middle = usages.get(mid);
211: if (offset < middle.getOffset())
212: high = mid - 1;
213: else if (offset >= middle.getEndOffset())
214: low = mid + 1;
215: else
216: return middle;
217: }
218: }
219:
220: if (contexts != null) {
221: if (!contextsSorted) {
222: Collections.sort(contexts, new ItemsComparator());
223: contextsSorted = true;
224: }
225: int low = 0;
226: int high = contexts.size() - 1;
227: while (low <= high) {
228: int mid = (low + high) >> 1;
229: DatabaseContext middle = contexts.get(mid);
230: if (offset < middle.getOffset())
231: high = mid - 1;
232: else if (offset >= middle.getEndOffset())
233: low = mid + 1;
234: else
235: return middle.getDatabaseItem(offset);
236: }
237: }
238: return null;
239: }
240:
241: public DatabaseContext getDatabaseContext(int offset) {
242: if (contexts == null || contexts.isEmpty())
243: return this ;
244:
245: if (!contextsSorted)
246: Collections.sort(contexts, new ItemsComparator());
247: contextsSorted = true;
248:
249: int low = 0;
250: int high = contexts.size() - 1;
251:
252: while (low <= high) {
253: int mid = (low + high) >> 1;
254: DatabaseContext middle = contexts.get(mid);
255: if (offset < middle.getOffset())
256: high = mid - 1;
257: else if (offset >= middle.getEndOffset())
258: low = mid + 1;
259: else {
260: DatabaseContext context = middle
261: .getDatabaseContext(offset);
262: if (context == null)
263: return middle;
264: return context;
265: }
266: }
267: return this ;
268: }
269:
270: /**
271: * Returns JSItem with given name closest to Context on given offset.
272: */
273: public DatabaseDefinition getDefinition(String name, int offset) {
274: if (definitions != null) {
275: if (!definitionsSorted)
276: Collections.sort(definitions, new ItemsComparator());
277: definitionsSorted = true;
278: Iterator<DatabaseDefinition> it = definitions.iterator();
279: while (it.hasNext()) {
280: DatabaseDefinition definition = it.next();
281: //if (databaseItem.getEndOffset () >= offset) break;
282: if (definition.getName().equals(name))
283: return definition;
284: }
285: }
286: if (parent != null)
287: return parent.getDefinition(name, offset);
288: return null;
289: }
290:
291: // public String getAsText () {
292: // StringBuilder sb = new StringBuilder ();
293: // getAsText (sb, "");
294: // return sb.toString ();
295: // }
296: //
297: // private void getAsText (StringBuilder sb, String indent) {
298: // sb.append (indent).append (getOffset ()).append ("\n");
299: // if (definitions != null) {
300: // Iterator<DatabaseDefinition> it = definitions.iterator ();
301: // while (it.hasNext ()) {
302: // DatabaseDefinition definition = it.next ();
303: // sb.append (indent + " ").append (definition.getName ()).append (" (").append (definition.getType ()).append (')');
304: // Iterator<DatabaseUsage> it3 = definition.getUsages ().iterator ();
305: // while (it3.hasNext ())
306: // sb.append (' ').append (it3.next ().getOffset ());
307: // sb.append ("\n");
308: // }
309: // }
310: // if (contexts != null) {
311: // Iterator<DatabaseContext> it2 = contexts.iterator ();
312: // while (it2.hasNext ()) {
313: // DatabaseContext context = it2.next ();
314: // context.getAsText (sb, indent + " ");
315: // }
316: // }
317: // sb.append (indent).append (getEndOffset ()).append ("\n");
318: // }
319:
320: public List<DatabaseContext> getContexts() {
321: if (contexts == null) {
322: return Collections.<DatabaseContext> emptyList();
323: }
324: return contexts;
325: }
326:
327: public void addContext(DatabaseContext context) {
328: if (contexts == null) {
329: contexts = new ArrayList<DatabaseContext>();
330: }
331: context.setParent(this );
332: contexts.add(context);
333: }
334:
335: public DatabaseContext getClosestContext(int offset) {
336: DatabaseContext result = null;
337: if (contexts != null) {
338: /** search children first */
339: for (DatabaseContext child : contexts) {
340: if (child.contains(offset)) {
341: result = child.getClosestContext(offset);
342: break;
343: }
344: }
345: }
346: if (result != null) {
347: return result;
348: } else {
349: if (this .contains(offset)) {
350: return this ;
351: } else {
352: /* we should return null here, since it may under a parent context's call,
353: * we shall tell the parent there is none in this and children of this
354: */
355: return null;
356: }
357: }
358: }
359:
360: private boolean contains(int offset) {
361: return offset >= getOffset() && offset < getEndOffset();
362: }
363:
364: public <T extends DatabaseDefinition> T getFirstDefinition(
365: Class<T> clazz) {
366: if (definitions == null)
367: return null;
368: for (DatabaseDefinition dfn : definitions) {
369: if (clazz.isInstance(dfn)) {
370: return (T) dfn;
371: }
372: }
373: return null;
374: }
375:
376: public <T extends DatabaseDefinition> Collection<T> getDefinitions(
377: Class<T> clazz) {
378: if (definitions == null)
379: return Collections.<T> emptyList();
380: Collection<T> result = new ArrayList<T>();
381: for (DatabaseDefinition dfn : definitions) {
382: if (clazz.isInstance(dfn)) {
383: result.add((T) dfn);
384: }
385: }
386: return result;
387: }
388:
389: public void collectDefinitionsInScope(
390: Collection<DatabaseDefinition> scopeDefinitions) {
391: if (definitions != null) {
392: scopeDefinitions.addAll(definitions);
393: }
394: if (parent != null) {
395: parent.collectDefinitionsInScope(scopeDefinitions);
396: }
397: }
398:
399: public <T extends DatabaseDefinition> T getDefinitionInScopeByName(
400: Class<T> clazz, String name) {
401: T result = null;
402: if (definitions != null) {
403: for (DatabaseDefinition dfn : definitions) {
404: if (clazz.isInstance(dfn) && name.equals(dfn.getName())) {
405: result = (T) dfn;
406: break;
407: }
408: }
409: }
410: if (result != null) {
411: return result;
412: } else {
413: if (parent != null) {
414: return parent.getDefinitionInScopeByName(clazz, name);
415: } else {
416: return null;
417: }
418: }
419: }
420:
421: public <T extends DatabaseDefinition> T getEnclosingDefinition(
422: Class<T> clazz, int offset) {
423: DatabaseContext context = getClosestContext(offset);
424: return context.getEnclosingDefinitionRecursively(clazz, offset);
425: }
426:
427: public <T extends DatabaseDefinition> T getEnclosingDefinition(
428: Class<T> clazz) {
429: return getEnclosingDefinitionRecursively(clazz, getOffset());
430: }
431:
432: private <T extends DatabaseDefinition> T getEnclosingDefinitionRecursively(
433: Class<T> clazz, final int offset) {
434: /** We are searching enclosing definition, so from it's parent context */
435: DatabaseContext parentCtx = getParent();
436: if (parentCtx != null) {
437: for (DatabaseDefinition dfn : parentCtx.getDefinitions()) {
438: if (clazz.isInstance(dfn)) {
439: DatabaseContext dfnContext = dfn.getContext();
440: if (dfnContext != null
441: && dfnContext.contains(offset)) {
442: return (T) dfn;
443: }
444: }
445: }
446: return parentCtx.getEnclosingDefinitionRecursively(clazz,
447: offset);
448: } else {
449: return null;
450: }
451:
452: }
453:
454: @Override
455: public String toString() {
456: return "Context " + getOffset() + "-" + getEndOffset();
457: }
458:
459: private static class ItemsComparator implements
460: Comparator<DatabaseItem> {
461:
462: public int compare(DatabaseItem o1, DatabaseItem o2) {
463: return o1.getOffset() < o2.getOffset() ? -1 : 1;
464: }
465: }
466: }
|