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.utils.cache;
043:
044: import java.util.Collections;
045: import java.util.HashMap;
046: import java.util.Map;
047:
048: /**
049: * APT string table manager
050: * Responsibility:
051: * - only one instance per String object
052: * - based on weak references to allow GC of unused strings
053: *
054: * @author Vladimir Voskresensky
055: */
056: public abstract class APTStringManager {
057: public enum CacheKind {
058: Single, Sliced
059: }
060:
061: public abstract CharSequence getString(CharSequence text);
062:
063: public abstract void dispose();
064:
065: private static final Map<String, APTStringManager> instances = Collections
066: .synchronizedMap(new HashMap<String, APTStringManager>());
067:
068: private static final int STRING_MANAGER_DEFAULT_CAPACITY = 1024;
069: private static final int STRING_MANAGER_DEFAULT_SLICED_NUMBER = 29;
070:
071: /*package*/static final String TEXT_MANAGER = "Manager of sharable texts"; // NOI18N
072: /*package*/static final int TEXT_MANAGER_INITIAL_CAPACITY = STRING_MANAGER_DEFAULT_CAPACITY;
073: /*package*/static final String FILE_PATH_MANAGER = "Manager of sharable file paths"; // NOI18N
074: /*package*/static final int FILE_PATH_MANAGER_INITIAL_CAPACITY = STRING_MANAGER_DEFAULT_CAPACITY;
075:
076: public static APTStringManager instance(String name, CacheKind kind) {
077: switch (kind) {
078: case Single:
079: return instance(name, STRING_MANAGER_DEFAULT_CAPACITY);
080: case Sliced:
081: return instance(name, STRING_MANAGER_DEFAULT_SLICED_NUMBER,
082: STRING_MANAGER_DEFAULT_CAPACITY);
083: }
084: throw new java.lang.IllegalArgumentException();
085: }
086:
087: private static APTStringManager instance(String name,
088: int initialCapacity) {
089: APTStringManager instance = instances.get(name);
090: if (instance == null) {
091: instance = new APTSingleStringManager(name, initialCapacity);
092: instances.put(name, instance);
093: }
094: return instance;
095: }
096:
097: private static APTStringManager instance(String name,
098: int sliceNumber, int initialCapacity) {
099: APTStringManager instance = instances.get(name);
100: if (instance == null) {
101: instance = new APTCompoundStringManager(name, sliceNumber,
102: initialCapacity);
103: instances.put(name, instance);
104: }
105: return instance;
106: }
107:
108: /*package*/static final class APTSingleStringManager extends
109: APTStringManager {
110: private final WeakSharedSet<CharSequence> storage;
111: private final int initialCapacity;
112: // To gebug
113: private final String name;
114:
115: /** Creates a new instance of APTStringManager */
116: private APTSingleStringManager(String name, int initialCapacity) {
117: storage = new WeakSharedSet<CharSequence>(initialCapacity);
118: this .initialCapacity = initialCapacity;
119: // To gebug
120: this .name = name;
121: }
122:
123: // we need exclusive copy of string => use "new String(String)" constructor
124: private final String lock = new String(
125: "lock in APTStringManager"); // NOI18N
126:
127: /**
128: * returns shared string instance equal to input text.
129: *
130: * @param test - interested shared string
131: * @return the shared instance of text
132: * @exception NullPointerException If the <code>text</code> parameter
133: * is <code>null</code>.
134: */
135: public final CharSequence getString(CharSequence text) {
136: if (text == null) {
137: throw new NullPointerException(
138: "null string is illegal to share"); // NOI18N
139: }
140: CharSequence outText = null;
141: synchronized (lock) {
142: outText = storage.addOrGet(text);
143: }
144: assert (outText != null);
145: assert (outText.equals(text));
146: return outText;
147: }
148:
149: public final void dispose() {
150: if (false) {
151: System.out.println("Dispose cache " + name + " "
152: + getClass().getName());
153: Object[] arr = storage.toArray();
154: Map<Class, Integer> classes = new HashMap<Class, Integer>();
155: for (Object o : arr) {
156: if (o != null) {
157: Integer i = classes.get(o.getClass());
158: if (i != null) {
159: i = new Integer(i.intValue() + 1);
160: } else {
161: i = new Integer(1);
162: }
163: classes.put(o.getClass(), i);
164: }
165: }
166: for (Map.Entry<Class, Integer> e : classes.entrySet()) {
167: System.out.println(" " + e.getValue() + " of "
168: + e.getKey().getName());
169: }
170: }
171: if (storage.size() > 0) {
172: storage.clear();
173: storage.resize(initialCapacity);
174: }
175: }
176: }
177:
178: /*package*/static final class APTCompoundStringManager extends
179: APTStringManager {
180: private final APTStringManager[] instances;
181: private final int sliceNumber; // primary number for better distribution
182: // To gebug
183: private final String name;
184:
185: /*package*/APTCompoundStringManager(String name,
186: int sliceNumber) {
187: this (name, sliceNumber,
188: APTStringManager.TEXT_MANAGER_INITIAL_CAPACITY);
189: }
190:
191: /*package*/APTCompoundStringManager(String name,
192: int sliceNumber, int initialCapacity) {
193: this .sliceNumber = sliceNumber;
194: instances = new APTStringManager[sliceNumber];
195: for (int i = 0; i < instances.length; i++) {
196: instances[i] = new APTSingleStringManager(name,
197: initialCapacity);
198: }
199: this .name = name;
200: }
201:
202: private APTStringManager getDelegate(CharSequence text) {
203: if (text == null) {
204: throw new NullPointerException(
205: "null string is illegal to share"); // NOI18N
206: }
207: int index = text.hashCode() % sliceNumber;
208: if (index < 0) {
209: index += sliceNumber;
210: }
211: return instances[index];
212: }
213:
214: public final CharSequence getString(CharSequence text) {
215: return getDelegate(text).getString(text);
216: }
217:
218: public final void dispose() {
219: for (int i = 0; i < instances.length; i++) {
220: instances[i].dispose();
221: }
222: }
223: }
224: }
|