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-2006 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.editor.ext;
043:
044: import org.netbeans.editor.StringMap;
045:
046: /**
047: * Cache holding the most commonly used strings.
048: * The unused strings are discarded when they reach the end of chain.
049: *
050: * @author Miloslav Metelka
051: * @version 1.00
052: */
053:
054: public class StringCache {
055:
056: private static final int DEFAULT_MAX_SIZE = 300;
057:
058: private static final int DEFAULT_INITIAL_CAPACITY = 701;
059:
060: int maxSize;
061:
062: int size;
063:
064: StringMap strMap;
065:
066: /** First chain member */
067: private Entry chain;
068:
069: /** Last chain member */
070: private Entry endChain;
071:
072: /** Last entry that was made free */
073: private Entry freeEntry;
074:
075: public int statQueries; // count of queries
076: public int statHits; // count of cache hits
077:
078: public StringCache() {
079: this (DEFAULT_MAX_SIZE, DEFAULT_INITIAL_CAPACITY);
080: }
081:
082: public StringCache(int maxSize) {
083: this (maxSize, 2 * maxSize);
084: }
085:
086: public StringCache(int maxSize, int initialMapCapacity) {
087: this .maxSize = maxSize;
088: strMap = new StringMap(initialMapCapacity);
089: }
090:
091: private void toStart(Entry e) {
092: if (e != chain) {
093: // chain removal
094: Entry ep = e.prev; // ep surely not null
095: Entry en = e.next;
096: if (en != null) {
097: en.prev = ep;
098: } else { // last chain member
099: endChain = ep;
100: }
101: ep.next = en;
102:
103: // insert to chain start
104: if (chain != null) {
105: e.next = chain;
106: chain.prev = e;
107: }
108: chain = e;
109: }
110: }
111:
112: public String getString(char[] chars, int offset, int len) {
113: statQueries++;
114: Object o = strMap.get(chars, offset, len);
115: String ret;
116: if (o instanceof Entry) {
117: Entry e = (Entry) o;
118: toStart(e);
119: statHits++;
120: ret = e.str;
121: } else if (o instanceof String) {
122: statHits++;
123: ret = (String) o;
124: } else { // string not found in cache
125: ret = new String(chars, offset, len);
126: storeString(ret);
127: }
128: return ret;
129: }
130:
131: /** Remove string that can be in the cache */
132: private void removeString(String s) {
133: Object o = strMap.remove(s);
134: if (o instanceof Entry) {
135: Entry e = (Entry) o;
136: Entry ep = e.prev;
137: Entry en = e.next;
138:
139: if (e == chain) {
140: chain = en;
141: if (e == endChain) {
142: endChain = null;
143: }
144: } else { // not begining of chain
145: if (en != null) {
146: en.prev = ep;
147: } else {
148: endChain = ep;
149: }
150: }
151:
152: freeEntry = e; // free - can be reused for addition
153: size--;
154: }
155: /* In other cases the removed object was either
156: * the string which should be fine here
157: * or it was null.
158: */
159: }
160:
161: /** Store string that's not yet in the cache */
162: private void storeString(String s) {
163: Entry e;
164: if (size >= maxSize) {
165: // take last one and move to begining and replace value
166: e = endChain;
167: toStart(e);
168: strMap.remove(e.str);
169: e.str = s;
170: } else { // count of entries less than max
171: if (freeEntry != null) {
172: e = freeEntry;
173: freeEntry = null;
174: e.str = s;
175: e.next = chain;
176: } else {
177: e = new Entry(s, chain);
178: }
179:
180: if (chain != null) {
181: chain.prev = e;
182: } else { // nothing inserted yet
183: endChain = e;
184: }
185: chain = e;
186: size++;
187: }
188: strMap.put(s, e);
189: }
190:
191: /** Put a string into cache that will survive there
192: * so that it will be never removed.
193: */
194: public void putSurviveString(String s) {
195: removeString(s);
196: strMap.put(s, s);
197: }
198:
199: static class Entry {
200:
201: Entry(String str, Entry next) { // prev always null
202: this .str = str;
203: this .next = next;
204: }
205:
206: String str;
207:
208: Entry next;
209:
210: Entry prev;
211:
212: }
213:
214: public String toString() {
215: String ret = "size=" + size + ", maxSize="
216: + maxSize // NOI18N
217: + ", statHits=" + statHits + ", statQueries="
218: + statQueries; // NOI18N
219: if (statQueries > 0) {
220: ret += ", hit ratio=" + (statHits * 100 / statQueries)
221: + "%"; // NOI18N
222: }
223: return ret;
224: }
225:
226: }
|