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;
043:
044: import java.io.DataInput;
045: import org.netbeans.modules.cnd.api.model.util.CsmKindUtilities;
046: import org.netbeans.modules.cnd.modelimpl.debug.TraceFlags;
047: import org.netbeans.modules.cnd.modelimpl.parser.FakeAST;
048: import java.util.*;
049:
050: import antlr.collections.AST;
051: import java.io.DataOutput;
052: import java.io.IOException;
053: import org.netbeans.modules.cnd.api.model.*;
054: import org.netbeans.modules.cnd.utils.cache.TextCache;
055: import org.netbeans.modules.cnd.modelimpl.parser.CsmAST;
056: import org.netbeans.modules.cnd.modelimpl.parser.generated.CPPTokenTypes;
057: import org.netbeans.modules.cnd.modelimpl.csm.core.*;
058: import org.netbeans.modules.cnd.modelimpl.debug.DiagnosticExceptoins;
059: import org.netbeans.modules.cnd.modelimpl.repository.PersistentUtils;
060: import org.netbeans.modules.cnd.modelimpl.textcache.NameCache;
061: import org.netbeans.modules.cnd.modelimpl.uid.UIDCsmConverter;
062: import org.netbeans.modules.cnd.modelimpl.uid.UIDObjectFactory;
063:
064: /**
065: *
066: * @author Vladimir Kvashin
067: */
068: public class TypeImpl extends OffsetableBase implements CsmType {
069:
070: private final byte pointerDepth;
071: private final boolean reference;
072: private final byte arrayDepth;
073: private final boolean _const;
074: private final CharSequence classifierText;
075:
076: private final List<CsmType> instantiationParams = new ArrayList();
077:
078: // FIX for lazy resolver calls
079: private CharSequence[] qname = null;
080: private int firstOffset;
081: private CsmUID<CsmClassifier> classifierUID;
082:
083: // package-local - for facory only
084: TypeImpl(CsmClassifier classifier, int pointerDepth,
085: boolean reference, int arrayDepth, AST ast, CsmFile file) {
086: super (ast, file);
087: this ._setClassifier(classifier);
088: this .pointerDepth = (byte) pointerDepth;
089: this .reference = reference;
090: this .arrayDepth = (byte) arrayDepth;
091: _const = initIsConst(ast);
092: if (classifier == null) {
093: this ._setClassifier(initClassifier(ast));
094: this .classifierText = initClassifierText(ast);
095: } else {
096: CharSequence typeName = classifier.getName();
097: if (typeName == null || typeName.length() == 0) {
098: this .classifierText = initClassifierText(ast);
099: } else {
100: this .classifierText = typeName;
101: }
102: }
103: }
104:
105: // package-local - for facory only
106: TypeImpl(AST classifier, CsmFile file, int pointerDepth,
107: boolean reference, int arrayDepth) {
108: super (classifier, file);
109: //setAst(classifier);
110: this .pointerDepth = (byte) pointerDepth;
111: this .reference = reference;
112: this .arrayDepth = (byte) arrayDepth;
113: _const = initIsConst(classifier);
114: this ._setClassifier(initClassifier(classifier));
115: this .classifierText = initClassifierText(classifier);
116: }
117:
118: @Override
119: protected CsmAST getEndAst(AST node) {
120: AST ast = node;
121: if (ast == null) {
122: return null;
123: }
124: ast = getLastNode(ast);
125: if (ast instanceof CsmAST) {
126: return (CsmAST) ast;
127: }
128: return super .getEndAst(node);
129: }
130:
131: private AST getLastNode(AST first) {
132: AST last = first;
133: for (AST token = last; token != null; token = token
134: .getNextSibling()) {
135: switch (token.getType()) {
136: case CPPTokenTypes.CSM_VARIABLE_DECLARATION:
137: case CPPTokenTypes.CSM_QUALIFIED_ID:
138: return AstUtil.getLastChildRecursively(last);
139: default:
140: last = token;
141: }
142: }
143: return null;
144: }
145:
146: public boolean isReference() {
147: return reference;
148: }
149:
150: public boolean isPointer() {
151: return pointerDepth > 0;
152: }
153:
154: public List<CsmType> getInstantiationParams() {
155: return instantiationParams;
156: }
157:
158: public boolean isInstantiation() {
159: return !instantiationParams.isEmpty();
160: }
161:
162: private static boolean initIsConst(AST node) {
163: if (node != null) {
164: for (AST token = node; token != null; token = token
165: .getNextSibling()) {
166: switch (token.getType()) {
167: case CPPTokenTypes.LITERAL_const:
168: case CPPTokenTypes.LITERAL___const:
169: return true;
170: case CPPTokenTypes.CSM_VARIABLE_DECLARATION:
171: case CPPTokenTypes.CSM_QUALIFIED_ID:
172: return false;
173: }
174: }
175: }
176: return false;
177: }
178:
179: public boolean isConst() {
180: return _const;
181: }
182:
183: public String getCanonicalText() {
184: return decorateText(classifierText, this , true, null)
185: .toString();
186: }
187:
188: @Override
189: public CharSequence getText() {
190: // TODO: resolve typedefs
191: return decorateText(classifierText, this , false, null)
192: .toString();
193: }
194:
195: protected StringBuilder getText(boolean canonical,
196: CharSequence variableNameToInsert) {
197: return decorateText(classifierText, this , canonical,
198: variableNameToInsert);
199: }
200:
201: public StringBuilder decorateText(CharSequence classifierText,
202: CsmType decorator, boolean canonical,
203: CharSequence variableNameToInsert) {
204: StringBuilder sb = new StringBuilder();
205: if (decorator.isConst()) {
206: sb.append("const "); // NOI18N
207: }
208: sb.append(classifierText);
209: for (int i = 0; i < decorator.getPointerDepth(); i++) {
210: sb.append('*');
211: }
212: if (decorator.isReference()) {
213: sb.append('&');
214: }
215: for (int i = 0; i < decorator.getArrayDepth(); i++) {
216: sb.append(canonical ? "*" : "[]"); // NOI18N
217: }
218: if (variableNameToInsert != null) {
219: sb.append(' ');
220: sb.append(variableNameToInsert);
221: }
222: return sb;
223: }
224:
225: private CharSequence initClassifierText(AST node) {
226: if (node == null) {
227: CsmClassifier classifier = _getClassifier();
228: return classifier == null ? "" : classifier.getName();
229: } else {
230: StringBuilder sb = new StringBuilder();
231: addText(sb, AstRenderer.getFirstSiblingSkipQualifiers(node));
232: return TextCache.getString(sb.toString());
233: // return sb.toString();
234: }
235: }
236:
237: private static void addText(StringBuilder sb, AST ast) {
238: if (!(ast instanceof FakeAST)) {
239: if (sb.length() > 0) {
240: sb.append(' ');
241: }
242: sb.append(ast.getText());
243: }
244: for (AST token = ast.getFirstChild(); token != null; token = token
245: .getNextSibling()) {
246: addText(sb, token);
247: }
248: }
249:
250: public CsmClassifier getClassifier() {
251: CsmClassifier res = getClassifier(null);
252: if (isInstantiation()
253: && CsmKindUtilities.isTemplate(res)
254: && !((CsmTemplate) res).getTemplateParameters()
255: .isEmpty()) {
256: res = (CsmClassifier) Instantiation.create(
257: (CsmTemplate) res, this );
258: }
259: return res;
260: }
261:
262: public CharSequence getClassifierText() {
263: return classifierText;
264: }
265:
266: public CsmClassifier getClassifier(Resolver parent) {
267: CsmClassifier classifier = _getClassifier();
268: if (classifier != null
269: && (!(classifier instanceof CsmValidable) || (((CsmValidable) classifier)
270: .isValid()))) {
271: return classifier;
272: } else {
273: _setClassifier(null);
274: if (qname != null) {
275: _setClassifier(renderClassifier(qname, parent));
276: } else if (classifierText.length() > 0) {
277: _setClassifier(renderClassifier(
278: new CharSequence[] { classifierText }, parent));
279: }
280: }
281: return _getClassifier();
282: }
283:
284: private CsmClassifier renderClassifier(CharSequence[] qname,
285: Resolver parent) {
286: CsmClassifier result = null;
287: Resolver resolver = ResolverFactory.createResolver(
288: getContainingFile(), firstOffset, parent);
289: CsmObject o = resolver.resolve(qname, Resolver.CLASSIFIER);
290: if (CsmKindUtilities.isClassifier(o)) {
291: result = (CsmClassifier) o;
292: }
293: if (result == null) {
294: result = ((ProjectBase) getContainingFile().getProject())
295: .getDummyForUnresolved(qname, getContainingFile(),
296: firstOffset);
297: }
298: return result;
299: }
300:
301: private CsmClassifier initClassifier(AST node) {
302: AST tokType = AstRenderer.getFirstSiblingSkipQualifiers(node);
303: if (tokType == null
304: || (tokType.getType() != CPPTokenTypes.CSM_TYPE_BUILTIN && tokType
305: .getType() != CPPTokenTypes.CSM_TYPE_COMPOUND)
306: && tokType.getType() != CPPTokenTypes.CSM_QUALIFIED_ID) {
307: return null;
308: }
309:
310: if (tokType.getType() == CPPTokenTypes.CSM_TYPE_BUILTIN) {
311: return BuiltinTypes.getBuiltIn(tokType);
312: } else { // tokType.getType() == CPPTokenTypes.CSM_TYPE_COMPOUND
313: try {
314: CsmAST tokFirstId = (CsmAST) tokType.getFirstChild();
315: if (tokFirstId == null) {
316: // this is unnormal; but we should be able to work even on incorrect AST
317: return null;
318: }
319:
320: firstOffset = tokFirstId.getOffset();
321: //Resolver resolver = ResolverFactory.createResolver(getContainingFile(), firstOffset);
322: // gather name components into string array
323: // for example, for std::vector new String[] { "std", "vector" }
324:
325: //TODO: we have AstRenderer.getNameTokens, it is better to use it here
326: List l = new ArrayList();
327: int templateDepth = 0;
328: for (AST namePart = tokFirstId; namePart != null; namePart = namePart
329: .getNextSibling()) {
330: if (templateDepth == 0
331: && namePart.getType() == CPPTokenTypes.ID) {
332: l.add(namePart.getText());
333: } else if (namePart.getType() == CPPTokenTypes.LESSTHAN) {
334: // the beginning of template parameters
335: templateDepth++;
336: } else if (namePart.getType() == CPPTokenTypes.GREATERTHAN) {
337: // the beginning of template parameters
338: templateDepth--;
339: } else {
340: //assert namePart.getType() == CPPTokenTypes.SCOPE;
341: if (templateDepth == 0) {
342: if (namePart.getType() != CPPTokenTypes.SCOPE) {
343: if (TraceFlags.DEBUG) {
344: StringBuilder tokenText = new StringBuilder();
345: tokenText.append('[').append(
346: namePart.getText());
347: if (namePart.getNumberOfChildren() == 0) {
348: tokenText
349: .append(", line=")
350: .append(
351: namePart
352: .getLine()); // NOI18N
353: tokenText
354: .append(", column=")
355: .append(
356: namePart
357: .getColumn()); // NOI18N
358: }
359: tokenText.append(']');
360: System.err
361: .println("Incorect token: expected '::', found "
362: + tokenText
363: .toString());
364: }
365: }
366: } else {
367: // TODO: maybe we need to filter out some more tokens
368: if (namePart.getType() == CPPTokenTypes.CSM_TYPE_BUILTIN
369: || namePart.getType() == CPPTokenTypes.CSM_TYPE_COMPOUND) {
370: instantiationParams.add(AstRenderer
371: .renderType(namePart,
372: getContainingFile()));
373: }
374: }
375: }
376: }
377: qname = (String[]) l.toArray(new String[l.size()]);
378: /*CsmObject o = resolver.resolve(qname);
379: if( CsmKindUtilities.isClassifier(o) ) {
380: result = (CsmClassifier) o;
381: }
382: // else if( CsmKindUtilities.isTypedef(o) ) {
383: // CsmTypedef td = (CsmTypedef) o;
384: // CsmType type = td.getType();
385: // if( type != null ) {
386: // result = type.getClassifier();
387: // }
388: // }
389: if( result == null ) {
390: result = ((ProjectBase) getContainingFile().getProject()).getDummyForUnresolved(qname, getContainingFile(), offset);
391: }*/
392: } catch (Exception e) {
393: DiagnosticExceptoins.register(e);
394: }
395: }
396: return null;
397: }
398:
399: public int getArrayDepth() {
400: return arrayDepth;
401: }
402:
403: public int getPointerDepth() {
404: return pointerDepth;
405: }
406:
407: private CsmClassifier _getClassifier() {
408: CsmClassifier classifier = UIDCsmConverter
409: .UIDtoDeclaration(classifierUID);
410: // can be null if cached one was removed
411: return classifier;
412: }
413:
414: private void _setClassifier(CsmClassifier classifier) {
415: this .classifierUID = UIDCsmConverter
416: .declarationToUID(classifier);
417: assert (classifierUID != null || classifier == null);
418: }
419:
420: public boolean isBuiltInBased(boolean resolveTypeChain) {
421: CsmClassifier classifier;
422: if (resolveTypeChain) {
423: classifier = getClassifier();
424: if (CsmKindUtilities.isTypedef(classifier)) {
425: return ((CsmTypedef) classifier).getType()
426: .isBuiltInBased(true);
427: }
428: } else {
429: classifier = _getClassifier();
430: }
431: return CsmKindUtilities.isBuiltIn(classifier);
432: }
433:
434: @Override
435: public String toString() {
436: return "TYPE " + getText() + getOffsetString(); // NOI18N
437: }
438:
439: //package-local
440: /**
441: * Return display text for a variable of this type
442: * (we actually need this for function pointers, where simple typeName+' '+variableName does not work.
443: */
444: String getVariableDisplayName(String variableName) {
445: return decorateText(classifierText, this , false, variableName)
446: .toString();
447: }
448:
449: ////////////////////////////////////////////////////////////////////////////
450: // impl of persistent
451:
452: @Override
453: public void write(DataOutput output) throws IOException {
454: super .write(output);
455: output.writeInt(pointerDepth);
456: output.writeBoolean(reference);
457: output.writeInt(arrayDepth);
458: output.writeBoolean(_const);
459: assert this .classifierText != null;
460: output.writeUTF(classifierText.toString());
461:
462: PersistentUtils.writeStrings(qname, output);
463: output.writeInt(firstOffset);
464: PersistentUtils.writeTypes(instantiationParams, output);
465: UIDObjectFactory.getDefaultFactory().writeUID(classifierUID,
466: output);
467: }
468:
469: public TypeImpl(DataInput input) throws IOException {
470: super (input);
471: this .pointerDepth = (byte) input.readInt();
472: this .reference = input.readBoolean();
473: this .arrayDepth = (byte) input.readInt();
474: this._const = input.readBoolean();
475: this.classifierText = NameCache.getManager().getString(
476: input.readUTF());
477: assert this.classifierText != null;
478:
479: this.qname = PersistentUtils.readStrings(input, NameCache
480: .getManager());
481: this.firstOffset = input.readInt();
482: PersistentUtils.readTypes(this.instantiationParams, input);
483: this.classifierUID = UIDObjectFactory.getDefaultFactory()
484: .readUID(input);
485: }
486:
487: }
|