001: /*
002: * Sun Public License Notice
003: *
004: * The contents of this file are subject to the Sun Public License
005: * Version 1.0 (the "License"). You may not use this file except in
006: * compliance with the License. A copy of the License is available at
007: * http://www.sun.com/
008: *
009: * The Original Code is NetBeans. The Initial Developer of the Original
010: * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
011: * Microsystems, Inc. All Rights Reserved.
012: */
013:
014: package org.netbeans.editor.ext;
015:
016: import org.netbeans.editor.StringMap;
017:
018: /**
019: * Cache holding the most commonly used strings. The unused strings are
020: * discarded when they reach the end of chain.
021: *
022: * @author Miloslav Metelka
023: * @version 1.00
024: */
025:
026: public class StringCache {
027:
028: private static final int DEFAULT_MAX_SIZE = 300;
029:
030: private static final int DEFAULT_INITIAL_CAPACITY = 701;
031:
032: int maxSize;
033:
034: int size;
035:
036: StringMap strMap;
037:
038: /** First chain member */
039: private Entry chain;
040:
041: /** Last chain member */
042: private Entry endChain;
043:
044: /** Last entry that was made free */
045: private Entry freeEntry;
046:
047: public int statQueries; // count of queries
048: public int statHits; // count of cache hits
049:
050: public StringCache() {
051: this (DEFAULT_MAX_SIZE, DEFAULT_INITIAL_CAPACITY);
052: }
053:
054: public StringCache(int maxSize) {
055: this (maxSize, 2 * maxSize);
056: }
057:
058: public StringCache(int maxSize, int initialMapCapacity) {
059: this .maxSize = maxSize;
060: strMap = new StringMap(initialMapCapacity);
061: }
062:
063: private void toStart(Entry e) {
064: if (e != chain) {
065: // chain removal
066: Entry ep = e.prev; // ep surely not null
067: Entry en = e.next;
068: if (en != null) {
069: en.prev = ep;
070: } else { // last chain member
071: endChain = ep;
072: }
073: ep.next = en;
074:
075: // insert to chain start
076: if (chain != null) {
077: e.next = chain;
078: chain.prev = e;
079: }
080: chain = e;
081: }
082: }
083:
084: public String getString(char[] chars, int offset, int len) {
085: statQueries++;
086: Object o = strMap.get(chars, offset, len);
087: String ret;
088: if (o instanceof Entry) {
089: Entry e = (Entry) o;
090: toStart(e);
091: statHits++;
092: ret = e.str;
093: } else if (o instanceof String) {
094: statHits++;
095: ret = (String) o;
096: } else { // string not found in cache
097: ret = new String(chars, offset, len);
098: storeString(ret);
099: }
100: return ret;
101: }
102:
103: /** Remove string that can be in the cache */
104: private void removeString(String s) {
105: Object o = strMap.remove(s);
106: if (o instanceof Entry) {
107: Entry e = (Entry) o;
108: Entry ep = e.prev;
109: Entry en = e.next;
110:
111: if (e == chain) {
112: chain = en;
113: if (e == endChain) {
114: endChain = null;
115: }
116: } else { // not begining of chain
117: if (en != null) {
118: en.prev = ep;
119: } else {
120: endChain = ep;
121: }
122: }
123:
124: freeEntry = e; // free - can be reused for addition
125: size--;
126: }
127: /*
128: * In other cases the removed object was either the string which should
129: * be fine here or it was null.
130: */
131: }
132:
133: /** Store string that's not yet in the cache */
134: private void storeString(String s) {
135: Entry e;
136: if (size >= maxSize) {
137: // take last one and move to begining and replace value
138: e = endChain;
139: toStart(e);
140: strMap.remove(e.str);
141: e.str = s;
142: } else { // count of entries less than max
143: if (freeEntry != null) {
144: e = freeEntry;
145: freeEntry = null;
146: e.str = s;
147: e.next = chain;
148: } else {
149: e = new Entry(s, chain);
150: }
151:
152: if (chain != null) {
153: chain.prev = e;
154: } else { // nothing inserted yet
155: endChain = e;
156: }
157: chain = e;
158: size++;
159: }
160: strMap.put(s, e);
161: }
162:
163: /**
164: * Put a string into cache that will survive there so that it will be never
165: * removed.
166: */
167: public void putSurviveString(String s) {
168: removeString(s);
169: strMap.put(s, s);
170: }
171:
172: static class Entry {
173:
174: Entry(String str, Entry next) { // prev always null
175: this .str = str;
176: this .next = next;
177: }
178:
179: String str;
180:
181: Entry next;
182:
183: Entry prev;
184:
185: }
186:
187: public String toString() {
188: String ret = "size=" + size + ", maxSize="
189: + maxSize // NOI18N
190: + ", statHits=" + statHits + ", statQueries="
191: + statQueries; // NOI18N
192: if (statQueries > 0) {
193: ret += ", hit ratio=" + (statHits * 100 / statQueries)
194: + "%"; // NOI18N
195: }
196: return ret;
197: }
198:
199: }
|