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.completion.impl.xref;
043:
044: import java.io.File;
045: import java.io.IOException;
046: import javax.swing.text.BadLocationException;
047: import javax.swing.text.Document;
048: import javax.swing.text.StyledDocument;
049: import org.netbeans.api.lexer.TokenSequence;
050: import org.netbeans.cnd.api.lexer.CndLexerUtilities;
051: import org.netbeans.cnd.api.lexer.CppTokenId;
052: import org.netbeans.editor.BaseDocument;
053: import org.netbeans.editor.SyntaxSupport;
054: import org.netbeans.editor.TokenItem;
055: import org.netbeans.editor.Utilities;
056: import org.netbeans.modules.cnd.api.model.CsmFile;
057: import org.netbeans.modules.cnd.api.model.CsmFriendFunction;
058: import org.netbeans.modules.cnd.api.model.CsmFunction;
059: import org.netbeans.modules.cnd.api.model.CsmFunctionDefinition;
060: import org.netbeans.modules.cnd.api.model.CsmInclude;
061: import org.netbeans.modules.cnd.api.model.CsmObject;
062: import org.netbeans.modules.cnd.api.model.CsmScope;
063: import org.netbeans.modules.cnd.api.model.CsmScopeElement;
064: import org.netbeans.modules.cnd.api.model.util.CsmKindUtilities;
065: import org.netbeans.modules.cnd.api.model.xref.CsmReference;
066: import org.netbeans.modules.cnd.api.model.xref.CsmReferenceKind;
067: import org.netbeans.modules.cnd.api.model.xref.CsmReferenceResolver.Scope;
068: import org.netbeans.modules.cnd.completion.cplusplus.CsmCompletionProvider;
069: import org.netbeans.modules.cnd.completion.cplusplus.hyperlink.CsmHyperlinkProvider;
070: import org.netbeans.modules.cnd.completion.cplusplus.hyperlink.CsmIncludeHyperlinkProvider;
071: import org.netbeans.modules.cnd.completion.cplusplus.utils.Token;
072: import org.netbeans.modules.cnd.completion.cplusplus.utils.TokenUtilities;
073: import org.netbeans.modules.cnd.completion.csm.CompletionUtilities;
074: import org.netbeans.modules.cnd.completion.csm.CsmOffsetResolver;
075: import org.netbeans.modules.cnd.completion.csm.CsmOffsetUtilities;
076: import org.netbeans.modules.cnd.editor.cplusplus.CCTokenContext;
077: import org.netbeans.modules.cnd.modelutil.CsmUtilities;
078: import org.netbeans.modules.editor.NbEditorUtilities;
079: import org.openide.cookies.EditorCookie;
080: import org.openide.filesystems.FileObject;
081: import org.openide.filesystems.FileUtil;
082: import org.openide.loaders.DataObject;
083: import org.openide.loaders.DataObjectNotFoundException;
084: import org.openide.text.CloneableEditorSupport;
085: import org.openide.util.UserQuestionException;
086:
087: /**
088: *
089: * @author Vladimir Voskresensky
090: */
091: public final class ReferencesSupport {
092:
093: private ReferencesSupport() {
094: }
095:
096: /**
097: * converts (line, col) into offset. Line and column info are 1-based, so
098: * the start of document is (1,1)
099: */
100: public static int getDocumentOffset(BaseDocument doc,
101: int lineIndex, int colIndex) {
102: return Utilities.getRowStartFromLineOffset(doc, lineIndex - 1)
103: + (colIndex - 1);
104: }
105:
106: public static BaseDocument getBaseDocument(final String absPath)
107: throws DataObjectNotFoundException, IOException {
108: File file = new File(absPath);
109: // convert file into file object
110: FileObject fileObject = FileUtil.toFileObject(file);
111: if (fileObject == null) {
112: return null;
113: }
114: DataObject dataObject = DataObject.find(fileObject);
115: EditorCookie cookie = (EditorCookie) dataObject
116: .getCookie(EditorCookie.class);
117: if (cookie == null) {
118: throw new IllegalStateException("Given file (\""
119: + dataObject.getName()
120: + "\") does not have EditorCookie."); // NOI18N
121: }
122:
123: StyledDocument doc = null;
124: try {
125: doc = cookie.openDocument();
126: } catch (UserQuestionException ex) {
127: ex.confirmed();
128: doc = cookie.openDocument();
129: }
130:
131: return doc instanceof BaseDocument ? (BaseDocument) doc : null;
132: }
133:
134: public static CsmObject findReferencedObject(CsmFile csmFile,
135: BaseDocument doc, int offset) {
136: return findReferencedObject(csmFile, doc, offset, null);
137: }
138:
139: /*static*/static CsmObject findOwnerObject(CsmFile csmFile,
140: BaseDocument baseDocument, int offset, Token token) {
141: CsmObject csmOwner = CsmOffsetResolver.findObject(csmFile,
142: offset);
143: return csmOwner;
144: }
145:
146: /*package*/static CsmObject findReferencedObject(CsmFile csmFile,
147: BaseDocument doc, int offset, Token jumpToken) {
148: CsmObject csmItem = null;
149: // emulate hyperlinks order
150: // first ask includes handler if offset in include sring token
151: CsmInclude incl = null;
152: jumpToken = jumpToken != null ? jumpToken : getTokenByOffset(
153: doc, offset);
154: if (jumpToken != null) {
155: switch (jumpToken.getTokenID().getNumericID()) {
156: case CCTokenContext.SYS_INCLUDE_ID:
157: case CCTokenContext.USR_INCLUDE_ID:
158: // look for include directive
159: incl = findInclude(csmFile, offset);
160: break;
161: }
162: }
163:
164: csmItem = incl == null ? null : incl.getIncludeFile();
165:
166: // if failed => ask declarations handler
167: if (csmItem == null) {
168: csmItem = findDeclaration(csmFile, doc, jumpToken, offset);
169: }
170: return csmItem;
171: }
172:
173: public static CsmInclude findInclude(CsmFile csmFile, int offset) {
174: assert (csmFile != null);
175: return CsmOffsetUtilities.findObject(csmFile.getIncludes(),
176: null, offset);
177: }
178:
179: public static CsmObject findDeclaration(final CsmFile csmFile,
180: final BaseDocument doc, Token tokenUnderOffset,
181: final int offset) {
182: // fast check, if possible
183: SyntaxSupport sup = doc.getSyntaxSupport();
184: int[] idFunBlk = null;
185: CsmObject csmItem = null;
186: try {
187: idFunBlk = NbEditorUtilities.getIdentifierAndMethodBlock(
188: doc, offset);
189: } catch (BadLocationException ex) {
190: // skip it
191: }
192: // check but not for function call
193: if (idFunBlk != null && idFunBlk.length != 3) {
194: csmItem = findDeclaration(csmFile, doc, tokenUnderOffset,
195: offset, true);
196: }
197: // then full check if needed
198: csmItem = csmItem != null ? csmItem : findDeclaration(csmFile,
199: doc, tokenUnderOffset, offset, false);
200: return csmItem;
201: }
202:
203: public static CsmObject findDeclaration(final CsmFile csmFile,
204: final BaseDocument doc, Token tokenUnderOffset,
205: final int offset, final boolean onlyLocal) {
206: assert csmFile != null;
207: tokenUnderOffset = tokenUnderOffset != null ? tokenUnderOffset
208: : getTokenByOffset(doc, offset);
209: // no token in document under offset position
210: if (tokenUnderOffset == null) {
211: return null;
212: }
213: CsmObject csmObject = null;
214: // support for overloaded operators
215: if (tokenUnderOffset.getTokenID() == CCTokenContext.OPERATOR) {
216: CsmObject foundObject = CsmOffsetResolver.findObject(
217: csmFile, offset);
218: csmObject = foundObject;
219: if (CsmKindUtilities.isFunction(csmObject)) {
220: CsmFunction decl = null;
221: if (CsmKindUtilities.isFunctionDefinition(csmObject)) {
222: decl = ((CsmFunctionDefinition) csmObject)
223: .getDeclaration();
224: } else if (CsmKindUtilities.isFriendMethod(csmObject)) {
225: decl = ((CsmFriendFunction) csmObject)
226: .getReferencedFunction();
227: }
228: if (decl != null) {
229: csmObject = decl;
230: }
231: } else {
232: csmObject = null;
233: }
234: }
235: if (csmObject == null) {
236: // try with code completion engine
237: csmObject = CompletionUtilities.findItemAtCaretPos(null,
238: doc, CsmCompletionProvider.getCompletionQuery(
239: csmFile, onlyLocal), offset);
240: }
241: return csmObject;
242: }
243:
244: private static Token getTokenByOffset(BaseDocument doc, int offset) {
245: Token token = TokenUtilities.getToken(doc, offset);
246: // if (token != null) {
247: // // try prev token if it's end of prev token and prev token is interested one
248: // if (!token.getText().contains("\n")) {
249: // token = TokenUtilities.getToken(doc, offset-1);
250: // }
251: // }
252: return token;
253: }
254:
255: /*package*/static ReferenceImpl createReferenceImpl(CsmFile file,
256: BaseDocument doc, int offset) {
257: Token token = getTokenByOffset(doc, offset);
258: ReferenceImpl ref = null;
259: if (isSupportedToken(token)) {
260: ref = createReferenceImpl(file, doc, offset, token);
261: }
262: return ref;
263: }
264:
265: /*package*/static ReferenceImpl createReferenceImpl(CsmFile file,
266: BaseDocument doc, TokenItem tokenItem) {
267: Token token = new Token(tokenItem);
268: ReferenceImpl ref = createReferenceImpl(file, doc, tokenItem
269: .getOffset(), token);
270: return ref;
271: }
272:
273: public static ReferenceImpl createReferenceImpl(CsmFile file,
274: BaseDocument doc, int offset, Token token) {
275: assert token != null;
276: assert file != null : "null file for document " + doc
277: + " on offset " + offset + " " + token;
278: ReferenceImpl ref = new ReferenceImpl(file, doc, offset, token);
279: return ref;
280: }
281:
282: private static boolean isSupportedToken(Token token) {
283: return token != null
284: && (CsmIncludeHyperlinkProvider.isSupportedToken(token) || CsmHyperlinkProvider
285: .isSupportedToken(token));
286: }
287:
288: public static Scope fastCheckScope(CsmReference ref) {
289: if (ref == null) {
290: throw new NullPointerException(
291: "null reference is not allowed");
292: }
293: CsmObject target = getTargetIfPossible(ref);
294: if (target == null) {
295: // try to resolve using only local context
296: int offset = getRefOffset(ref);
297: BaseDocument doc = getRefDocument(ref);
298: if (doc != null) {
299: Token token = getRefTokenIfPossible(ref);
300: target = findDeclaration(ref.getContainingFile(), doc,
301: token, offset, true);
302: setResolvedInfo(ref, target);
303: }
304: }
305: return getTargetScope(target);
306: }
307:
308: private static Scope getTargetScope(CsmObject obj) {
309: if (obj == null) {
310: return Scope.UNKNOWN;
311: }
312: if (isLocalElement(obj)) {
313: return Scope.LOCAL;
314: } else {
315: return Scope.GLOBAL;
316: }
317: }
318:
319: private static CsmObject getTargetIfPossible(CsmReference ref) {
320: if (ref instanceof ReferenceImpl) {
321: return ((ReferenceImpl) ref).getTarget();
322: }
323: return null;
324: }
325:
326: private static Token getRefTokenIfPossible(CsmReference ref) {
327: if (ref instanceof ReferenceImpl) {
328: return ((ReferenceImpl) ref).getToken();
329: } else {
330: return null;
331: }
332: }
333:
334: private static BaseDocument getRefDocument(CsmReference ref) {
335: if (ref instanceof ReferenceImpl) {
336: return ((ReferenceImpl) ref).getDocument();
337: } else {
338: CsmFile file = ref.getContainingFile();
339: CloneableEditorSupport ces = CsmUtilities
340: .findCloneableEditorSupport(file);
341: Document doc = null;
342: if (ces != null) {
343: doc = ces.getDocument();
344: }
345: return doc instanceof BaseDocument ? (BaseDocument) doc
346: : null;
347: }
348: }
349:
350: private static int getRefOffset(CsmReference ref) {
351: if (ref instanceof ReferenceImpl) {
352: return ((ReferenceImpl) ref).getOffset();
353: } else {
354: return (ref.getStartOffset() + ref.getEndOffset() + 1) / 2;
355: }
356: }
357:
358: private static void setResolvedInfo(CsmReference ref,
359: CsmObject target) {
360: if (target != null && (ref instanceof ReferenceImpl)) {
361: ((ReferenceImpl) ref).setTarget(target);
362: }
363: }
364:
365: private static boolean isLocalElement(CsmObject decl) {
366: assert decl != null;
367: CsmObject scopeElem = decl;
368: while (CsmKindUtilities.isScopeElement(scopeElem)) {
369: CsmScope scope = ((CsmScopeElement) scopeElem).getScope();
370: if (CsmKindUtilities.isFunction(scope)) {
371: return true;
372: } else if (CsmKindUtilities.isScopeElement(scope)) {
373: scopeElem = ((CsmScopeElement) scope);
374: } else {
375: break;
376: }
377: }
378: return false;
379: }
380:
381: static BaseDocument getDocument(CsmFile file) {
382: BaseDocument doc = null;
383: try {
384: doc = ReferencesSupport.getBaseDocument(file
385: .getAbsolutePath().toString());
386: } catch (DataObjectNotFoundException ex) {
387: ex.printStackTrace(System.err);
388: } catch (IOException ex) {
389: ex.printStackTrace(System.err);
390: }
391: return doc;
392: }
393:
394: static CsmReferenceKind getReferenceKind(CsmReference ref) {
395: CsmReferenceKind kind = CsmReferenceKind.UNKNOWN;
396: CsmObject owner = ref.getOwner();
397: if (CsmKindUtilities.isType(owner)) {
398: kind = getReferenceUsageKind(ref);
399: }
400: return kind;
401: }
402:
403: static CsmReferenceKind getReferenceUsageKind(CsmReference ref) {
404: CsmReferenceKind kind = CsmReferenceKind.DIRECT_USAGE;
405: if (ref instanceof ReferenceImpl) {
406: Document doc = getRefDocument(ref);
407: int offset = ref.getStartOffset();
408: // check previous token
409: TokenSequence<CppTokenId> ts = CndLexerUtilities
410: .getCppTokenSequence(doc, offset);
411: if (ts != null && ts.isValid()) {
412: ts.move(offset);
413: org.netbeans.api.lexer.Token<CppTokenId> token = null;
414: if (ts.movePrevious()) {
415: token = ts.offsetToken();
416: }
417: while (token != null
418: && CppTokenId.WHITESPACE_CATEGORY.equals(token
419: .id().primaryCategory())) {
420: if (ts.movePrevious()) {
421: token = ts.offsetToken();
422: } else {
423: token = null;
424: }
425: }
426: if (token != null) {
427: switch (token.id()) {
428: case DOT:
429: case DOTMBR:
430: case ARROW:
431: case ARROWMBR:
432: case SCOPE:
433: kind = CsmReferenceKind.AFTER_DEREFERENCE_USAGE;
434: }
435: }
436: }
437: }
438: return kind;
439: }
440: }
|