author | joehw |
Wed, 18 Oct 2017 13:25:49 -0700 | |
changeset 47359 | e1a6c0168741 |
parent 47216 | 71c04702a3d5 |
child 47712 | bde0215f1f70 |
permissions | -rw-r--r-- |
6 | 1 |
/* |
47359
e1a6c0168741
8181150: Fix lint warnings in JAXP repo: rawtypes and unchecked
joehw
parents:
47216
diff
changeset
|
2 |
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. |
e1a6c0168741
8181150: Fix lint warnings in JAXP repo: rawtypes and unchecked
joehw
parents:
47216
diff
changeset
|
3 |
* @LastModified: Oct 2017 |
6 | 4 |
*/ |
5 |
/* |
|
44797
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
6 |
* Licensed to the Apache Software Foundation (ASF) under one or more |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
7 |
* contributor license agreements. See the NOTICE file distributed with |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
8 |
* this work for additional information regarding copyright ownership. |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
9 |
* The ASF licenses this file to You under the Apache License, Version 2.0 |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
10 |
* (the "License"); you may not use this file except in compliance with |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
11 |
* the License. You may obtain a copy of the License at |
6 | 12 |
* |
13 |
* http://www.apache.org/licenses/LICENSE-2.0 |
|
14 |
* |
|
15 |
* Unless required by applicable law or agreed to in writing, software |
|
16 |
* distributed under the License is distributed on an "AS IS" BASIS, |
|
17 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
18 |
* See the License for the specific language governing permissions and |
|
19 |
* limitations under the License. |
|
20 |
*/ |
|
21 |
||
22 |
package com.sun.org.apache.xerces.internal.jaxp.validation; |
|
23 |
||
24 |
import java.lang.ref.Reference; |
|
25 |
import java.lang.ref.ReferenceQueue; |
|
26 |
import java.lang.ref.SoftReference; |
|
27 |
||
28 |
import com.sun.org.apache.xerces.internal.xni.grammars.Grammar; |
|
29 |
import com.sun.org.apache.xerces.internal.xni.grammars.XMLGrammarDescription; |
|
30 |
import com.sun.org.apache.xerces.internal.xni.grammars.XMLSchemaDescription; |
|
31 |
import com.sun.org.apache.xerces.internal.xni.grammars.XMLGrammarPool; |
|
32 |
||
33 |
/** |
|
34 |
* <p>This grammar pool is a memory sensitive cache. The grammars |
|
35 |
* stored in the pool are softly reachable and may be cleared by |
|
36 |
* the garbage collector in response to memory demand. Equality |
|
37 |
* of <code>XMLSchemaDescription</code>s is determined using both |
|
38 |
* the target namespace for the schema and schema location.</p> |
|
39 |
* |
|
40 |
* @author Michael Glavassevich, IBM |
|
41 |
*/ |
|
42 |
final class SoftReferenceGrammarPool implements XMLGrammarPool { |
|
43 |
||
44 |
// |
|
45 |
// Constants |
|
46 |
// |
|
47 |
||
48 |
/** Default size. */ |
|
49 |
protected static final int TABLE_SIZE = 11; |
|
50 |
||
51 |
/** Zero length grammar array. */ |
|
52 |
protected static final Grammar [] ZERO_LENGTH_GRAMMAR_ARRAY = new Grammar [0]; |
|
53 |
||
54 |
// |
|
55 |
// Data |
|
56 |
// |
|
57 |
||
58 |
/** Grammars. */ |
|
59 |
protected Entry [] fGrammars = null; |
|
60 |
||
61 |
/** Flag indicating whether this pool is locked */ |
|
62 |
protected boolean fPoolIsLocked; |
|
63 |
||
64 |
/** The number of grammars in the pool */ |
|
65 |
protected int fGrammarCount = 0; |
|
66 |
||
67 |
/** Reference queue for cleared grammar references */ |
|
47359
e1a6c0168741
8181150: Fix lint warnings in JAXP repo: rawtypes and unchecked
joehw
parents:
47216
diff
changeset
|
68 |
protected final ReferenceQueue<Grammar> fReferenceQueue = new ReferenceQueue<>(); |
6 | 69 |
|
70 |
// |
|
71 |
// Constructors |
|
72 |
// |
|
73 |
||
74 |
/** Constructs a grammar pool with a default number of buckets. */ |
|
75 |
public SoftReferenceGrammarPool() { |
|
76 |
fGrammars = new Entry[TABLE_SIZE]; |
|
77 |
fPoolIsLocked = false; |
|
78 |
} // <init>() |
|
79 |
||
80 |
/** Constructs a grammar pool with a specified number of buckets. */ |
|
81 |
public SoftReferenceGrammarPool(int initialCapacity) { |
|
82 |
fGrammars = new Entry[initialCapacity]; |
|
83 |
fPoolIsLocked = false; |
|
84 |
} |
|
85 |
||
86 |
// |
|
87 |
// XMLGrammarPool methods |
|
88 |
// |
|
89 |
||
90 |
/* <p> Retrieve the initial known set of grammars. This method is |
|
91 |
* called by a validator before the validation starts. The application |
|
92 |
* can provide an initial set of grammars available to the current |
|
93 |
* validation attempt. </p> |
|
94 |
* |
|
95 |
* @param grammarType The type of the grammar, from the |
|
96 |
* <code>com.sun.org.apache.xerces.internal.xni.grammars.XMLGrammarDescription</code> |
|
97 |
* interface. |
|
98 |
* @return The set of grammars the validator may put in its "bucket" |
|
99 |
*/ |
|
100 |
public Grammar [] retrieveInitialGrammarSet (String grammarType) { |
|
101 |
synchronized (fGrammars) { |
|
102 |
clean(); |
|
103 |
// Return no grammars. This allows the garbage collector to sift |
|
104 |
// out grammars which are not in use when memory demand is high. |
|
105 |
// It also allows the pool to return the "right" schema grammar |
|
106 |
// based on schema locations. |
|
107 |
return ZERO_LENGTH_GRAMMAR_ARRAY; |
|
108 |
} |
|
109 |
} // retrieveInitialGrammarSet (String): Grammar[] |
|
110 |
||
111 |
/* <p> Return the final set of grammars that the validator ended up |
|
112 |
* with. This method is called after the validation finishes. The |
|
113 |
* application may then choose to cache some of the returned grammars.</p> |
|
114 |
* <p>In this implementation, we make our choice based on whether this object |
|
115 |
* is "locked"--that is, whether the application has instructed |
|
116 |
* us not to accept any new grammars.</p> |
|
117 |
* |
|
118 |
* @param grammarType The type of the grammars being returned; |
|
119 |
* @param grammars An array containing the set of grammars being |
|
120 |
* returned; order is not significant. |
|
121 |
*/ |
|
122 |
public void cacheGrammars(String grammarType, Grammar[] grammars) { |
|
123 |
if (!fPoolIsLocked) { |
|
124 |
for (int i = 0; i < grammars.length; ++i) { |
|
125 |
putGrammar(grammars[i]); |
|
126 |
} |
|
127 |
} |
|
128 |
} // cacheGrammars(String, Grammar[]); |
|
129 |
||
130 |
/* <p> This method requests that the application retrieve a grammar |
|
131 |
* corresponding to the given GrammarIdentifier from its cache. |
|
132 |
* If it cannot do so it must return null; the parser will then |
|
133 |
* call the EntityResolver. </p> |
|
134 |
* <strong>An application must not call its EntityResolver itself |
|
135 |
* from this method; this may result in infinite recursions.</strong> |
|
136 |
* |
|
137 |
* This implementation chooses to use the root element name to identify a DTD grammar |
|
138 |
* and the target namespace to identify a Schema grammar. |
|
139 |
* |
|
140 |
* @param desc The description of the Grammar being requested. |
|
141 |
* @return The Grammar corresponding to this description or null if |
|
142 |
* no such Grammar is known. |
|
143 |
*/ |
|
144 |
public Grammar retrieveGrammar(XMLGrammarDescription desc) { |
|
145 |
return getGrammar(desc); |
|
146 |
} // retrieveGrammar(XMLGrammarDescription): Grammar |
|
147 |
||
148 |
// |
|
149 |
// Public methods |
|
150 |
// |
|
151 |
||
152 |
/** |
|
153 |
* Puts the specified grammar into the grammar pool and associates it to |
|
154 |
* its root element name or its target namespace. |
|
155 |
* |
|
156 |
* @param grammar The Grammar. |
|
157 |
*/ |
|
158 |
public void putGrammar(Grammar grammar) { |
|
159 |
if (!fPoolIsLocked) { |
|
160 |
synchronized (fGrammars) { |
|
161 |
clean(); |
|
162 |
XMLGrammarDescription desc = grammar.getGrammarDescription(); |
|
163 |
int hash = hashCode(desc); |
|
164 |
int index = (hash & 0x7FFFFFFF) % fGrammars.length; |
|
165 |
for (Entry entry = fGrammars[index]; entry != null; entry = entry.next) { |
|
166 |
if (entry.hash == hash && equals(entry.desc, desc)) { |
|
167 |
if (entry.grammar.get() != grammar) { |
|
168 |
entry.grammar = new SoftGrammarReference(entry, grammar, fReferenceQueue); |
|
169 |
} |
|
170 |
return; |
|
171 |
} |
|
172 |
} |
|
173 |
// create a new entry |
|
174 |
Entry entry = new Entry(hash, index, desc, grammar, fGrammars[index], fReferenceQueue); |
|
175 |
fGrammars[index] = entry; |
|
176 |
fGrammarCount++; |
|
177 |
} |
|
178 |
} |
|
179 |
} // putGrammar(Grammar) |
|
180 |
||
181 |
/** |
|
182 |
* Returns the grammar associated to the specified grammar description. |
|
183 |
* Currently, the root element name is used as the key for DTD grammars |
|
184 |
* and the target namespace is used as the key for Schema grammars. |
|
185 |
* |
|
186 |
* @param desc The Grammar Description. |
|
187 |
*/ |
|
188 |
public Grammar getGrammar(XMLGrammarDescription desc) { |
|
189 |
synchronized (fGrammars) { |
|
190 |
clean(); |
|
191 |
int hash = hashCode(desc); |
|
192 |
int index = (hash & 0x7FFFFFFF) % fGrammars.length; |
|
193 |
for (Entry entry = fGrammars[index]; entry != null; entry = entry.next) { |
|
194 |
Grammar tempGrammar = (Grammar) entry.grammar.get(); |
|
195 |
/** If the soft reference has been cleared, remove this entry from the pool. */ |
|
196 |
if (tempGrammar == null) { |
|
197 |
removeEntry(entry); |
|
198 |
} |
|
199 |
else if ((entry.hash == hash) && equals(entry.desc, desc)) { |
|
200 |
return tempGrammar; |
|
201 |
} |
|
202 |
} |
|
203 |
return null; |
|
204 |
} |
|
205 |
} // getGrammar(XMLGrammarDescription):Grammar |
|
206 |
||
207 |
/** |
|
208 |
* Removes the grammar associated to the specified grammar description from the |
|
209 |
* grammar pool and returns the removed grammar. Currently, the root element name |
|
210 |
* is used as the key for DTD grammars and the target namespace is used |
|
211 |
* as the key for Schema grammars. |
|
212 |
* |
|
213 |
* @param desc The Grammar Description. |
|
214 |
* @return The removed grammar. |
|
215 |
*/ |
|
216 |
public Grammar removeGrammar(XMLGrammarDescription desc) { |
|
217 |
synchronized (fGrammars) { |
|
218 |
clean(); |
|
219 |
int hash = hashCode(desc); |
|
220 |
int index = (hash & 0x7FFFFFFF) % fGrammars.length; |
|
221 |
for (Entry entry = fGrammars[index]; entry != null; entry = entry.next) { |
|
222 |
if ((entry.hash == hash) && equals(entry.desc, desc)) { |
|
223 |
return removeEntry(entry); |
|
224 |
} |
|
225 |
} |
|
226 |
return null; |
|
227 |
} |
|
228 |
} // removeGrammar(XMLGrammarDescription):Grammar |
|
229 |
||
230 |
/** |
|
231 |
* Returns true if the grammar pool contains a grammar associated |
|
232 |
* to the specified grammar description. Currently, the root element name |
|
233 |
* is used as the key for DTD grammars and the target namespace is used |
|
234 |
* as the key for Schema grammars. |
|
235 |
* |
|
236 |
* @param desc The Grammar Description. |
|
237 |
*/ |
|
238 |
public boolean containsGrammar(XMLGrammarDescription desc) { |
|
239 |
synchronized (fGrammars) { |
|
240 |
clean(); |
|
241 |
int hash = hashCode(desc); |
|
242 |
int index = (hash & 0x7FFFFFFF) % fGrammars.length; |
|
243 |
for (Entry entry = fGrammars[index]; entry != null ; entry = entry.next) { |
|
244 |
Grammar tempGrammar = (Grammar) entry.grammar.get(); |
|
245 |
/** If the soft reference has been cleared, remove this entry from the pool. */ |
|
246 |
if (tempGrammar == null) { |
|
247 |
removeEntry(entry); |
|
248 |
} |
|
249 |
else if ((entry.hash == hash) && equals(entry.desc, desc)) { |
|
250 |
return true; |
|
251 |
} |
|
252 |
} |
|
253 |
return false; |
|
254 |
} |
|
255 |
} // containsGrammar(XMLGrammarDescription):boolean |
|
256 |
||
257 |
/* <p> Sets this grammar pool to a "locked" state--i.e., |
|
258 |
* no new grammars will be added until it is "unlocked". |
|
259 |
*/ |
|
260 |
public void lockPool() { |
|
261 |
fPoolIsLocked = true; |
|
262 |
} // lockPool() |
|
263 |
||
264 |
/* <p> Sets this grammar pool to an "unlocked" state--i.e., |
|
265 |
* new grammars will be added when putGrammar or cacheGrammars |
|
266 |
* are called. |
|
267 |
*/ |
|
268 |
public void unlockPool() { |
|
269 |
fPoolIsLocked = false; |
|
270 |
} // unlockPool() |
|
271 |
||
272 |
/* |
|
273 |
* <p>This method clears the pool-i.e., removes references |
|
274 |
* to all the grammars in it.</p> |
|
275 |
*/ |
|
276 |
public void clear() { |
|
277 |
for (int i=0; i<fGrammars.length; i++) { |
|
278 |
if(fGrammars[i] != null) { |
|
279 |
fGrammars[i].clear(); |
|
280 |
fGrammars[i] = null; |
|
281 |
} |
|
282 |
} |
|
283 |
fGrammarCount = 0; |
|
284 |
} // clear() |
|
285 |
||
286 |
/** |
|
287 |
* This method checks whether two grammars are the same. Currently, we compare |
|
288 |
* the root element names for DTD grammars and the target namespaces for Schema grammars. |
|
289 |
* The application can override this behaviour and add its own logic. |
|
290 |
* |
|
291 |
* @param desc1 The grammar description |
|
292 |
* @param desc2 The grammar description of the grammar to be compared to |
|
293 |
* @return True if the grammars are equal, otherwise false |
|
294 |
*/ |
|
295 |
public boolean equals(XMLGrammarDescription desc1, XMLGrammarDescription desc2) { |
|
296 |
if (desc1 instanceof XMLSchemaDescription) { |
|
297 |
if (!(desc2 instanceof XMLSchemaDescription)) { |
|
298 |
return false; |
|
299 |
} |
|
300 |
final XMLSchemaDescription sd1 = (XMLSchemaDescription) desc1; |
|
301 |
final XMLSchemaDescription sd2 = (XMLSchemaDescription) desc2; |
|
302 |
final String targetNamespace = sd1.getTargetNamespace(); |
|
303 |
if (targetNamespace != null) { |
|
304 |
if (!targetNamespace.equals(sd2.getTargetNamespace())) { |
|
305 |
return false; |
|
306 |
} |
|
307 |
} |
|
308 |
else if (sd2.getTargetNamespace() != null) { |
|
309 |
return false; |
|
310 |
} |
|
311 |
// The JAXP 1.3 spec says that the implementation can assume that |
|
312 |
// if two schema location hints are the same they always resolve |
|
313 |
// to the same document. In the default grammar pool implementation |
|
314 |
// we only look at the target namespaces. Here we also compare |
|
315 |
// location hints. |
|
316 |
final String expandedSystemId = sd1.getExpandedSystemId(); |
|
317 |
if (expandedSystemId != null) { |
|
318 |
if (!expandedSystemId.equals(sd2.getExpandedSystemId())) { |
|
319 |
return false; |
|
320 |
} |
|
321 |
} |
|
322 |
else if (sd2.getExpandedSystemId() != null) { |
|
323 |
return false; |
|
324 |
} |
|
325 |
return true; |
|
326 |
} |
|
327 |
return desc1.equals(desc2); |
|
328 |
} |
|
329 |
||
330 |
/** |
|
331 |
* Returns the hash code value for the given grammar description. |
|
332 |
* |
|
333 |
* @param desc The grammar description |
|
334 |
* @return The hash code value |
|
335 |
*/ |
|
336 |
public int hashCode(XMLGrammarDescription desc) { |
|
337 |
if (desc instanceof XMLSchemaDescription) { |
|
338 |
final XMLSchemaDescription sd = (XMLSchemaDescription) desc; |
|
339 |
final String targetNamespace = sd.getTargetNamespace(); |
|
340 |
final String expandedSystemId = sd.getExpandedSystemId(); |
|
341 |
int hash = (targetNamespace != null) ? targetNamespace.hashCode() : 0; |
|
342 |
hash ^= (expandedSystemId != null) ? expandedSystemId.hashCode() : 0; |
|
343 |
return hash; |
|
344 |
} |
|
345 |
return desc.hashCode(); |
|
346 |
} |
|
347 |
||
348 |
/** |
|
349 |
* Removes the given entry from the pool |
|
350 |
* |
|
351 |
* @param entry the entry to remove |
|
352 |
* @return The grammar attached to this entry |
|
353 |
*/ |
|
354 |
private Grammar removeEntry(Entry entry) { |
|
355 |
if (entry.prev != null) { |
|
356 |
entry.prev.next = entry.next; |
|
357 |
} |
|
358 |
else { |
|
359 |
fGrammars[entry.bucket] = entry.next; |
|
360 |
} |
|
361 |
if (entry.next != null) { |
|
362 |
entry.next.prev = entry.prev; |
|
363 |
} |
|
364 |
--fGrammarCount; |
|
365 |
entry.grammar.entry = null; |
|
366 |
return (Grammar) entry.grammar.get(); |
|
367 |
} |
|
368 |
||
369 |
/** |
|
370 |
* Removes stale entries from the pool. |
|
371 |
*/ |
|
372 |
private void clean() { |
|
47359
e1a6c0168741
8181150: Fix lint warnings in JAXP repo: rawtypes and unchecked
joehw
parents:
47216
diff
changeset
|
373 |
Reference<? extends Grammar> ref = fReferenceQueue.poll(); |
6 | 374 |
while (ref != null) { |
375 |
Entry entry = ((SoftGrammarReference) ref).entry; |
|
376 |
if (entry != null) { |
|
377 |
removeEntry(entry); |
|
378 |
} |
|
379 |
ref = fReferenceQueue.poll(); |
|
380 |
} |
|
381 |
} |
|
382 |
||
383 |
/** |
|
384 |
* This class is a grammar pool entry. Each entry acts as a node |
|
385 |
* in a doubly linked list. |
|
386 |
*/ |
|
387 |
static final class Entry { |
|
388 |
||
389 |
public int hash; |
|
390 |
public int bucket; |
|
391 |
public Entry prev; |
|
392 |
public Entry next; |
|
393 |
public XMLGrammarDescription desc; |
|
394 |
public SoftGrammarReference grammar; |
|
395 |
||
47359
e1a6c0168741
8181150: Fix lint warnings in JAXP repo: rawtypes and unchecked
joehw
parents:
47216
diff
changeset
|
396 |
protected Entry(int hash, int bucket, XMLGrammarDescription desc, Grammar grammar, |
e1a6c0168741
8181150: Fix lint warnings in JAXP repo: rawtypes and unchecked
joehw
parents:
47216
diff
changeset
|
397 |
Entry next, ReferenceQueue<Grammar> queue) { |
6 | 398 |
this.hash = hash; |
399 |
this.bucket = bucket; |
|
400 |
this.prev = null; |
|
401 |
this.next = next; |
|
402 |
if (next != null) { |
|
403 |
next.prev = this; |
|
404 |
} |
|
405 |
this.desc = desc; |
|
406 |
this.grammar = new SoftGrammarReference(this, grammar, queue); |
|
407 |
} |
|
408 |
||
409 |
// clear this entry; useful to promote garbage collection |
|
410 |
// since reduces reference count of objects to be destroyed |
|
411 |
protected void clear () { |
|
412 |
desc = null; |
|
413 |
grammar = null; |
|
414 |
if(next != null) { |
|
415 |
next.clear(); |
|
416 |
next = null; |
|
417 |
} |
|
418 |
} // clear() |
|
419 |
||
420 |
} // class Entry |
|
421 |
||
422 |
/** |
|
423 |
* This class stores a soft reference to a grammar object. It keeps a reference |
|
424 |
* to its associated entry, so that it can be easily removed from the pool. |
|
425 |
*/ |
|
47359
e1a6c0168741
8181150: Fix lint warnings in JAXP repo: rawtypes and unchecked
joehw
parents:
47216
diff
changeset
|
426 |
static final class SoftGrammarReference extends SoftReference<Grammar> { |
6 | 427 |
|
428 |
public Entry entry; |
|
429 |
||
47359
e1a6c0168741
8181150: Fix lint warnings in JAXP repo: rawtypes and unchecked
joehw
parents:
47216
diff
changeset
|
430 |
protected SoftGrammarReference(Entry entry, Grammar grammar, ReferenceQueue<Grammar> queue) { |
6 | 431 |
super(grammar, queue); |
432 |
this.entry = entry; |
|
433 |
} |
|
434 |
||
435 |
} // class SoftGrammarReference |
|
436 |
||
437 |
} // class SoftReferenceGrammarPool |