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;
015:
016: import java.lang.ref.WeakReference;
017: import java.util.ArrayList;
018: import java.util.Iterator;
019:
020: import javax.swing.event.ChangeEvent;
021: import javax.swing.event.ChangeListener;
022: import javax.swing.text.JTextComponent;
023:
024: /**
025: * All the documents and components register here so that they become available
026: * to the processing that crosses different components and documents such as
027: * cross document position stack or word matching.
028: *
029: * @author Miloslav Metelka
030: * @version 1.00
031: */
032: public class Registry {
033:
034: private static final WeakReference[] EMPTY = new WeakReference[0];
035:
036: /** Array of weak references to documents */
037: private static WeakReference[] docRefs = EMPTY;
038:
039: /** Number of the document references */
040: private static int docRefsCount;
041:
042: /** Array of activated document numbers */
043: private static final ArrayList docAct = new ArrayList();
044:
045: /** Array list of weak references to components */
046: private static WeakReference[] compRefs = EMPTY;
047:
048: /** Number of the document references */
049: private static int compRefsCount;
050:
051: /** Array of activated component numbers */
052: private static final ArrayList compAct = new ArrayList();
053:
054: /** List of the registered changes listeners */
055: private static final WeakEventListenerList listenerList = new WeakEventListenerList();
056:
057: private static int consolidateCounter;
058:
059: /**
060: * Add weak listener to listen to change of activity of documents or
061: * components. The caller must hold the listener object in some instance
062: * variable to prevent it from being garbage collected.
063: *
064: * @param l
065: * listener to add
066: */
067: public static void addChangeListener(ChangeListener l) {
068: listenerList.add(SettingsChangeListener.class, l);
069: }
070:
071: /**
072: * Remove listener for changes in activity. It's optional to remove the
073: * listener. It would be done automatically if the object holding the
074: * listener would be garbage collected.
075: *
076: * @param l
077: * listener to remove
078: */
079: public static void removeChangeListener(SettingsChangeListener l) {
080: listenerList.remove(SettingsChangeListener.class, l);
081: }
082:
083: /**
084: * Get document ID from the document.
085: *
086: * @return document id or -1 if document was not yet added to the registry
087: * by <code>addDocument()</code>.
088: */
089: public static synchronized int getID(BaseDocument doc) {
090: Integer i = getIDInteger(doc);
091: return (i != null) ? i.intValue() : -1;
092: }
093:
094: /**
095: * Get component ID from the component.
096: *
097: * @return component id or -1 if component was not yet added to the registry
098: * by <code>addComponent()</code>.
099: */
100: public static synchronized int getID(JTextComponent c) {
101: return getIDImpl(c);
102: }
103:
104: /**
105: * Get document when its ID is known. It's rather cheap operation.
106: *
107: * @param docID
108: * document ID. It can be retrieved from the document by
109: * <code>getID(doc)</code>.
110: * @return document instance or null when document no longer exists
111: */
112: public static synchronized BaseDocument getDocument(int docID) {
113: if (docID < 0 || docID >= docRefsCount) {
114: return null;
115: }
116:
117: WeakReference wr = docRefs[docID];
118: return (wr != null) ? (BaseDocument) wr.get() : null;
119: }
120:
121: /**
122: * Get component when its ID is known. It's rather cheap operation.
123: *
124: * @param compID
125: * component ID. It can be retrieved from the component by
126: * <code>getID(c)</code>.
127: * @return component instance or null when document no longer exists
128: */
129: public static synchronized JTextComponent getComponent(int compID) {
130: if (compID < 0 || compID >= compRefsCount) {
131: return null;
132: }
133:
134: WeakReference wr = compRefs[compID];
135: return (wr != null) ? (JTextComponent) wr.get() : null;
136: }
137:
138: /**
139: * Add document to registry. Doesn't search for repetitive adding.
140: *
141: * @return registry unique ID of the document
142: */
143: public static synchronized int addDocument(BaseDocument doc) {
144: Integer docID = getIDInteger(doc);
145: if (docID != null) { // already added
146: return docID.intValue();
147: }
148:
149: if (docRefsCount >= docRefs.length) {
150: docRefs = realloc(docRefs);
151: }
152:
153: docRefs[docRefsCount] = new WeakReference(doc);
154: doc
155: .putProperty(BaseDocument.ID_PROP, new Integer(
156: docRefsCount));
157: return docRefsCount++;
158: }
159:
160: /**
161: * Add component to registry. If the component is already registered it
162: * returns the existing} ID. The document that is currently assigned to the
163: * component is _not_ registered automatically.
164: *
165: * @return ID of the component
166: */
167: public static synchronized int addComponent(JTextComponent c) {
168: int compID = getIDImpl(c);
169: if (compID != -1) {
170: return compID; // already registered
171: }
172:
173: if (compRefsCount >= compRefs.length) {
174: compRefs = realloc(compRefs);
175: }
176:
177: compRefs[compRefsCount] = new WeakReference(c);
178: ((BaseTextUI) c.getUI()).componentID = compRefsCount;
179: return compRefsCount++;
180: }
181:
182: /**
183: * Remove component from registry. It's usually done when the UI of the
184: * component is being deinstalled.
185: *
186: * @return ID that the component had in the registry. The possible new ID
187: * will be different from this one. -1 will be returned if the
188: * component was not yet added to the registry.
189: */
190: public static synchronized int removeComponent(JTextComponent c) {
191: int compID = getIDImpl(c);
192:
193: if (compID != -1) {
194: compRefs[compID] = null;
195: // Search whether was activated
196: for (int i = compAct.size() - 1; i >= 0; i--) {
197: if (((Integer) compAct.get(i)).intValue() == compID) {
198: compAct.remove(i);
199: break;
200: }
201: }
202: }
203:
204: return compID;
205: }
206:
207: /**
208: * Put the component to the first position in the array of last accessed
209: * components. The activate of document is also called automatically.
210: */
211: public static synchronized void activate(JTextComponent c) {
212: boolean activated = false;
213: int compID = getIDImpl(c);
214: if (compID == -1) { // c not registered
215: return;
216: }
217:
218: int actSize = compAct.size();
219: int ind = 0;
220: while (ind < actSize) {
221: int id = ((Integer) compAct.get(ind)).intValue();
222: if (id == compID) { // found
223: if (ind == 0) {
224: break;
225: }
226: compAct.add(0, compAct.remove(ind));
227: activated = true;
228: break;
229: }
230:
231: ind++;
232: }
233:
234: if (ind == actSize) {
235: compAct.add(0, new Integer(compID));
236: activated = true;
237: }
238:
239: // Try to activate component's document too
240: Object doc = c.getDocument();
241: if (doc instanceof BaseDocument) {
242: if (doActivate((BaseDocument) doc)) {
243: activated = true;
244: }
245: }
246:
247: if (activated) {
248: fireChange();
249: }
250: }
251:
252: /**
253: * Put the document to the first position in the array of last accessed
254: * documents. The document must be registered otherwise nothing is done.
255: *
256: * @param doc
257: * document to be activated
258: */
259: public static synchronized void activate(BaseDocument doc) {
260: if (doActivate(doc)) {
261: fireChange();
262: }
263: }
264:
265: public static synchronized BaseDocument getMostActiveDocument() {
266: return getValidDoc(0, true);
267: }
268:
269: public static synchronized BaseDocument getLeastActiveDocument() {
270: int lastInd = docAct.size() - 1;
271: return getValidDoc(lastInd, false);
272: }
273:
274: public static BaseDocument getLessActiveDocument(BaseDocument doc) {
275: return getLessActiveDocument(getID(doc));
276: }
277:
278: public static synchronized BaseDocument getLessActiveDocument(
279: int docID) {
280: return getNextActiveDoc(docID, true);
281: }
282:
283: public static BaseDocument getMoreActiveDocument(BaseDocument doc) {
284: return getMoreActiveDocument(getID(doc));
285: }
286:
287: public static synchronized BaseDocument getMoreActiveDocument(
288: int docID) {
289: return getNextActiveDoc(docID, false);
290: }
291:
292: /**
293: * Get the iterator over the active documents. It starts with the most
294: * active document till the least active document. It's just the current
295: * snapshot so the iterator will not reflect future changes.
296: */
297: public static synchronized Iterator getDocumentIterator() {
298: consolidate();
299:
300: ArrayList docList = new ArrayList();
301: int actSize = docAct.size();
302: for (int i = 0; i < actSize; i++) {
303: int ind = ((Integer) docAct.get(i)).intValue();
304: WeakReference wr = docRefs[ind];
305: if (wr != null) {
306: Object doc = wr.get();
307: if (doc != null) {
308: docList.add(doc);
309: }
310: }
311: }
312:
313: return docList.iterator();
314: }
315:
316: public static synchronized JTextComponent getMostActiveComponent() {
317: return getValidComp(0, true);
318: }
319:
320: public static synchronized JTextComponent getLeastActiveComponent() {
321: int lastInd = compAct.size() - 1;
322: return getValidComp(lastInd, false);
323: }
324:
325: public static JTextComponent getLessActiveComponent(JTextComponent c) {
326: return getLessActiveComponent(getID(c));
327: }
328:
329: public static synchronized JTextComponent getLessActiveComponent(
330: int compID) {
331: return getNextActiveComp(compID, true);
332: }
333:
334: public static JTextComponent getMoreActiveComponent(JTextComponent c) {
335: return getMoreActiveComponent(getID(c));
336: }
337:
338: public static synchronized JTextComponent getMoreActiveComponent(
339: int compID) {
340: return getNextActiveComp(compID, false);
341: }
342:
343: /**
344: * Get the iterator over the active components. It starts with the most
345: * active component till the least active component.
346: */
347: public static synchronized Iterator getComponentIterator() {
348: consolidate();
349:
350: ArrayList compList = new ArrayList();
351: int actSize = compAct.size();
352: for (int i = 0; i < actSize; i++) {
353: int ind = ((Integer) compAct.get(i)).intValue();
354: WeakReference wr = compRefs[ind];
355: if (wr != null) {
356: Object comp = wr.get();
357: if (comp != null) {
358: compList.add(comp);
359: }
360: }
361: }
362:
363: return compList.iterator();
364: }
365:
366: private static WeakReference[] realloc(WeakReference[] refs) {
367: WeakReference[] tmp = new WeakReference[refs.length * 2 + 4];
368: System.arraycopy(refs, 0, tmp, 0, refs.length);
369: return tmp;
370: }
371:
372: private static void consolidate() {
373: while (++consolidateCounter >= 20) { // after every 20th call
374: consolidateCounter = 0;
375:
376: // Remove empty document references
377: for (int i = docAct.size() - 1; i >= 0; i--) {
378: int ind = ((Integer) docAct.get(i)).intValue();
379: WeakReference wr = docRefs[ind];
380: if (wr != null) {
381: if (wr.get() == null) { // empty reference
382: docAct.remove(i);
383: docRefs[ind] = null;
384: }
385: }
386: }
387:
388: // Remove empty component references
389: for (int i = compAct.size() - 1; i >= 0; i--) {
390: int ind = ((Integer) compAct.get(i)).intValue();
391: WeakReference wr = compRefs[ind];
392: if (wr != null) {
393: if (wr.get() == null) { // empty reference
394: compAct.remove(i);
395: compRefs[ind] = null;
396: }
397: }
398: }
399: }
400: }
401:
402: private static int getIDImpl(JTextComponent c) {
403: if (c == null) {
404: return -1;
405: }
406: return ((BaseTextUI) c.getUI()).componentID;
407: }
408:
409: private static Integer getIDInteger(BaseDocument doc) {
410: if (doc == null) {
411: return null;
412: }
413:
414: return (Integer) doc.getProperty(BaseDocument.ID_PROP);
415: }
416:
417: private static boolean doActivate(BaseDocument doc) {
418: Integer docIDInteger = getIDInteger(doc);
419:
420: if (docIDInteger == null) {
421: return false; // document not added to registry
422: }
423:
424: int docID = (docIDInteger != null) ? docIDInteger.intValue()
425: : -1;
426:
427: int size = docAct.size();
428: for (int ind = 0; ind < size; ind++) {
429: int id = ((Integer) docAct.get(ind)).intValue();
430: if (id == docID) {
431: if (ind == 0) { // no change
432: return false;
433: }
434:
435: docAct.add(0, docAct.remove(ind));
436: return true;
437: }
438: }
439:
440: docAct.add(0, docIDInteger);
441: return true;
442: }
443:
444: private static BaseDocument getValidDoc(int ind, boolean forward) {
445: consolidate();
446:
447: int actSize = docAct.size();
448: while (ind >= 0 && ind < actSize) {
449: int docID = ((Integer) docAct.get(ind)).intValue();
450: WeakReference wr = docRefs[docID];
451: BaseDocument doc = (wr != null) ? (BaseDocument) wr.get()
452: : null;
453: if (doc != null) {
454: return doc;
455: }
456: ind += forward ? +1 : -1;
457: }
458: return null;
459: }
460:
461: private static BaseDocument getNextActiveDoc(int docID,
462: boolean forward) {
463: consolidate();
464:
465: int actSize = docAct.size();
466: int ind = forward ? 0 : (actSize - 1);
467: while (ind >= 0 && ind < actSize) {
468: if (((Integer) docAct.get(ind)).intValue() == docID) {
469: ind += forward ? +1 : -1; // get next one
470: return getValidDoc(ind, forward);
471: }
472: ind += forward ? +1 : -1;
473: }
474: return null;
475: }
476:
477: private static JTextComponent getValidComp(int ind, boolean forward) {
478: consolidate();
479:
480: int actSize = compAct.size();
481: while (ind >= 0 && ind < actSize) {
482: int compID = ((Integer) compAct.get(ind)).intValue();
483: WeakReference wr = compRefs[compID];
484: JTextComponent c = (wr != null) ? (JTextComponent) wr.get()
485: : null;
486: if (c != null) {
487: return c;
488: }
489: ind += forward ? +1 : -1;
490: }
491: return null;
492: }
493:
494: private static JTextComponent getNextActiveComp(int compID,
495: boolean forward) {
496: int actSize = compAct.size();
497: int ind = forward ? 0 : (actSize - 1);
498: while (ind >= 0 && ind < actSize) {
499: if (((Integer) compAct.get(ind)).intValue() == compID) {
500: ind += forward ? +1 : -1;
501: return getValidComp(ind, forward);
502: }
503: ind += forward ? +1 : -1;
504: }
505: return null;
506: }
507:
508: private static void fireChange() {
509: ChangeListener[] listeners = (ChangeListener[]) listenerList
510: .getListeners(ChangeListener.class);
511: ChangeEvent evt = new ChangeEvent(Registry.class);
512: for (int i = 0; i < listeners.length; i++) {
513: listeners[i].stateChanged(evt);
514: }
515: }
516:
517: /** Debug the registry into string. */
518: public static synchronized String registryToString() {
519: StringBuffer sb = new StringBuffer();
520: sb.append("Document References:\n"); // NOI18N
521: for (int i = 0; i < docRefsCount; i++) {
522: WeakReference wr = docRefs[i];
523: sb.append("docRefs[" + i + "]="
524: + ((wr != null) ? wr.get() : "null") + "\n"); // NOI18N
525: }
526: sb.append("Component References:\n"); // NOI18N
527: for (int i = 0; i < compRefsCount; i++) {
528: WeakReference wr = (WeakReference) compRefs[i];
529: sb.append("compRefs[" + i + "]="
530: + ((wr != null) ? wr.get() : "null") + "\n"); // NOI18N
531: }
532: sb.append("\nActive Document Indexes:\n"); // NOI18N
533: for (int i = 0; i < docAct.size(); i++) {
534: sb.append(docAct.get(i));
535: if (i != docAct.size() - 1) {
536: sb.append(", "); // NOI18N
537: }
538: }
539: sb.append("\nActive Component Indexes:\n"); // NOI18N
540: for (int i = 0; i < compAct.size(); i++) {
541: sb.append(compAct.get(i));
542: if (i != compAct.size() - 1) {
543: sb.append(", "); // NOI18N
544: }
545: }
546:
547: return sb.toString();
548: }
549:
550: }
|