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:
042: package org.netbeans.modules.cnd.modelimpl.csm.core;
043:
044: import org.netbeans.modules.cnd.api.model.*;
045: import java.util.*;
046: import org.netbeans.modules.cnd.api.model.deep.CsmDeclarationStatement;
047: import org.netbeans.modules.cnd.api.model.util.CsmBaseUtilities;
048: import org.netbeans.modules.cnd.api.model.util.CsmKindUtilities;
049: import org.netbeans.modules.cnd.modelimpl.csm.FunctionDefinitionImpl;
050: import org.netbeans.modules.cnd.modelimpl.csm.InheritanceImpl;
051: import org.netbeans.modules.cnd.modelimpl.csm.NamespaceImpl;
052: import org.netbeans.modules.cnd.modelimpl.csm.TypeImpl;
053: import org.netbeans.modules.cnd.modelimpl.csm.UsingDeclarationImpl;
054: import org.netbeans.modules.cnd.modelimpl.debug.DiagnosticExceptoins;
055: import org.netbeans.modules.cnd.utils.cache.CharSequenceKey;
056:
057: /**
058: * @author Vladimir Kvasihn
059: */
060: public class Resolver3 implements Resolver {
061: private static final int INFINITE_RECURSION = 200;
062: private static final int LIMITED_RECURSION = 5;
063:
064: private ProjectBase project;
065: private CsmFile file;
066: private int offset;
067: private final int origOffset;
068: private Resolver parentResolver;
069:
070: private List<CharSequence> usedNamespaces = new ArrayList<CharSequence>();
071: private Map<CharSequence, CsmNamespace> namespaceAliases = new HashMap<CharSequence, CsmNamespace>();
072: private Map<CharSequence, CsmDeclaration> usingDeclarations = new HashMap<CharSequence, CsmDeclaration>();
073:
074: private CsmTypedef currTypedef;
075:
076: private CharSequence[] names;
077: private int currNamIdx;
078: private int interestedKind;
079:
080: private CharSequence currName() {
081: return (names != null && currNamIdx < names.length) ? names[currNamIdx]
082: : CharSequenceKey.empty();
083: }
084:
085: private CsmNamespace containingNamespace;
086: private CsmClass containingClass;
087: private boolean contextFound = false;
088:
089: private CsmNamespace getContainingNamespace() {
090: if (!contextFound) {
091: findContext();
092: }
093: return containingNamespace;
094: }
095:
096: private CsmClass getContainingClass() {
097: if (!contextFound) {
098: findContext();
099: }
100: return containingClass;
101: }
102:
103: private void findContext() {
104: contextFound = true;
105: findContext(file.getDeclarations());
106: }
107:
108: private Set<CsmFile> visitedFiles = new HashSet<CsmFile>();
109:
110: //private CsmNamespace currentNamespace;
111:
112: public Resolver3(CsmFile file, int offset, Resolver parent) {
113: this .file = file;
114: this .offset = offset;
115: this .origOffset = offset;
116: parentResolver = parent;
117: this .project = (ProjectBase) file.getProject();
118: }
119:
120: public Resolver3(CsmOffsetable context, Resolver parent) {
121: this .file = context.getContainingFile();
122: this .offset = context.getStartOffset();
123: this .origOffset = offset;
124: parentResolver = parent;
125: this .project = (ProjectBase) context.getContainingFile()
126: .getProject();
127: }
128:
129: private CsmClassifier findClassifier(CsmNamespace ns,
130: CharSequence qulifiedNamePart) {
131: CsmClassifier result = null;
132: while (ns != null && result == null) {
133: String fqn = ns.getQualifiedName() + "::"
134: + qulifiedNamePart; // NOI18N
135: result = findClassifier(fqn);
136: ns = ns.getParent();
137: }
138: return result;
139: }
140:
141: private CsmClassifier findClassifier(CharSequence qualifiedName) {
142: CsmClassifier result = project.findClassifier(qualifiedName);
143: if (result == null) {
144: for (Iterator iter = project.getLibraries().iterator(); iter
145: .hasNext()
146: && result == null;) {
147: CsmProject lib = (CsmProject) iter.next();
148: result = lib.findClassifier(qualifiedName);
149: }
150: }
151: return result;
152: }
153:
154: public CsmNamespace findNamespace(CharSequence qualifiedName) {
155: CsmNamespace result = project.findNamespace(qualifiedName);
156: if (result == null) {
157: for (Iterator iter = project.getLibraries().iterator(); iter
158: .hasNext()
159: && result == null;) {
160: CsmProject lib = (CsmProject) iter.next();
161: result = lib.findNamespace(qualifiedName);
162: }
163: }
164: return result;
165: }
166:
167: private void findContext(Iterable declarations) {
168: for (Iterator it = declarations.iterator(); it.hasNext();) {
169: CsmDeclaration decl = (CsmDeclaration) it.next();
170: if (decl.getKind() == CsmDeclaration.Kind.NAMESPACE_DEFINITION) {
171: CsmNamespaceDefinition nd = (CsmNamespaceDefinition) decl;
172: if (nd.getStartOffset() < this .offset
173: && this .offset < nd.getEndOffset()) {
174: containingNamespace = nd.getNamespace();
175: findContext(nd.getDeclarations());
176: }
177: } else if (decl.getKind() == CsmDeclaration.Kind.CLASS
178: || decl.getKind() == CsmDeclaration.Kind.STRUCT
179: || decl.getKind() == CsmDeclaration.Kind.UNION) {
180:
181: CsmClass cls = (CsmClass) decl;
182: if (cls.getStartOffset() < this .offset
183: && this .offset < cls.getEndOffset()) {
184: containingClass = cls;
185: }
186: } else if (decl.getKind() == CsmDeclaration.Kind.FUNCTION_DEFINITION) {
187: CsmFunctionDefinition fd = (CsmFunctionDefinition) decl;
188: if (fd.getStartOffset() < this .offset
189: && this .offset < fd.getEndOffset()) {
190: CsmNamespace ns = CsmBaseUtilities
191: .getFunctionNamespace(fd);
192: if (ns != null && !ns.isGlobal()) {
193: containingNamespace = ns;
194: }
195: CsmFunction fun = getFunctionDeclaration(fd);
196: if (fun != null
197: && CsmKindUtilities
198: .isMethodDeclaration(fun)) {
199: containingClass = ((CsmMethod) fun)
200: .getContainingClass();
201: }
202: }
203: }
204: }
205: }
206:
207: private CsmFunction getFunctionDeclaration(CsmFunctionDefinition fd) {
208: if (fd instanceof FunctionDefinitionImpl) {
209: if (isRecursionOnResolving(INFINITE_RECURSION)) {
210: return null;
211: }
212: return ((FunctionDefinitionImpl) fd).getDeclaration(this );
213: }
214: return fd.getDeclaration();
215: }
216:
217: private boolean isRecursionOnResolving(int maxRecursion) {
218: Resolver3 parent = (Resolver3) parentResolver;
219: int count = 0;
220: while (parent != null) {
221: if (parent.origOffset == origOffset
222: && parent.file.equals(file)) {
223: return true;
224: }
225: parent = (Resolver3) parent.parentResolver;
226: count++;
227: if (count > maxRecursion) {
228: return true;
229: }
230: }
231: return false;
232: }
233:
234: protected void gatherMaps(CsmFile file) {
235: if (file == null || visitedFiles.contains(file)) {
236: return;
237: }
238: visitedFiles.add(file);
239:
240: for (Iterator<CsmInclude> iter = file.getIncludes().iterator(); iter
241: .hasNext();) {
242: CsmInclude inc = iter.next();
243: CsmFile incFile = inc.getIncludeFile();
244: if (incFile != null) {
245: int oldOffset = offset;
246: offset = Integer.MAX_VALUE;
247: gatherMaps(incFile);
248: offset = oldOffset;
249: }
250: }
251: gatherMaps(file.getDeclarations());
252: }
253:
254: protected void gatherMaps(Iterable declarations) {
255: for (Iterator it = declarations.iterator(); it.hasNext();) {
256: Object o = it.next();
257: assert o instanceof CsmOffsetable;
258: try {
259: int start = ((CsmOffsetable) o).getStartOffset();
260: int end = ((CsmOffsetable) o).getEndOffset();
261: if (start >= this .offset) {
262: break;
263: }
264: //assert o instanceof CsmScopeElement;
265: if (o instanceof CsmScopeElement) {
266: gatherMaps((CsmScopeElement) o, end);
267: } else {
268: if (FileImpl.reportErrors) {
269: System.err
270: .println("Expected CsmScopeElement, got "
271: + o);
272: }
273: }
274: } catch (NullPointerException ex) {
275: if (FileImpl.reportErrors) {
276: // FIXUP: do not crush on NPE
277: System.err
278: .println("Unexpected NULL element in declarations collection");
279: DiagnosticExceptoins.register(ex);
280: }
281: }
282: }
283: }
284:
285: private void doProcessTypedefsInUpperNamespaces(
286: CsmNamespaceDefinition nsd) {
287: for (Iterator iter = nsd.getDeclarations().iterator(); iter
288: .hasNext();) {
289: CsmDeclaration decl = (CsmDeclaration) iter.next();
290: if (decl.getKind() == CsmDeclaration.Kind.NAMESPACE_DEFINITION) {
291: processTypedefsInUpperNamespaces((CsmNamespaceDefinition) decl);
292: } else if (decl.getKind() == CsmDeclaration.Kind.TYPEDEF) {
293: CsmTypedef typedef = (CsmTypedef) decl;
294: if (CharSequenceKey.Comparator.compare(currName(),
295: typedef.getName()) == 0) {
296: currTypedef = typedef;
297: }
298: }
299: }
300: }
301:
302: private void processTypedefsInUpperNamespaces(
303: CsmNamespaceDefinition nsd) {
304: if (CharSequenceKey.Comparator.compare(nsd.getName(),
305: currName()) == 0) {
306: currNamIdx++;
307: doProcessTypedefsInUpperNamespaces(nsd);
308: } else {
309: CsmNamespace cns = getContainingNamespace();
310: if (cns != null) {
311: if (cns.equals(nsd.getNamespace())) {
312: doProcessTypedefsInUpperNamespaces(nsd);
313: }
314: }
315: }
316: }
317:
318: /**
319: * It is quaranteed that element.getStartOffset < this.offset
320: */
321: protected void gatherMaps(CsmScopeElement element, int end) {
322:
323: CsmDeclaration.Kind kind = (element instanceof CsmDeclaration) ? ((CsmDeclaration) element)
324: .getKind()
325: : null;
326: if (kind == CsmDeclaration.Kind.NAMESPACE_DEFINITION) {
327: CsmNamespaceDefinition nsd = (CsmNamespaceDefinition) element;
328: if (nsd.getName().length() == 0) {
329: // this is unnamed namespace and it should be considered as
330: // it declares using itself
331: usedNamespaces.add(nsd.getQualifiedName());
332: }
333: if (this .offset < end) {
334: //currentNamespace = nsd.getNamespace();
335: gatherMaps(nsd.getDeclarations());
336: } else if (needClassifiers()) {
337: processTypedefsInUpperNamespaces(nsd);
338: }
339: } else if (kind == CsmDeclaration.Kind.NAMESPACE_ALIAS) {
340: CsmNamespaceAlias alias = (CsmNamespaceAlias) element;
341: namespaceAliases.put(alias.getAlias(), alias
342: .getReferencedNamespace());
343: } else if (kind == CsmDeclaration.Kind.USING_DECLARATION) {
344: CsmDeclaration decl = resolveUsingDeclaration((CsmUsingDeclaration) element);
345: if (decl != null) {
346: CharSequence id;
347: if (decl.getKind() == CsmDeclaration.Kind.FUNCTION
348: || decl.getKind() == CsmDeclaration.Kind.FUNCTION_DEFINITION) {
349: // TODO: decide how to resolve functions
350: id = ((CsmFunction) decl).getSignature();
351: } else {
352: id = decl.getName();
353: }
354: usingDeclarations.put(id, decl);
355: }
356: } else if (kind == CsmDeclaration.Kind.USING_DIRECTIVE) {
357: CsmUsingDirective udir = (CsmUsingDirective) element;
358: usedNamespaces.add(udir.getName()); // getReferencedNamespace()
359: } else if (element instanceof CsmDeclarationStatement) {
360: CsmDeclarationStatement ds = (CsmDeclarationStatement) element;
361: if (ds.getStartOffset() < this .offset) {
362: gatherMaps(((CsmDeclarationStatement) element)
363: .getDeclarators());
364: }
365: } else if (element instanceof CsmScope) {
366: if (this .offset < end) {
367: gatherMaps(((CsmScope) element).getScopeElements());
368: }
369: } else if (kind == CsmDeclaration.Kind.TYPEDEF
370: && needClassifiers()) {
371: CsmTypedef typedef = (CsmTypedef) element;
372: if (CharSequenceKey.Comparator.compare(currName(), typedef
373: .getName()) == 0) {
374: currTypedef = typedef;
375: }
376: }
377: }
378:
379: private CsmDeclaration resolveUsingDeclaration(
380: CsmUsingDeclaration udecl) {
381: CsmDeclaration decl = null;
382: if (udecl instanceof UsingDeclarationImpl) {
383: if (isRecursionOnResolving(LIMITED_RECURSION)) {
384: return null;
385: }
386: decl = ((UsingDeclarationImpl) udecl)
387: .getReferencedDeclaration(this );
388: }
389: return decl;
390: }
391:
392: public CsmObject resolve(CharSequence qualified, int interestedKind) {
393: return resolve(Utils.splitQualifiedName(qualified.toString()),
394: interestedKind);
395: }
396:
397: /**
398: * Resolver class or namespace name.
399: * Why class or namespace? Because in usage of kind org::vk::test
400: * you don't know which is class and which is namespace name
401: *
402: * @param nameTokens tokenized name to resolve
403: * (for example, for std::vector it is new String[] { "std", "vector" })
404: *
405: * @param context declaration within which the name found
406: *
407: * @return object of the following class:
408: * CsmClass
409: * CsmEnum
410: * CsmNamespace
411: */
412: public CsmObject resolve(CharSequence[] nameTokens,
413: int interestedKind) {
414: CsmObject result = null;
415:
416: names = nameTokens;
417: currNamIdx = 0;
418: this .interestedKind = interestedKind;
419: CsmNamespace containingNS = null;
420:
421: if (nameTokens.length == 1) {
422: if (needClassifiers()) {
423: result = findClassifier(nameTokens[0]);
424: }
425: if (result == null && needNamespaces()) {
426: result = findNamespace(nameTokens[0]);
427: }
428: if (result == null && needClassifiers()) {
429: containingNS = getContainingNamespace();
430: result = findClassifier(containingNS, nameTokens[0]);
431: }
432: if (result == null && needClassifiers()) {
433: CsmClass cls = getContainingClass();
434: result = resolveInClass(cls, nameTokens[0]);
435: if (result == null) {
436: result = resolveInBaseClasses(cls, nameTokens[0]);
437: }
438: }
439: if (result == null) {
440: currTypedef = null;
441: gatherMaps(file);
442: if (currTypedef != null && needClassifiers()) {
443: CsmType type = currTypedef.getType();
444: if (type != null) {
445: result = getTypeClassifier(type);
446: }
447: }
448:
449: if (result == null) {
450: CsmDeclaration decl = (CsmDeclaration) usingDeclarations
451: .get(CharSequenceKey.create(nameTokens[0]));
452: if (decl != null) {
453: result = decl;
454: }
455: }
456:
457: if (result == null && needClassifiers()) {
458: for (Iterator<CharSequence> iter = usedNamespaces
459: .iterator(); iter.hasNext();) {
460: String nsp = iter.next().toString();
461: String fqn = nsp + "::" + nameTokens[0]; // NOI18N
462: result = findClassifier(fqn);
463: if (result == null) {
464: result = findClassifier(containingNS, fqn);
465: }
466: if (result != null) {
467: break;
468: }
469: }
470: }
471:
472: if (result == null && needNamespaces()) {
473: Object o = namespaceAliases.get(CharSequenceKey
474: .create(nameTokens[0]));
475: if (o instanceof CsmNamespace) {
476: result = (CsmNamespace) o;
477: }
478: }
479:
480: if (result == null && needNamespaces()) {
481: for (Iterator<CharSequence> iter = usedNamespaces
482: .iterator(); iter.hasNext();) {
483: String nsp = iter.next().toString();
484: String fqn = nsp + "::" + nameTokens[0]; // NOI18N
485: result = findNamespace(fqn);
486: if (result != null) {
487: break;
488: }
489: }
490: }
491: }
492: } else if (nameTokens.length > 1) {
493: StringBuilder sb = new StringBuilder(nameTokens[0]);
494: for (int i = 1; i < nameTokens.length; i++) {
495: sb.append("::"); // NOI18N
496: sb.append(nameTokens[i]);
497: }
498: if (needClassifiers()) {
499: result = findClassifier(sb.toString());
500: }
501: if (result == null && needNamespaces()) {
502: // containingNS = getContainingNamespace();
503: // result = findClassifier(containingNS, sb.toString());
504: // }
505: // if( result == null ) {
506: result = findNamespace(sb.toString());
507: }
508: if (result == null && needClassifiers()) {
509: gatherMaps(file);
510: if (currTypedef != null) {
511: CsmType type = currTypedef.getType();
512: if (type != null) {
513: result = getTypeClassifier(type);
514: }
515: }
516: if (result == null) {
517: CsmObject first = new Resolver3(this .file,
518: this .origOffset, this ).resolve(
519: nameTokens[0], NAMESPACE);
520: if (first != null) {
521: if (first instanceof CsmNamespace) {
522: NamespaceImpl ns = (NamespaceImpl) first;
523: sb = new StringBuilder(ns
524: .getQualifiedName());
525: for (int i = 1; i < nameTokens.length; i++) {
526: sb.append("::"); // NOI18N
527: sb.append(nameTokens[i]);
528: }
529: result = findClassifier(sb.toString());
530: } else if (first instanceof CsmClass) {
531:
532: }
533: }
534: } else {
535: gatherMaps(file);
536: if (currTypedef != null) {
537: CsmType type = currTypedef.getType();
538: if (type != null) {
539: result = getTypeClassifier(type);
540: }
541: }
542: }
543: }
544: }
545: if (result == null) {
546: result = project.getDummyForUnresolved(nameTokens, file,
547: offset);
548: }
549:
550: // CsmObject curr = null;
551: // for( int i = 0; i < nameTokens.length; i++ ) {
552: // String name = nameTokens[i];
553: // }
554: return result;
555: }
556:
557: private CsmObject getTypeClassifier(CsmType type) {
558: if (type instanceof TypeImpl) {
559: if (isRecursionOnResolving(INFINITE_RECURSION)) {
560: return null;
561: }
562: return ((TypeImpl) type).getClassifier(this );
563: }
564: return type.getClassifier();
565: }
566:
567: private CsmObject resolveInBaseClasses(CsmClass cls,
568: CharSequence name) {
569: return _resolveInBaseClasses(cls, name, new HashSet<CsmClass>());
570: }
571:
572: private CsmObject _resolveInBaseClasses(CsmClass cls,
573: CharSequence name, Set<CsmClass> antiLoop) {
574: if (cls != null && cls.isValid()) {
575: for (CsmInheritance inh : cls.getBaseClasses()) {
576: CsmClass base = getInheritanceClass(inh);
577: if (base != null && !antiLoop.contains(base)) {
578: antiLoop.add(base);
579: CsmObject result = resolveInClass(base, name);
580: if (result != null) {
581: return result;
582: }
583: result = _resolveInBaseClasses(base, name, antiLoop);
584: if (result != null) {
585: return result;
586: }
587: }
588: }
589: }
590: return null;
591: }
592:
593: private CsmClass getInheritanceClass(CsmInheritance inh) {
594: if (inh instanceof InheritanceImpl) {
595: if (isRecursionOnResolving(INFINITE_RECURSION)) {
596: return null;
597: }
598: return ((InheritanceImpl) inh).getCsmClass(this );
599: }
600: return inh.getCsmClass();
601: }
602:
603: private CsmObject resolveInClass(CsmClass cls, CharSequence name) {
604: if (cls != null && cls.isValid()) {
605: String fqn = cls.getQualifiedName() + "::" + name; // NOI18N
606: return findClassifier(fqn);
607: }
608: return null;
609: }
610:
611: private boolean needClassifiers() {
612: return (interestedKind & CLASSIFIER) == CLASSIFIER;
613: }
614:
615: private boolean needNamespaces() {
616: return (interestedKind & NAMESPACE) == NAMESPACE;
617: }
618: }
|