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 java.io.DataOutput;
046: import java.io.IOException;
047: import java.util.concurrent.ConcurrentHashMap;
048: import java.util.concurrent.locks.ReadWriteLock;
049: import java.util.concurrent.locks.ReentrantReadWriteLock;
050: import org.netbeans.modules.cnd.api.model.*;
051: import java.util.*;
052: import org.netbeans.modules.cnd.modelimpl.csm.core.*;
053: import org.netbeans.modules.cnd.modelimpl.debug.DiagnosticExceptoins;
054: import org.netbeans.modules.cnd.modelimpl.debug.TraceFlags;
055: import org.netbeans.modules.cnd.modelimpl.repository.RepositoryUtils;
056: import org.netbeans.modules.cnd.modelimpl.textcache.QualifiedNameCache;
057: import org.netbeans.modules.cnd.modelimpl.uid.UIDCsmConverter;
058: import org.netbeans.modules.cnd.modelimpl.uid.UIDObjectFactory;
059: import org.netbeans.modules.cnd.modelimpl.uid.UIDUtilities;
060: import org.netbeans.modules.cnd.repository.spi.Persistent;
061: import org.netbeans.modules.cnd.repository.support.SelfPersistent;
062: import org.netbeans.modules.cnd.utils.cache.CharSequenceKey;
063: import org.netbeans.modules.cnd.modelimpl.textcache.NameCache;
064:
065: /**
066: * CsmNamespace implementation
067: * @author Vladimir Kvashin
068: */
069: public class NamespaceImpl implements CsmNamespace,
070: MutableDeclarationsContainer, Persistent, SelfPersistent,
071: Disposable {
072:
073: private static final CharSequence GLOBAL = CharSequenceKey
074: .create("$Global$"); // NOI18N)
075: // only one of project/projectUID must be used (based on USE_UID_TO_CONTAINER)
076: private/*final*/ProjectBase projectRef;// can be set in onDispose or contstructor only
077: private final CsmUID<CsmProject> projectUID;
078:
079: // only one of parent/parentUID must be used (based on USE_UID_TO_CONTAINER)
080: private/*final*/CsmNamespace parentRef;// can be set in onDispose or contstructor only
081: private final CsmUID<CsmNamespace> parentUID;
082:
083: private final CharSequence name;
084: private final CharSequence qualifiedName;
085:
086: /** maps namespaces FQN to namespaces */
087: private Map<CharSequence, CsmUID<CsmNamespace>> nestedMap = new ConcurrentHashMap<CharSequence, CsmUID<CsmNamespace>>();
088:
089: private Map<CharSequence, CsmUID<CsmOffsetableDeclaration>> declarations = new ConcurrentHashMap<CharSequence, CsmUID<CsmOffsetableDeclaration>>();
090: //private Collection/*<CsmNamespace>*/ nestedNamespaces = Collections.synchronizedList(new ArrayList/*<CsmNamespace>*/());
091:
092: // private Collection/*<CsmNamespaceDefinition>*/ definitions = new ArrayList/*<CsmNamespaceDefinition>*/();
093: private Map<CharSequence, CsmUID<CsmNamespaceDefinition>> nsDefinitions = new TreeMap<CharSequence, CsmUID<CsmNamespaceDefinition>>(
094: CharSequenceKey.Comparator);
095: private ReadWriteLock nsDefinitionsLock = new ReentrantReadWriteLock();
096:
097: private final boolean global;
098:
099: /** Constructor used for global namespace */
100: public NamespaceImpl(ProjectBase project) {
101: this .name = GLOBAL;
102: this .qualifiedName = CharSequenceKey.empty(); // NOI18N
103: this .parentUID = null;
104: this .parentRef = null;
105: this .global = true;
106: assert project != null;
107:
108: this .projectUID = UIDCsmConverter.projectToUID(project);
109: assert this .projectUID != null;
110:
111: this .projectRef = null;
112: project.registerNamespace(this );
113: }
114:
115: private static final boolean CHECK_PARENT = false;
116:
117: public NamespaceImpl(ProjectBase project, NamespaceImpl parent,
118: String name, String qualifiedName) {
119: this .name = NameCache.getManager().getString(name);
120: this .global = false;
121: assert project != null;
122:
123: this .projectUID = UIDCsmConverter.projectToUID(project);
124: assert this .projectUID != null;
125:
126: this .projectRef = null;
127: this .qualifiedName = QualifiedNameCache.getManager().getString(
128: qualifiedName);
129: // TODO: rethink once more
130: // now all classes do have namespaces
131: // // TODO: this makes parent-child relationships assymetric, that's bad;
132: // // on the other hand I dont like an idea of top-level namespaces' getParent() returning non-null
133: // // Probably the CsmProject should have 2 methods:
134: // // getGlobalNamespace() and getTopLevelNamespaces()
135: // this.parent = (parent == null || parent.isGlobal()) ? null : parent;
136: assert !CHECK_PARENT || parent != null;
137:
138: this .parentUID = UIDCsmConverter.namespaceToUID(parent);
139: assert parentUID != null || parent == null;
140:
141: this .parentRef = null;
142:
143: project.registerNamespace(this );
144: if (parent != null) {
145: // nb: this.parent should be set first, since getQualidfiedName request parent's fqn
146: parent.addNestedNamespace(this );
147: }
148: notifyCreation();
149: }
150:
151: protected void notifyCreation() {
152: assert !isGlobal();
153: Notificator.instance().registerNewNamespace(this );
154: }
155:
156: public void dispose() {
157: onDispose();
158: notifyRemove();
159: }
160:
161: private void onDispose() {
162: if (TraceFlags.RESTORE_CONTAINER_FROM_UID) {
163: // restore container from it's UID
164: this .projectRef = (ProjectBase) UIDCsmConverter
165: .UIDtoProject(this .projectUID);
166: assert this .projectRef != null || this .projectUID == null : "no object for UID "
167: + this .projectUID;
168: // restore container from it's UID
169: this .parentRef = UIDCsmConverter
170: .UIDtoNamespace(this .parentUID);
171: assert this .parentRef != null || this .parentUID == null : "no object for UID "
172: + this .parentUID;
173: }
174: }
175:
176: protected void notifyRemove() {
177: assert !isGlobal();
178: Notificator.instance().registerRemovedNamespace(this );
179: }
180:
181: private static final String UNNAMED_PREFIX = "<unnamed>"; // NOI18N
182: private Set<Integer> unnamedNrs = new HashSet<Integer>();
183:
184: public String getNameForUnnamedElement() {
185: String out = UNNAMED_PREFIX;
186: int minVal = getMinUnnamedValue();
187: if (minVal != 0) {
188: out = out + minVal;
189: }
190: unnamedNrs.add(Integer.valueOf(minVal));
191: return out;
192: }
193:
194: private int getMinUnnamedValue() {
195: for (int i = 0; i < unnamedNrs.size(); i++) {
196: if (!unnamedNrs.contains(Integer.valueOf(i))) {
197: return i;
198: }
199: }
200: return unnamedNrs.size();
201: }
202:
203: public CsmNamespace getParent() {
204: return _getParentNamespace();
205: }
206:
207: public Collection<CsmNamespace> getNestedNamespaces() {
208: Collection<CsmNamespace> out = UIDCsmConverter
209: .UIDsToNamespaces(new ArrayList(nestedMap.values()));
210: return out;
211: }
212:
213: public Collection<CsmOffsetableDeclaration> getDeclarations() {
214: Collection<CsmOffsetableDeclaration> decls = UIDCsmConverter
215: .UIDsToDeclarations(new ArrayList<CsmUID<CsmOffsetableDeclaration>>(
216: declarations.values()));
217: return decls;
218: }
219:
220: public boolean isGlobal() {
221: return global;
222: }
223:
224: public CharSequence getQualifiedName() {
225: return qualifiedName;
226: }
227:
228: /** creates or gets (if already exists) namespace with the given name and current parent */
229: public NamespaceImpl getNamespace(String name) {
230: assert name != null && name.length() != 0 : "non empty namespace should be asked";
231: String fqn = Utils.getNestedNamespaceQualifiedName(name, this ,
232: true);
233: NamespaceImpl impl = _getNestedNamespace(fqn);
234: if (impl == null) {
235: impl = new NamespaceImpl(_getProject(), this , name, fqn);
236: // it would register automatically
237: }
238: return impl;
239: }
240:
241: public CharSequence getName() {
242: return name;
243: }
244:
245: private NamespaceImpl _getNestedNamespace(CharSequence fqn) {
246: fqn = CharSequenceKey.create(fqn);
247: CsmUID<CsmNamespace> nestedNsUid = nestedMap.get(fqn);
248: NamespaceImpl out = (NamespaceImpl) UIDCsmConverter
249: .UIDtoNamespace(nestedNsUid);
250: assert out != null || nestedNsUid == null;
251: return out;
252: }
253:
254: private void addNestedNamespace(NamespaceImpl nsp) {
255: assert nsp != null;
256: CsmUID<CsmNamespace> nestedNsUid = RepositoryUtils.put(nsp);
257: assert nestedNsUid != null;
258: nestedMap.put(nsp.getQualifiedName(), nestedNsUid);
259: RepositoryUtils.put(this );
260: }
261:
262: private void removeNestedNamespace(NamespaceImpl nsp) {
263: assert nsp != null;
264: CsmUID<CsmNamespace> nestedNsUid = nestedMap.remove(nsp
265: .getQualifiedName());
266: assert nestedNsUid != null;
267: // handle unnamed namespace index
268: if (nsp.getName().length() == 0) {
269: String fqn = nsp.getQualifiedName().toString();
270: int greaterInd = fqn.lastIndexOf('>');
271: assert greaterInd >= 0;
272: if (greaterInd + 1 < fqn.length()) {
273: try {
274: Integer index = Integer.parseInt(fqn
275: .substring(greaterInd + 1));
276: unnamedNrs.remove(index);
277: } catch (NumberFormatException ex) {
278: DiagnosticExceptoins.register(ex);
279: }
280: } else {
281: unnamedNrs.remove(Integer.valueOf(0));
282: }
283: }
284: }
285:
286: public void addDeclaration(CsmOffsetableDeclaration declaration) {
287:
288: if (!ProjectBase.canRegisterDeclaration(declaration)) {
289: return;
290: }
291:
292: // TODO: remove this dirty hack!
293: if ((declaration instanceof VariableImpl)) {
294: VariableImpl v = (VariableImpl) declaration;
295: if (isMine(v)) {
296: v.setScope(this );
297: } else {
298: return;
299: }
300: }
301:
302: CharSequence uniqueName = declaration.getUniqueName();
303: CsmOffsetableDeclaration oldDecl;
304:
305: CsmUID<CsmOffsetableDeclaration> oldDeclarationUid = declarations
306: .get(uniqueName);
307: oldDecl = UIDCsmConverter.UIDtoDeclaration(oldDeclarationUid);
308: // use TraceFlags.SAFE_UID_ACCESS as workaround
309: // see IZ#101952
310: if (!TraceFlags.SAFE_UID_ACCESS) {
311: assert (oldDecl != null || oldDeclarationUid == null) : "no object for UID "
312: + oldDeclarationUid;
313: }
314:
315: // // replace declaration with new one unless
316: // // 1) it's a function 2) old one contains body 3) new one does not
317: // if( oldDecl instanceof CsmFunctionDefinition ) {
318: // if( ! (declaration instanceof CsmFunctionDefinition) ) {
319: // return;
320: // }
321: // }
322: // TODO: replace this hack with proper processing
323: if (oldDecl != null
324: && oldDecl.getKind() == CsmDeclaration.Kind.FUNCTION
325: && declaration.getKind() == CsmDeclaration.Kind.FUNCTION_DEFINITION) {
326: //CsmFunction func = (CsmFunction) oldDecl;
327: //CsmFunctionDefinition fdef = (CsmFunctionDefinition) declaration;
328: //if( ! func.getContainingFile().getName().equals(declaration.getContainingFile().getName()) ) {
329: return;
330: //}
331: }
332:
333: CsmUID<CsmOffsetableDeclaration> newDeclarationUID = UIDCsmConverter
334: .declarationToUID(declaration);
335: declarations.put(uniqueName, newDeclarationUID);
336:
337: // if( "Cursor".equals(declaration.getName()) ) {
338: // System.err.println("Cursor");
339: // }
340: // update repository
341: RepositoryUtils.put(this );
342:
343: if (oldDecl != null) { //&& oldDecl.getKind() == declaration.getKind() ) {
344: //Notificator.instance().registerChangedDeclaration(oldDecl,declaration);
345: // It's notificator responsibility of detecting change event.
346: Notificator.instance().registerRemovedDeclaration(oldDecl);
347: Notificator.instance().registerNewDeclaration(declaration);
348: } else {
349: Notificator.instance().registerNewDeclaration(declaration);
350: }
351: }
352:
353: private boolean isMine(VariableImpl v) {
354: if (FileImpl.isOfFileScope(v)) {
355: return false;
356: }
357: return true;
358: }
359:
360: public void removeDeclaration(CsmOffsetableDeclaration declaration) {
361: CsmUID<CsmOffsetableDeclaration> declarationUid = declarations
362: .remove(declaration.getUniqueName());
363: // do not clean repository, it must be done from physical container of declaration
364: if (false)
365: RepositoryUtils.remove(declarationUid);
366: // update repository
367: RepositoryUtils.put(this );
368: Notificator.instance().registerRemovedDeclaration(declaration);
369: }
370:
371: public Collection<CsmNamespaceDefinition> getDefinitions() {
372: List<CsmUID<CsmNamespaceDefinition>> uids = new ArrayList<CsmUID<CsmNamespaceDefinition>>();
373: try {
374: nsDefinitionsLock.readLock().lock();
375: uids.addAll(nsDefinitions.values());
376: } finally {
377: nsDefinitionsLock.readLock().unlock();
378: }
379: Collection<CsmNamespaceDefinition> defs = UIDCsmConverter
380: .UIDsToDeclarations(uids);
381: return defs;
382: }
383:
384: public void addNamespaceDefinition(CsmNamespaceDefinition def) {
385: CsmUID<CsmNamespaceDefinition> definitionUid = RepositoryUtils
386: .put(def);
387: try {
388: nsDefinitionsLock.writeLock().lock();
389: nsDefinitions.put(getSortKey(def), definitionUid);
390: } finally {
391: nsDefinitionsLock.writeLock().unlock();
392: }
393: // update repository
394: RepositoryUtils.put(this );
395: }
396:
397: public void removeNamespaceDefinition(CsmNamespaceDefinition def) {
398: assert !this .isGlobal();
399: boolean remove = false;
400: CsmUID<CsmNamespaceDefinition> definitionUid = null;
401: try {
402: nsDefinitionsLock.writeLock().lock();
403: definitionUid = nsDefinitions.remove(getSortKey(def));
404: } finally {
405: nsDefinitionsLock.writeLock().unlock();
406: }
407: // does not remove unregistered declaration from repository, it's responsibility of physical container
408: if (false)
409: RepositoryUtils.remove(definitionUid);
410: // update repository about itself
411: RepositoryUtils.put(this );
412: try {
413: nsDefinitionsLock.readLock().lock();
414: remove = (nsDefinitions.size() == 0);
415: } finally {
416: nsDefinitionsLock.readLock().unlock();
417: }
418: if (remove) {
419: NamespaceImpl parent = (NamespaceImpl) _getParentNamespace();
420: if (parent != null) {
421: parent.removeNestedNamespace(this );
422: }
423: _getProject().unregisterNamesace(this );
424: dispose();
425: }
426: }
427:
428: public static String getSortKey(CsmNamespaceDefinition def) {
429: StringBuilder sb = new StringBuilder(def.getContainingFile()
430: .getAbsolutePath());
431: int start = ((CsmOffsetable) def).getStartOffset();
432: String s = Integer.toString(start);
433: int gap = 8 - s.length();
434: while (gap-- > 0) {
435: sb.append('0');
436: }
437: sb.append(s);
438: sb.append(def.getName());
439: return sb.toString();
440: }
441:
442: public Collection<CsmScopeElement> getScopeElements() {
443: return (List) getDeclarations();
444: }
445:
446: public CsmProject getProject() {
447: return _getProject();
448: }
449:
450: private CsmUID<CsmNamespace> uid = null;
451:
452: public CsmUID<CsmNamespace> getUID() {
453: if (uid == null) {
454: uid = createUID();
455: }
456: return uid;
457: }
458:
459: protected CsmUID<CsmNamespace> createUID() {
460: return UIDUtilities.createNamespaceUID(this );
461: }
462:
463: private ProjectBase _getProject() {
464: ProjectBase prj = this .projectRef;
465: if (prj == null) {
466: prj = (ProjectBase) UIDCsmConverter
467: .UIDtoProject(this .projectUID);
468: assert (prj != null || this .projectUID == null) : "empty project for UID "
469: + this .projectUID;
470: }
471: return prj;
472: }
473:
474: private CsmNamespace _getParentNamespace() {
475: CsmNamespace ns = this .parentRef;
476: if (ns == null) {
477: ns = UIDCsmConverter.UIDtoNamespace(this .parentUID);
478: assert (ns != null || this .parentUID == null) : "null object for UID "
479: + this .parentUID;
480: }
481: return ns;
482: }
483:
484: @Override
485: public String toString() {
486: StringBuilder sb = new StringBuilder(getName());
487: sb.append(' ');
488: sb.append(getQualifiedName());
489: sb.append(" NamespaceImpl @"); // NOI18N
490: sb.append(hashCode());
491: return sb.toString();
492: }
493:
494: ////////////////////////////////////////////////////////////////////////////
495: // impl of persistent
496:
497: public void write(DataOutput output) throws IOException {
498: output.writeBoolean(this .global);
499:
500: UIDObjectFactory theFactory = UIDObjectFactory
501: .getDefaultFactory();
502: // not null UID
503: assert this .projectUID != null;
504: theFactory.writeUID(this .projectUID, output);
505: // can be null for global ns
506: assert !CHECK_PARENT || this .parentUID != null || isGlobal();
507: theFactory.writeUID(this .parentUID, output);
508:
509: assert this .name != null;
510: output.writeUTF(this .name.toString());
511: assert this .qualifiedName != null;
512: output.writeUTF(this .qualifiedName.toString());
513: theFactory.writeStringToUIDMap(this .nestedMap, output, true);
514: theFactory.writeStringToUIDMap(this .declarations, output, true);
515: try {
516: nsDefinitionsLock.readLock().lock();
517: theFactory.writeStringToUIDMap(this .nsDefinitions, output,
518: false);
519: } finally {
520: nsDefinitionsLock.readLock().unlock();
521: }
522: }
523:
524: public NamespaceImpl(DataInput input) throws IOException {
525: this .global = input.readBoolean();
526:
527: UIDObjectFactory theFactory = UIDObjectFactory
528: .getDefaultFactory();
529:
530: this .projectUID = theFactory.readUID(input);
531: this .parentUID = theFactory.readUID(input);
532: // not null UID
533: assert this.projectUID != null;
534: assert !CHECK_PARENT || this.parentUID != null || this.global;
535: this.projectRef = null;
536: this.parentRef = null;
537:
538: this.name = NameCache.getManager().getString(input.readUTF());
539: assert this.name != null;
540: this.qualifiedName = QualifiedNameCache.getManager().getString(
541: input.readUTF());
542: assert this.qualifiedName != null;
543: theFactory.readStringToUIDMap(this.nestedMap, input,
544: QualifiedNameCache.getManager());
545: theFactory.readStringToUIDMap(this.declarations, input,
546: QualifiedNameCache.getManager());
547: theFactory.readStringToUIDMap(this.nsDefinitions, input,
548: QualifiedNameCache.getManager());
549: }
550:
551: }
|