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 java.io.DataInput;
045: import java.io.DataOutput;
046: import java.io.IOException;
047: import java.util.ArrayList;
048: import java.util.Collection;
049: import java.util.Collections;
050: import java.util.HashSet;
051: import java.util.List;
052: import java.util.Map;
053: import java.util.Set;
054: import java.util.SortedMap;
055: import java.util.TreeMap;
056: import java.util.concurrent.ConcurrentHashMap;
057: import java.util.concurrent.locks.ReadWriteLock;
058: import java.util.concurrent.locks.ReentrantReadWriteLock;
059: import org.netbeans.modules.cnd.api.model.CsmClass;
060: import org.netbeans.modules.cnd.api.model.CsmDeclaration;
061: import org.netbeans.modules.cnd.api.model.CsmFile;
062: import org.netbeans.modules.cnd.api.model.CsmFriend;
063: import org.netbeans.modules.cnd.api.model.CsmFriendClass;
064: import org.netbeans.modules.cnd.api.model.CsmFriendFunction;
065: import org.netbeans.modules.cnd.api.model.CsmFunction;
066: import org.netbeans.modules.cnd.api.model.CsmOffsetableDeclaration;
067: import org.netbeans.modules.cnd.api.model.CsmUID;
068: import org.netbeans.modules.cnd.api.model.util.CsmKindUtilities;
069: import org.netbeans.modules.cnd.modelimpl.repository.DeclarationContainerKey;
070: import org.netbeans.modules.cnd.modelimpl.repository.RepositoryUtils;
071: import org.netbeans.modules.cnd.modelimpl.uid.UIDCsmConverter;
072: import org.netbeans.modules.cnd.modelimpl.uid.UIDObjectFactory;
073: import org.netbeans.modules.cnd.repository.spi.Persistent;
074: import org.netbeans.modules.cnd.repository.support.SelfPersistent;
075: import org.netbeans.modules.cnd.utils.cache.CharSequenceKey;
076: import org.netbeans.modules.cnd.modelimpl.textcache.UniqueNameCache;
077:
078: /**
079: * Storage for project declarations. Class was extracted from ProjectBase.
080: * @author Alexander Simon
081: */
082: /*package-local*/class DeclarationContainer extends ProjectComponent
083: implements Persistent, SelfPersistent {
084:
085: private SortedMap<CharSequence, Object> declarations = new TreeMap<CharSequence, Object>(
086: CharSequenceKey.Comparator);
087: private ReadWriteLock declarationsLock = new ReentrantReadWriteLock();
088:
089: private Map<CharSequence, Set<CsmUID<? extends CsmFriend>>> friends = new ConcurrentHashMap<CharSequence, Set<CsmUID<? extends CsmFriend>>>();
090:
091: /** Creates a new instance of ProjectDeclarations */
092: public DeclarationContainer(ProjectBase project) {
093: super (new DeclarationContainerKey(project.getUniqueName()
094: .toString()));
095: put();
096: }
097:
098: public DeclarationContainer(DataInput input) throws IOException {
099: super (input);
100: read(input);
101: }
102:
103: public void removeDeclaration(CsmDeclaration decl) {
104: CharSequence uniqueName = CharSequenceKey.create(decl
105: .getUniqueName());
106: // synchronized(declarations){
107: Object o = null;
108: try {
109: declarationsLock.writeLock().lock();
110: o = declarations.get(uniqueName);
111:
112: if (o instanceof CsmUID[]) {
113: @SuppressWarnings("unchecked")
114: CsmUID<CsmOffsetableDeclaration>[] uids = (CsmUID<CsmOffsetableDeclaration>[]) o;
115: int size = uids.length;
116: CsmUID<CsmOffsetableDeclaration> res = null;
117: int k = size;
118: for (int i = 0; i < size; i++) {
119: CsmUID<CsmOffsetableDeclaration> uid = uids[i];
120: if (isSameFile(uid, (CsmOffsetableDeclaration) decl)) {
121: uids[i] = null;
122: k--;
123: } else {
124: res = uid;
125: }
126: }
127: if (k == 0) {
128: declarations.remove(uniqueName);
129: } else if (k == 1) {
130: declarations.put(uniqueName, res);
131: } else {
132: @SuppressWarnings("unchecked")
133: CsmUID<CsmOffsetableDeclaration>[] newUids = new CsmUID[k];
134: k = 0;
135: for (int i = 0; i < size; i++) {
136: CsmUID<CsmOffsetableDeclaration> uid = uids[i];
137: if (uid != null) {
138: newUids[k] = uid;
139: k++;
140: }
141: }
142: declarations.put(uniqueName, newUids);
143: }
144: } else if (o instanceof CsmUID) {
145: declarations.remove(uniqueName);
146: }
147: } finally {
148: declarationsLock.writeLock().unlock();
149: }
150: removeFriend(decl);
151: // }
152: put();
153: }
154:
155: private void removeFriend(CsmDeclaration decl) {
156: if (CsmKindUtilities.isFriendClass(decl)) {
157: CsmFriendClass cls = (CsmFriendClass) decl;
158: CharSequence name = CharSequenceKey.create(cls.getName());
159: Set<CsmUID<? extends CsmFriend>> set = friends.get(name);
160: if (set != null) {
161: set.remove(cls.getUID());
162: if (set.size() == 0) {
163: friends.remove(name);
164: }
165: }
166: } else if (CsmKindUtilities.isFriendMethod(decl)) {
167: CsmFriendFunction fun = (CsmFriendFunction) decl;
168: CharSequence name = CharSequenceKey.create(fun
169: .getSignature());
170: Set<CsmUID<? extends CsmFriend>> set = friends.get(name);
171: if (set != null) {
172: set.remove(fun.getUID());
173: if (set.size() == 0) {
174: friends.remove(name);
175: }
176: }
177: }
178: }
179:
180: public void putDeclaration(CsmOffsetableDeclaration decl) {
181: CharSequence name = UniqueNameCache.getManager().getString(
182: decl.getUniqueName());
183: @SuppressWarnings("unchecked")
184: CsmUID<CsmOffsetableDeclaration> uid = RepositoryUtils
185: .put(decl);
186: assert uid != null;
187: // synchronized(declarations) {
188: try {
189: declarationsLock.writeLock().lock();
190:
191: Object o = declarations.get(name);
192: if (o instanceof CsmUID[]) {
193: @SuppressWarnings("unchecked")
194: CsmUID<CsmOffsetableDeclaration>[] uids = (CsmUID[]) o;
195: boolean find = false;
196: for (int i = 0; i < uids.length; i++) {
197: if (isSameFile(uids[i], uid)) {
198: uids[i] = uid;
199: find = true;
200: break;
201: }
202: }
203: if (!find) {
204: @SuppressWarnings("unchecked")
205: CsmUID<CsmOffsetableDeclaration>[] res = new CsmUID[uids.length + 1];
206: res[0] = uid;
207: for (int i = 0; i < uids.length; i++) {
208: res[i + 1] = uids[i];
209: }
210: declarations.put(name, res);
211: }
212: } else if (o instanceof CsmUID) {
213: @SuppressWarnings("unchecked")
214: CsmUID<CsmOffsetableDeclaration> oldUid = (CsmUID<CsmOffsetableDeclaration>) o;
215: if (isSameFile(oldUid, uid)) {
216: declarations.put(name, uid);
217: } else {
218: CsmUID[] uids = new CsmUID[] { uid, oldUid };
219: declarations.put(name, uids);
220: }
221: } else {
222: declarations.put(name, uid);
223: }
224: } finally {
225: declarationsLock.writeLock().unlock();
226: }
227: putFriend(decl);
228: // }
229: }
230:
231: private void putFriend(CsmDeclaration decl) {
232: if (CsmKindUtilities.isFriendClass(decl)) {
233: CsmFriendClass cls = (CsmFriendClass) decl;
234: CharSequence name = CharSequenceKey.create(cls.getName());
235: Set<CsmUID<? extends CsmFriend>> set = friends.get(name);
236: if (set == null) {
237: set = new HashSet<CsmUID<? extends CsmFriend>>();
238: friends.put(name, set);
239: }
240: set.add(cls.getUID());
241: } else if (CsmKindUtilities.isFriendMethod(decl)) {
242: CsmFriendFunction fun = (CsmFriendFunction) decl;
243: CharSequence name = CharSequenceKey.create(fun
244: .getSignature());
245: Set<CsmUID<? extends CsmFriend>> set = friends.get(name);
246: if (set == null) {
247: set = new HashSet<CsmUID<? extends CsmFriend>>();
248: friends.put(name, set);
249: }
250: set.add(fun.getUID());
251: }
252: put();
253: }
254:
255: public Collection<CsmOffsetableDeclaration> getDeclarationsRange(
256: CharSequence from, CharSequence to) {
257: List<CsmUID<CsmOffsetableDeclaration>> list = new ArrayList<CsmUID<CsmOffsetableDeclaration>>();
258: from = CharSequenceKey.create(from);
259: to = CharSequenceKey.create(to);
260: try {
261: declarationsLock.readLock().lock();
262: for (Map.Entry<CharSequence, Object> entry : declarations
263: .subMap(from, to).entrySet()) {
264: Object o = entry.getValue();
265: if (o instanceof CsmUID[]) {
266: CsmUID[] uids = (CsmUID[]) o;
267: for (CsmUID uid : uids) {
268: list.add(uid);
269: }
270: } else if (o instanceof CsmUID) {
271: list.add((CsmUID) o);
272: }
273: }
274: } finally {
275: declarationsLock.readLock().unlock();
276: }
277: return UIDCsmConverter.UIDsToDeclarations(list);
278: }
279:
280: public Collection<CsmFriend> findFriends(
281: CsmOffsetableDeclaration decl) {
282: CharSequence name = null;
283: if (CsmKindUtilities.isClass(decl)) {
284: CsmClass cls = (CsmClass) decl;
285: name = cls.getName();
286: } else if (CsmKindUtilities.isFunction(decl)) {
287: CsmFunction fun = (CsmFunction) decl;
288: name = fun.getSignature();
289: }
290: if (name != null) {
291: name = CharSequenceKey.create(name);
292: List<CsmUID<? extends CsmFriend>> list = new ArrayList<CsmUID<? extends CsmFriend>>();
293: try {
294: declarationsLock.readLock().lock();
295: Set<CsmUID<? extends CsmFriend>> set = friends
296: .get(name);
297: if (set != null) {
298: list.addAll(set);
299: }
300: } finally {
301: declarationsLock.readLock().unlock();
302: }
303: if (list.size() > 0) {
304: Collection<CsmFriend> res = new ArrayList<CsmFriend>();
305: for (CsmUID<? extends CsmFriend> friendUID : list) {
306: CsmFriend friend = friendUID.getObject();
307: if (CsmKindUtilities.isFriendClass(friend)) {
308: CsmFriendClass cls = (CsmFriendClass) friend;
309: if (decl.equals(cls.getReferencedClass())) {
310: res.add(cls);
311: }
312: } else if (CsmKindUtilities.isFriendMethod(friend)) {
313: CsmFriendFunction fun = (CsmFriendFunction) friend;
314: if (decl.equals(fun.getReferencedFunction())) {
315: res.add(fun);
316: }
317: }
318: }
319: return res;
320: }
321: }
322: return Collections.<CsmFriend> emptyList();
323: }
324:
325: public Collection<CsmOffsetableDeclaration> findDeclarations(
326: CharSequence uniqueName) {
327: List<CsmUID<CsmOffsetableDeclaration>> list = new ArrayList<CsmUID<CsmOffsetableDeclaration>>();
328: uniqueName = CharSequenceKey.create(uniqueName);
329: try {
330: declarationsLock.readLock().lock();
331: Object o = declarations.get(uniqueName);
332: if (o instanceof CsmUID[]) {
333: CsmUID[] uids = (CsmUID[]) o;
334: for (CsmUID uid : uids) {
335: list.add(uid);
336: }
337: } else if (o instanceof CsmUID) {
338: list.add((CsmUID) o);
339: }
340: } finally {
341: declarationsLock.readLock().unlock();
342: }
343: return UIDCsmConverter.UIDsToDeclarations(list);
344: }
345:
346: public CsmDeclaration getDeclaration(CharSequence uniqueName) {
347: CsmDeclaration result;
348: CsmUID<CsmDeclaration> uid = null;
349: uniqueName = CharSequenceKey.create(uniqueName);
350: try {
351: declarationsLock.readLock().lock();
352: Object o = declarations.get(uniqueName);
353: if (o instanceof CsmUID[]) {
354: uid = ((CsmUID[]) o)[0];
355: } else if (o instanceof CsmUID) {
356: uid = (CsmUID) o;
357: }
358: } finally {
359: declarationsLock.readLock().unlock();
360: }
361: result = UIDCsmConverter.UIDtoDeclaration(uid);
362: assert result != null || uid == null : "no declaration for UID "
363: + uid;
364: return result;
365: }
366:
367: public void clearDeclarations() {
368: try {
369: declarationsLock.writeLock().lock();
370: declarations.clear();
371: put();
372: } finally {
373: declarationsLock.writeLock().unlock();
374: }
375: }
376:
377: @Override
378: public void write(DataOutput aStream) throws IOException {
379: super .write(aStream);
380: try {
381: declarationsLock.readLock().lock();
382: UIDObjectFactory.getDefaultFactory()
383: .writeStringToArrayUIDMap(declarations, aStream,
384: false);
385: } finally {
386: declarationsLock.readLock().unlock();
387: }
388: }
389:
390: private void read(DataInput aStream) throws IOException {
391: UIDObjectFactory.getDefaultFactory().readStringToArrayUIDMap(
392: declarations, aStream, UniqueNameCache.getManager());
393: }
394:
395: private boolean isSameFile(CsmUID<CsmOffsetableDeclaration> uid1,
396: CsmUID<CsmOffsetableDeclaration> uid2) {
397: return isSameFile(uid1.getObject(), uid2.getObject());
398: }
399:
400: private boolean isSameFile(CsmUID<CsmOffsetableDeclaration> uid1,
401: CsmOffsetableDeclaration decl2) {
402: return isSameFile(uid1.getObject(), decl2);
403: }
404:
405: private boolean isSameFile(CsmOffsetableDeclaration decl1,
406: CsmOffsetableDeclaration decl2) {
407: if (decl1 != null && decl2 != null) {
408: CsmFile file1 = decl1.getContainingFile();
409: CsmFile file2 = decl2.getContainingFile();
410: if (file1 != null && file1 != null) {
411: return file1.equals(file2);
412: }
413: }
414: // assert?
415: return false;
416: }
417: }
|