KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > xerces > jaxp > validation > SoftReferenceGrammarPool


1 /*
2  * Copyright 2005 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.apache.xerces.jaxp.validation;
18
19 import java.lang.ref.Reference JavaDoc;
20 import java.lang.ref.ReferenceQueue JavaDoc;
21 import java.lang.ref.SoftReference JavaDoc;
22
23 import org.apache.xerces.xni.grammars.Grammar;
24 import org.apache.xerces.xni.grammars.XMLGrammarDescription;
25 import org.apache.xerces.xni.grammars.XMLSchemaDescription;
26 import org.apache.xerces.xni.grammars.XMLGrammarPool;
27
28 /**
29  * <p>This grammar pool is a memory sensitive cache. The grammars
30  * stored in the pool are softly reachable and may be cleared by
31  * the garbage collector in response to memory demand. Equality
32  * of <code>XMLSchemaDescription</code>s is determined using both
33  * the target namespace for the schema and schema location.</p>
34  *
35  * @author Michael Glavassevich, IBM
36  * @version $Id: SoftReferenceGrammarPool.java,v 1.2 2005/05/28 02:06:59 mrglavas Exp $
37  */

38 final class SoftReferenceGrammarPool implements XMLGrammarPool {
39     
40     //
41
// Constants
42
//
43

44     /** Default size. */
45     protected static final int TABLE_SIZE = 11;
46     
47     /** Zero length grammar array. */
48     protected static final Grammar [] ZERO_LENGTH_GRAMMAR_ARRAY = new Grammar [0];
49     
50     //
51
// Data
52
//
53

54     /** Grammars. */
55     protected Entry [] fGrammars = null;
56     
57     /** Flag indicating whether this pool is locked */
58     protected boolean fPoolIsLocked;
59     
60     /** The number of grammars in the pool */
61     protected int fGrammarCount = 0;
62     
63     /** Reference queue for cleared grammar references */
64     protected final ReferenceQueue JavaDoc fReferenceQueue = new ReferenceQueue JavaDoc();
65     
66     //
67
// Constructors
68
//
69

70     /** Constructs a grammar pool with a default number of buckets. */
71     public SoftReferenceGrammarPool() {
72         fGrammars = new Entry[TABLE_SIZE];
73         fPoolIsLocked = false;
74     } // <init>()
75

76     /** Constructs a grammar pool with a specified number of buckets. */
77     public SoftReferenceGrammarPool(int initialCapacity) {
78         fGrammars = new Entry[initialCapacity];
79         fPoolIsLocked = false;
80     }
81     
82     //
83
// XMLGrammarPool methods
84
//
85

86     /* <p> Retrieve the initial known set of grammars. This method is
87      * called by a validator before the validation starts. The application
88      * can provide an initial set of grammars available to the current
89      * validation attempt. </p>
90      *
91      * @param grammarType The type of the grammar, from the
92      * <code>org.apache.xerces.xni.grammars.XMLGrammarDescription</code>
93      * interface.
94      * @return The set of grammars the validator may put in its "bucket"
95      */

96     public Grammar [] retrieveInitialGrammarSet (String JavaDoc grammarType) {
97         synchronized (fGrammars) {
98             clean();
99             // Return no grammars. This allows the garbage collector to sift
100
// out grammars which are not in use when memory demand is high.
101
// It also allows the pool to return the "right" schema grammar
102
// based on schema locations.
103
return ZERO_LENGTH_GRAMMAR_ARRAY;
104         }
105     } // retrieveInitialGrammarSet (String): Grammar[]
106

107     /* <p> Return the final set of grammars that the validator ended up
108      * with. This method is called after the validation finishes. The
109      * application may then choose to cache some of the returned grammars.</p>
110      * <p>In this implementation, we make our choice based on whether this object
111      * is "locked"--that is, whether the application has instructed
112      * us not to accept any new grammars.</p>
113      *
114      * @param grammarType The type of the grammars being returned;
115      * @param grammars An array containing the set of grammars being
116      * returned; order is not significant.
117      */

118     public void cacheGrammars(String JavaDoc grammarType, Grammar[] grammars) {
119         if (!fPoolIsLocked) {
120             for (int i = 0; i < grammars.length; ++i) {
121                 putGrammar(grammars[i]);
122             }
123         }
124     } // cacheGrammars(String, Grammar[]);
125

126     /* <p> This method requests that the application retrieve a grammar
127      * corresponding to the given GrammarIdentifier from its cache.
128      * If it cannot do so it must return null; the parser will then
129      * call the EntityResolver. </p>
130      * <strong>An application must not call its EntityResolver itself
131      * from this method; this may result in infinite recursions.</strong>
132      *
133      * This implementation chooses to use the root element name to identify a DTD grammar
134      * and the target namespace to identify a Schema grammar.
135      *
136      * @param desc The description of the Grammar being requested.
137      * @return The Grammar corresponding to this description or null if
138      * no such Grammar is known.
139      */

140     public Grammar retrieveGrammar(XMLGrammarDescription desc) {
141         return getGrammar(desc);
142     } // retrieveGrammar(XMLGrammarDescription): Grammar
143

144     //
145
// Public methods
146
//
147

148     /**
149      * Puts the specified grammar into the grammar pool and associates it to
150      * its root element name or its target namespace.
151      *
152      * @param grammar The Grammar.
153      */

154     public void putGrammar(Grammar grammar) {
155         if (!fPoolIsLocked) {
156             synchronized (fGrammars) {
157                 clean();
158                 XMLGrammarDescription desc = grammar.getGrammarDescription();
159                 int hash = hashCode(desc);
160                 int index = (hash & 0x7FFFFFFF) % fGrammars.length;
161                 for (Entry entry = fGrammars[index]; entry != null; entry = entry.next) {
162                     if (entry.hash == hash && equals(entry.desc, desc)) {
163                         if (entry.grammar.get() != grammar) {
164                             entry.grammar = new SoftGrammarReference(entry, grammar, fReferenceQueue);
165                         }
166                         return;
167                     }
168                 }
169                 // create a new entry
170
Entry entry = new Entry(hash, index, desc, grammar, fGrammars[index], fReferenceQueue);
171                 fGrammars[index] = entry;
172                 fGrammarCount++;
173             }
174         }
175     } // putGrammar(Grammar)
176

177     /**
178      * Returns the grammar associated to the specified grammar description.
179      * Currently, the root element name is used as the key for DTD grammars
180      * and the target namespace is used as the key for Schema grammars.
181      *
182      * @param desc The Grammar Description.
183      */

184     public Grammar getGrammar(XMLGrammarDescription desc) {
185         synchronized (fGrammars) {
186             clean();
187             int hash = hashCode(desc);
188             int index = (hash & 0x7FFFFFFF) % fGrammars.length;
189             for (Entry entry = fGrammars[index]; entry != null; entry = entry.next) {
190                 Grammar tempGrammar = (Grammar) entry.grammar.get();
191                 /** If the soft reference has been cleared, remove this entry from the pool. */
192                 if (tempGrammar == null) {
193                     removeEntry(entry);
194                 }
195                 else if ((entry.hash == hash) && equals(entry.desc, desc)) {
196                     return tempGrammar;
197                 }
198             }
199             return null;
200         }
201     } // getGrammar(XMLGrammarDescription):Grammar
202

203     /**
204      * Removes the grammar associated to the specified grammar description from the
205      * grammar pool and returns the removed grammar. Currently, the root element name
206      * is used as the key for DTD grammars and the target namespace is used
207      * as the key for Schema grammars.
208      *
209      * @param desc The Grammar Description.
210      * @return The removed grammar.
211      */

212     public Grammar removeGrammar(XMLGrammarDescription desc) {
213         synchronized (fGrammars) {
214             clean();
215             int hash = hashCode(desc);
216             int index = (hash & 0x7FFFFFFF) % fGrammars.length;
217             for (Entry entry = fGrammars[index]; entry != null; entry = entry.next) {
218                 if ((entry.hash == hash) && equals(entry.desc, desc)) {
219                     return removeEntry(entry);
220                 }
221             }
222             return null;
223         }
224     } // removeGrammar(XMLGrammarDescription):Grammar
225

226     /**
227      * Returns true if the grammar pool contains a grammar associated
228      * to the specified grammar description. Currently, the root element name
229      * is used as the key for DTD grammars and the target namespace is used
230      * as the key for Schema grammars.
231      *
232      * @param desc The Grammar Description.
233      */

234     public boolean containsGrammar(XMLGrammarDescription desc) {
235         synchronized (fGrammars) {
236             clean();
237             int hash = hashCode(desc);
238             int index = (hash & 0x7FFFFFFF) % fGrammars.length;
239             for (Entry entry = fGrammars[index]; entry != null ; entry = entry.next) {
240                 Grammar tempGrammar = (Grammar) entry.grammar.get();
241                 /** If the soft reference has been cleared, remove this entry from the pool. */
242                 if (tempGrammar == null) {
243                     removeEntry(entry);
244                 }
245                 else if ((entry.hash == hash) && equals(entry.desc, desc)) {
246                     return true;
247                 }
248             }
249             return false;
250         }
251     } // containsGrammar(XMLGrammarDescription):boolean
252

253     /* <p> Sets this grammar pool to a "locked" state--i.e.,
254      * no new grammars will be added until it is "unlocked".
255      */

256     public void lockPool() {
257         fPoolIsLocked = true;
258     } // lockPool()
259

260     /* <p> Sets this grammar pool to an "unlocked" state--i.e.,
261      * new grammars will be added when putGrammar or cacheGrammars
262      * are called.
263      */

264     public void unlockPool() {
265         fPoolIsLocked = false;
266     } // unlockPool()
267

268     /*
269      * <p>This method clears the pool-i.e., removes references
270      * to all the grammars in it.</p>
271      */

272     public void clear() {
273         for (int i=0; i<fGrammars.length; i++) {
274             if(fGrammars[i] != null) {
275                 fGrammars[i].clear();
276                 fGrammars[i] = null;
277             }
278         }
279         fGrammarCount = 0;
280     } // clear()
281

282     /**
283      * This method checks whether two grammars are the same. Currently, we compare
284      * the root element names for DTD grammars and the target namespaces for Schema grammars.
285      * The application can override this behaviour and add its own logic.
286      *
287      * @param desc1 The grammar description
288      * @param desc2 The grammar description of the grammar to be compared to
289      * @return True if the grammars are equal, otherwise false
290      */

291     public boolean equals(XMLGrammarDescription desc1, XMLGrammarDescription desc2) {
292         if (desc1 instanceof XMLSchemaDescription) {
293             if (!(desc2 instanceof XMLSchemaDescription)) {
294                 return false;
295             }
296             final XMLSchemaDescription sd1 = (XMLSchemaDescription) desc1;
297             final XMLSchemaDescription sd2 = (XMLSchemaDescription) desc2;
298             final String JavaDoc targetNamespace = sd1.getTargetNamespace();
299             if (targetNamespace != null) {
300                 if (!targetNamespace.equals(sd2.getTargetNamespace())) {
301                     return false;
302                 }
303             }
304             else if (sd2.getTargetNamespace() != null) {
305                 return false;
306             }
307             // The JAXP 1.3 spec says that the implementation can assume that
308
// if two schema location hints are the same they always resolve
309
// to the same document. In the default grammar pool implementation
310
// we only look at the target namespaces. Here we also compare
311
// location hints.
312
final String JavaDoc expandedSystemId = sd1.getExpandedSystemId();
313             if (expandedSystemId != null) {
314                 if (!expandedSystemId.equals(sd2.getExpandedSystemId())) {
315                     return false;
316                 }
317             }
318             else if (sd2.getExpandedSystemId() != null) {
319                 return false;
320             }
321             return true;
322         }
323         return desc1.equals(desc2);
324     }
325     
326     /**
327      * Returns the hash code value for the given grammar description.
328      *
329      * @param desc The grammar description
330      * @return The hash code value
331      */

332     public int hashCode(XMLGrammarDescription desc) {
333         if (desc instanceof XMLSchemaDescription) {
334             final XMLSchemaDescription sd = (XMLSchemaDescription) desc;
335             final String JavaDoc targetNamespace = sd.getTargetNamespace();
336             final String JavaDoc expandedSystemId = sd.getExpandedSystemId();
337             int hash = (targetNamespace != null) ? targetNamespace.hashCode() : 0;
338             hash ^= (expandedSystemId != null) ? expandedSystemId.hashCode() : 0;
339             return hash;
340         }
341         return desc.hashCode();
342     }
343     
344     /**
345      * Removes the given entry from the pool
346      *
347      * @param entry the entry to remove
348      * @return The grammar attached to this entry
349      */

350     private Grammar removeEntry(Entry entry) {
351         if (entry.prev != null) {
352             entry.prev.next = entry.next;
353         }
354         else {
355             fGrammars[entry.bucket] = entry.next;
356         }
357         if (entry.next != null) {
358             entry.next.prev = entry.prev;
359         }
360         --fGrammarCount;
361         entry.grammar.entry = null;
362         return (Grammar) entry.grammar.get();
363     }
364     
365     /**
366      * Removes stale entries from the pool.
367      */

368     private void clean() {
369         Reference JavaDoc ref = fReferenceQueue.poll();
370         while (ref != null) {
371             Entry entry = ((SoftGrammarReference) ref).entry;
372             if (entry != null) {
373                 removeEntry(entry);
374             }
375             ref = fReferenceQueue.poll();
376         }
377     }
378     
379     /**
380      * This class is a grammar pool entry. Each entry acts as a node
381      * in a doubly linked list.
382      */

383     static final class Entry {
384
385         public int hash;
386         public int bucket;
387         public Entry prev;
388         public Entry next;
389         public XMLGrammarDescription desc;
390         public SoftGrammarReference grammar;
391                 
392         protected Entry(int hash, int bucket, XMLGrammarDescription desc, Grammar grammar, Entry next, ReferenceQueue JavaDoc queue) {
393             this.hash = hash;
394             this.bucket = bucket;
395             this.prev = null;
396             this.next = next;
397             if (next != null) {
398                 next.prev = this;
399             }
400             this.desc = desc;
401             this.grammar = new SoftGrammarReference(this, grammar, queue);
402         }
403         
404         // clear this entry; useful to promote garbage collection
405
// since reduces reference count of objects to be destroyed
406
protected void clear () {
407             desc = null;
408             grammar = null;
409             if(next != null) {
410                 next.clear();
411                 next = null;
412             }
413         } // clear()
414

415     } // class Entry
416

417     /**
418      * This class stores a soft reference to a grammar object. It keeps a reference
419      * to its associated entry, so that it can be easily removed from the pool.
420      */

421     static final class SoftGrammarReference extends SoftReference JavaDoc {
422
423         public Entry entry;
424         
425         protected SoftGrammarReference(Entry entry, Grammar grammar, ReferenceQueue JavaDoc queue) {
426             super(grammar, queue);
427             this.entry = entry;
428         }
429         
430     } // class SoftGrammarReference
431

432 } // class SoftReferenceGrammarPool
433
Popular Tags