Line 0
Link Here
|
|
|
1 |
/* |
2 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
3 |
* |
4 |
* Copyright 2008 Sun Microsystems, Inc. All rights reserved. |
5 |
* |
6 |
* The contents of this file are subject to the terms of either the GNU |
7 |
* General Public License Version 2 only ("GPL") or the Common |
8 |
* Development and Distribution License("CDDL") (collectively, the |
9 |
* "License"). You may not use this file except in compliance with the |
10 |
* License. You can obtain a copy of the License at |
11 |
* http://www.netbeans.org/cddl-gplv2.html |
12 |
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the |
13 |
* specific language governing permissions and limitations under the |
14 |
* License. When distributing the software, include this License Header |
15 |
* Notice in each file and include the License file at |
16 |
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this |
17 |
* particular file as subject to the "Classpath" exception as provided |
18 |
* by Sun in the GPL Version 2 section of the License file that |
19 |
* accompanied this code. If applicable, add the following below the |
20 |
* License Header, with the fields enclosed by brackets [] replaced by |
21 |
* your own identifying information: |
22 |
* "Portions Copyrighted [year] [name of copyright owner]" |
23 |
* |
24 |
* Contributor(s): |
25 |
* |
26 |
* The Original Software is NetBeans. The Initial Developer of the Original |
27 |
* Software is Sun Microsystems, Inc. Portions Copyright 2008 Sun |
28 |
* Microsystems, Inc. All Rights Reserved. |
29 |
* |
30 |
* If you wish your version of this file to be governed by only the CDDL |
31 |
* or only the GPL Version 2, indicate your decision by adding |
32 |
* "[Contributor] elects to include this software in this distribution |
33 |
* under the [CDDL or GPL Version 2] license." If you do not indicate a |
34 |
* single choice of license, a recipient has the option to distribute |
35 |
* your version of this file under either the CDDL, the GPL Version 2 or |
36 |
* to extend the choice of license to its licensees as provided above. |
37 |
* However, if you add GPL Version 2 code and therefore, elected the GPL |
38 |
* Version 2 license, then the option applies only if the new code is |
39 |
* made subject to such option by the copyright holder. |
40 |
*/ |
41 |
|
42 |
package org.netbeans.modules.projectapi; |
43 |
|
44 |
import java.io.IOException; |
45 |
import java.lang.ref.Reference; |
46 |
import java.lang.ref.WeakReference; |
47 |
import java.util.Collection; |
48 |
import java.util.Collections; |
49 |
import java.util.Enumeration; |
50 |
import java.util.HashMap; |
51 |
import java.util.HashSet; |
52 |
import java.util.LinkedHashSet; |
53 |
import java.util.LinkedList; |
54 |
import java.util.List; |
55 |
import java.util.Map; |
56 |
import java.util.Map.Entry; |
57 |
import java.util.Set; |
58 |
import java.util.prefs.AbstractPreferences; |
59 |
import java.util.prefs.BackingStoreException; |
60 |
import java.util.prefs.Preferences; |
61 |
import org.netbeans.api.project.Project; |
62 |
import org.netbeans.api.project.ProjectManager; |
63 |
import org.netbeans.spi.project.AuxiliaryConfiguration; |
64 |
import org.netbeans.spi.project.AuxiliaryProperties; |
65 |
import org.openide.filesystems.FileObject; |
66 |
import org.openide.modules.ModuleInfo; |
67 |
import org.openide.util.Exceptions; |
68 |
import org.openide.util.Lookup; |
69 |
import org.openide.util.RequestProcessor; |
70 |
import org.openide.util.RequestProcessor.Task; |
71 |
import org.openide.util.Utilities; |
72 |
import org.openide.xml.XMLUtil; |
73 |
import org.w3c.dom.DOMException; |
74 |
import org.w3c.dom.Element; |
75 |
import org.w3c.dom.Node; |
76 |
import org.w3c.dom.NodeList; |
77 |
|
78 |
/** |
79 |
* @author Jan Lahoda |
80 |
*/ |
81 |
public class AuxiliaryConfigBasedPreferencesProvider { |
82 |
|
83 |
private static Map<Project, Reference<AuxiliaryConfigBasedPreferencesProvider>> projects2SharedPrefs = new HashMap<Project, Reference<AuxiliaryConfigBasedPreferencesProvider>>(); |
84 |
private static Map<Project, Reference<AuxiliaryConfigBasedPreferencesProvider>> projects2PrivatePrefs = new HashMap<Project, Reference<AuxiliaryConfigBasedPreferencesProvider>>(); |
85 |
|
86 |
static synchronized AuxiliaryConfigBasedPreferencesProvider findProvider(Project p, boolean shared) { |
87 |
Map<Project, Reference<AuxiliaryConfigBasedPreferencesProvider>> target = shared ? projects2SharedPrefs : projects2PrivatePrefs; |
88 |
Reference<AuxiliaryConfigBasedPreferencesProvider> provRef = target.get(p); |
89 |
AuxiliaryConfigBasedPreferencesProvider prov = provRef != null ? provRef.get() : null; |
90 |
|
91 |
if (prov != null) { |
92 |
return prov; |
93 |
} |
94 |
|
95 |
AuxiliaryConfiguration ac = p.getLookup().lookup(AuxiliaryConfiguration.class); |
96 |
AuxiliaryProperties ap = p.getLookup().lookup(AuxiliaryProperties.class); |
97 |
|
98 |
if (ac != null || ap != null) { |
99 |
target.put(p, new CleaningWeakReference<AuxiliaryConfigBasedPreferencesProvider>(prov = new AuxiliaryConfigBasedPreferencesProvider(p, ac, ap, shared), target, p)); |
100 |
} else { |
101 |
if (!shared) { |
102 |
ap = new FallbackAuxiliaryPropertiesImpl(p.getProjectDirectory()); |
103 |
target.put(p, new CleaningWeakReference<AuxiliaryConfigBasedPreferencesProvider>(prov = new AuxiliaryConfigBasedPreferencesProvider(p, null, ap, shared), target, p)); |
104 |
} |
105 |
} |
106 |
|
107 |
return prov; |
108 |
} |
109 |
|
110 |
public static Preferences getPreferences(Project project, Class clazz, boolean shared) { |
111 |
AuxiliaryConfigBasedPreferencesProvider provider = findProvider(project, shared); |
112 |
|
113 |
if (provider == null) { |
114 |
return null; |
115 |
} |
116 |
|
117 |
return provider.findModule(AuxiliaryConfigBasedPreferencesProvider.findCNBForClass(clazz)); |
118 |
} |
119 |
|
120 |
private static String encodeString(String s, Character additionalValid) { |
121 |
StringBuilder result = new StringBuilder(); |
122 |
|
123 |
for (char c : s.toCharArray()) { |
124 |
if (VALID_KEY_CHARACTERS.indexOf(c) != (-1) || (additionalValid != null && ((char) additionalValid) == c)) { |
125 |
result.append(c); |
126 |
} else { |
127 |
result.append("_"); |
128 |
result.append(Integer.toHexString((int) c)); |
129 |
result.append("_"); |
130 |
} |
131 |
} |
132 |
|
133 |
return result.toString(); |
134 |
} |
135 |
|
136 |
private static String decodeString(String s) { |
137 |
StringBuilder result = new StringBuilder(); |
138 |
String[] parts = s.split("_"); |
139 |
|
140 |
for (int cntr = 0; cntr < parts.length; cntr += 2) { |
141 |
result.append(parts[cntr]); |
142 |
|
143 |
if (cntr + 1 < parts.length) { |
144 |
result.append((char) Integer.parseInt(parts[cntr + 1], 16)); |
145 |
} |
146 |
} |
147 |
|
148 |
return result.toString(); |
149 |
} |
150 |
|
151 |
static final String NAMESPACE = "http://www.netbeans.org/ns/auxiliary-configuration-preferences/1"; |
152 |
|
153 |
static final String EL_PREFERENCES = "preferences"; |
154 |
private static final String EL_MODULE = "module"; |
155 |
private static final String EL_PROPERTY = "property"; |
156 |
private static final String EL_NODE = "node"; |
157 |
|
158 |
private static final String ATTR_NAME = "name"; |
159 |
private static final String ATTR_VALUE = "value"; |
160 |
|
161 |
private static final String VALID_KEY_CHARACTERS = "ABCDEFGHIJKLMNOPQRSTVUWXYZabcdefghijklmnopqrstvuwxyz0123456789-"; |
162 |
|
163 |
private static final RequestProcessor WORKER = new RequestProcessor("AuxiliaryConfigBasedPreferencesProvider worker", 1); |
164 |
private static final int AUTOFLUSH_TIMEOUT = 5000; |
165 |
|
166 |
private final Project project; |
167 |
private final AuxiliaryConfiguration ac; |
168 |
private final AuxiliaryProperties ap; |
169 |
private final boolean shared; |
170 |
private final Map<String, Reference<AuxiliaryConfigBasedPreferences>> module2Preferences = new HashMap<String, Reference<AuxiliaryConfigBasedPreferences>>(); |
171 |
private Element configRoot; |
172 |
private boolean modified; |
173 |
private final Task autoFlushTask = WORKER.create(new Runnable() { |
174 |
public void run() { |
175 |
flush(); |
176 |
} |
177 |
}); |
178 |
|
179 |
private final Map<String, Map<String, String>> path2Data = new HashMap<String, Map<String, String>>(); |
180 |
private final Map<String, Set<String>> path2Removed = new HashMap<String, Set<String>>(); |
181 |
private final Set<String> removedNodes = new HashSet<String>(); |
182 |
private final Set<String> createdNodes = new HashSet<String>(); |
183 |
|
184 |
AuxiliaryConfigBasedPreferencesProvider(Project project, AuxiliaryConfiguration ac, AuxiliaryProperties ap, boolean shared) { |
185 |
this.project = project; |
186 |
this.ac = ac; |
187 |
this.ap = ap; |
188 |
this.shared = shared; |
189 |
loadConfigRoot(); |
190 |
} |
191 |
|
192 |
private void loadConfigRoot() { |
193 |
if (ac == null) { |
194 |
return ; |
195 |
} |
196 |
|
197 |
Element configRootLoc = ac.getConfigurationFragment(EL_PREFERENCES, NAMESPACE, shared); |
198 |
|
199 |
if (configRootLoc == null) { |
200 |
configRootLoc = XMLUtil.createDocument(EL_PREFERENCES, NAMESPACE, null, null).createElementNS(NAMESPACE, |
201 |
EL_PREFERENCES); |
202 |
} |
203 |
|
204 |
this.configRoot = configRootLoc; |
205 |
} |
206 |
|
207 |
synchronized void flush() { |
208 |
if (!modified) { |
209 |
return ; |
210 |
} |
211 |
|
212 |
boolean domModified = false; |
213 |
|
214 |
for (String removedNode : removedNodes) { |
215 |
if (ac != null) { |
216 |
Element el = findRelative(removedNode, false); |
217 |
|
218 |
if (el != null) { |
219 |
el.getParentNode().removeChild(el); |
220 |
} |
221 |
|
222 |
domModified = true; |
223 |
} |
224 |
|
225 |
if (ap != null) { |
226 |
String propName = toPropertyName(removedNode, ""); |
227 |
|
228 |
for (String key : ap.listKeys(shared)) { |
229 |
if (key.startsWith(propName)) { |
230 |
ap.put(key, null, shared); |
231 |
} |
232 |
} |
233 |
} |
234 |
} |
235 |
|
236 |
for (String createdNode : createdNodes) { |
237 |
if (ap != null) { |
238 |
String propName = toPropertyName(createdNode, ""); |
239 |
|
240 |
ap.put(propName, "", shared); |
241 |
} else { |
242 |
findRelative(createdNode, true); |
243 |
|
244 |
domModified = true; |
245 |
} |
246 |
} |
247 |
|
248 |
for (Entry<String, Map<String, String>> e : path2Data.entrySet()) { |
249 |
if (ap != null) { |
250 |
for (Entry<String, String> value : e.getValue().entrySet()) { |
251 |
ap.put(toPropertyName(e.getKey(), value.getKey()), value.getValue(), shared); |
252 |
} |
253 |
} else { |
254 |
Element el = findRelative(e.getKey(), true); |
255 |
|
256 |
if (el != null) { |
257 |
for (Entry<String, String> value : e.getValue().entrySet()) { |
258 |
Element p = find(el, value.getKey(), EL_PROPERTY, true); |
259 |
|
260 |
p.setAttribute(ATTR_VALUE, value.getValue()); |
261 |
} |
262 |
|
263 |
domModified = true; |
264 |
} |
265 |
} |
266 |
} |
267 |
|
268 |
for (Entry<String, Set<String>> e : path2Removed.entrySet()) { |
269 |
if (ac != null) { |
270 |
Element el = findRelative(e.getKey(), false); |
271 |
|
272 |
if (el != null) { |
273 |
for (String removed : e.getValue()) { |
274 |
Element p = find(el, removed, EL_PROPERTY, true); |
275 |
|
276 |
el.removeChild(p); |
277 |
} |
278 |
|
279 |
domModified = true; |
280 |
} |
281 |
} |
282 |
|
283 |
if (ap != null) { |
284 |
for (String removed : e.getValue()) { |
285 |
ap.put(toPropertyName(e.getKey(), removed), "", shared); |
286 |
} |
287 |
} |
288 |
} |
289 |
|
290 |
if (domModified) { |
291 |
ac.putConfigurationFragment(configRoot, true); |
292 |
} |
293 |
|
294 |
try { |
295 |
ProjectManager.getDefault().saveProject(project); |
296 |
} catch (IOException ex) { |
297 |
Exceptions.printStackTrace(ex); |
298 |
} |
299 |
|
300 |
path2Data.clear(); |
301 |
path2Removed.clear(); |
302 |
removedNodes.clear(); |
303 |
createdNodes.clear(); |
304 |
modified = false; |
305 |
} |
306 |
|
307 |
synchronized void sync() { |
308 |
loadConfigRoot(); |
309 |
flush(); |
310 |
} |
311 |
|
312 |
private void markModified() { |
313 |
autoFlushTask.cancel(); |
314 |
autoFlushTask.schedule(AUTOFLUSH_TIMEOUT); |
315 |
modified = true; |
316 |
} |
317 |
|
318 |
private static String findCNBForClass(Class cls) { |
319 |
String absolutePath = null; |
320 |
ClassLoader cl = cls.getClassLoader(); |
321 |
for (ModuleInfo module : Lookup.getDefault().lookupAll(ModuleInfo.class)) { |
322 |
if (module.isEnabled() && module.getClassLoader() == cl) { |
323 |
absolutePath = module.getCodeNameBase(); |
324 |
break; |
325 |
} |
326 |
} |
327 |
if (absolutePath == null) { |
328 |
absolutePath = cls.getName().replaceFirst("(^|\\.)[^.]+$", "");//NOI18N |
329 |
} |
330 |
assert absolutePath != null; |
331 |
return absolutePath.replace('.', '-'); |
332 |
} |
333 |
|
334 |
public synchronized Preferences findModule(String moduleName) { |
335 |
Reference<AuxiliaryConfigBasedPreferences> prefRef = module2Preferences.get(moduleName); |
336 |
AuxiliaryConfigBasedPreferences pref = prefRef != null ? prefRef.get() : null; |
337 |
|
338 |
if (pref == null) { |
339 |
module2Preferences.put(moduleName, new CleaningWeakReference<AuxiliaryConfigBasedPreferences>(pref = new AuxiliaryConfigBasedPreferences(null, "", moduleName), module2Preferences, moduleName)); |
340 |
} |
341 |
|
342 |
return pref; |
343 |
} |
344 |
|
345 |
private Element findRelative(String path, boolean createIfMissing) { |
346 |
if (ac == null) { |
347 |
return null; |
348 |
} |
349 |
|
350 |
String[] sep = path.split("/"); |
351 |
|
352 |
assert sep.length > 0; |
353 |
|
354 |
Element e = find(configRoot, sep[0], EL_MODULE, createIfMissing); |
355 |
|
356 |
for (int cntr = 1; cntr < sep.length && e != null; cntr++) { |
357 |
e = find(e, sep[cntr], EL_NODE, createIfMissing); |
358 |
} |
359 |
|
360 |
return e; |
361 |
} |
362 |
|
363 |
private Map<String, String> getData(String path) { |
364 |
Map<String, String> data = path2Data.get(path); |
365 |
|
366 |
if (data == null) { |
367 |
path2Data.put(path, data = new HashMap<String, String>()); |
368 |
} |
369 |
|
370 |
return data; |
371 |
} |
372 |
|
373 |
private Set<String> getRemoved(String path) { |
374 |
Set<String> removed = path2Removed.get(path); |
375 |
|
376 |
if (removed == null) { |
377 |
path2Removed.put(path, removed = new HashSet<String>()); |
378 |
} |
379 |
|
380 |
return removed; |
381 |
} |
382 |
|
383 |
private void removeNode(String path) { |
384 |
path2Data.remove(path); |
385 |
path2Removed.remove(path); |
386 |
createdNodes.remove(path); |
387 |
removedNodes.add(path); |
388 |
} |
389 |
|
390 |
private boolean isRemovedNode(String path) { |
391 |
return removedNodes.contains(path); |
392 |
} |
393 |
|
394 |
private static Element find(Element dom, String key, String elementName, boolean createIfMissing) { |
395 |
NodeList nl = dom.getChildNodes(); |
396 |
|
397 |
for (int cntr = 0; cntr < nl.getLength(); cntr++) { |
398 |
Node n = nl.item(cntr); |
399 |
|
400 |
if (n.getNodeType() == Node.ELEMENT_NODE && NAMESPACE.equals(n.getNamespaceURI()) && elementName.equals(n.getLocalName())) { |
401 |
if (key.equals(((Element) n).getAttribute(ATTR_NAME))) { |
402 |
return (Element) n; |
403 |
} |
404 |
} |
405 |
} |
406 |
|
407 |
if (!createIfMissing) { |
408 |
return null; |
409 |
} |
410 |
|
411 |
Element el = dom.getOwnerDocument().createElementNS(NAMESPACE, elementName); |
412 |
|
413 |
el.setAttribute(ATTR_NAME, key); |
414 |
|
415 |
dom.appendChild(el); |
416 |
|
417 |
return el; |
418 |
} |
419 |
|
420 |
private String toPropertyName(String path, String propertyName) { |
421 |
return encodeString(path, '/').replace('/', '.') + '.' + encodeString(propertyName, null); |
422 |
} |
423 |
|
424 |
private class AuxiliaryConfigBasedPreferences extends AbstractPreferences { |
425 |
|
426 |
private final String path; |
427 |
|
428 |
public AuxiliaryConfigBasedPreferences(AbstractPreferences parent, String name, String path) { |
429 |
super(parent, name); |
430 |
this.path = path; |
431 |
} |
432 |
|
433 |
@Override |
434 |
protected void putSpi(String key, String value) { |
435 |
synchronized (AuxiliaryConfigBasedPreferencesProvider.this) { |
436 |
getData(path).put(key, value); |
437 |
getRemoved(path).remove(key); |
438 |
|
439 |
markModified(); |
440 |
} |
441 |
} |
442 |
|
443 |
@Override |
444 |
protected String getSpi(String key) { |
445 |
synchronized (AuxiliaryConfigBasedPreferencesProvider.this) { |
446 |
if (getRemoved(path).contains(key)) { |
447 |
return null; |
448 |
} |
449 |
|
450 |
if (getData(path).containsKey(key)) { |
451 |
return getData(path).get(key); |
452 |
} |
453 |
|
454 |
if (isRemovedNode(path)) { |
455 |
return null; |
456 |
} |
457 |
|
458 |
if (ap != null ) { |
459 |
String keyProp = toPropertyName(path, key); |
460 |
String res = AuxiliaryConfigBasedPreferencesProvider.this.ap.get(keyProp, shared); |
461 |
|
462 |
if (res != null) { |
463 |
return res; |
464 |
} |
465 |
} |
466 |
Element p = findRelative(path, false); |
467 |
|
468 |
p = p != null ? AuxiliaryConfigBasedPreferencesProvider.find(p, key, EL_PROPERTY, false) : null; |
469 |
|
470 |
if (p == null) { |
471 |
return null; |
472 |
} |
473 |
|
474 |
return p.getAttribute(ATTR_VALUE); |
475 |
} |
476 |
} |
477 |
|
478 |
@Override |
479 |
protected void removeSpi(String key) { |
480 |
synchronized (AuxiliaryConfigBasedPreferencesProvider.this) { |
481 |
getData(path).remove(key); |
482 |
getRemoved(path).add(key); |
483 |
|
484 |
markModified(); |
485 |
} |
486 |
} |
487 |
|
488 |
@Override |
489 |
protected void removeNodeSpi() throws BackingStoreException { |
490 |
synchronized (AuxiliaryConfigBasedPreferencesProvider.this) { |
491 |
AuxiliaryConfigBasedPreferencesProvider.this.removeNode(path); |
492 |
markModified(); |
493 |
} |
494 |
} |
495 |
|
496 |
@Override |
497 |
protected String[] keysSpi() throws BackingStoreException { |
498 |
synchronized (AuxiliaryConfigBasedPreferencesProvider.this) { |
499 |
Collection<String> result = new LinkedHashSet<String>(); |
500 |
|
501 |
if (!isRemovedNode(path)) { |
502 |
result.addAll(list(EL_PROPERTY)); |
503 |
} |
504 |
|
505 |
if (ap != null) { |
506 |
String prefix = toPropertyName(path, ""); |
507 |
|
508 |
for (String key : ap.listKeys(shared)) { |
509 |
if (key.startsWith(prefix)) { |
510 |
String name = key.substring(prefix.length()); |
511 |
|
512 |
if (name.length() > 0 && name.indexOf('.') == (-1)) { |
513 |
result.add(decodeString(name)); |
514 |
} |
515 |
} |
516 |
} |
517 |
} |
518 |
|
519 |
result.addAll(getData(path).keySet()); |
520 |
result.removeAll(getRemoved(path)); |
521 |
|
522 |
return result.toArray(new String[0]); |
523 |
} |
524 |
} |
525 |
|
526 |
@Override |
527 |
protected String[] childrenNamesSpi() throws BackingStoreException { |
528 |
synchronized (AuxiliaryConfigBasedPreferencesProvider.this) { |
529 |
return getChildrenNames().toArray(new String[0]); |
530 |
} |
531 |
} |
532 |
|
533 |
@Override |
534 |
protected AbstractPreferences childSpi(String name) { |
535 |
synchronized (AuxiliaryConfigBasedPreferencesProvider.this) { |
536 |
String nuePath = path + "/" + name; |
537 |
if (!getChildrenNames().contains(name)) { |
538 |
AuxiliaryConfigBasedPreferencesProvider.this.createdNodes.add(nuePath); |
539 |
} |
540 |
|
541 |
return new AuxiliaryConfigBasedPreferences(this, name, nuePath); |
542 |
} |
543 |
} |
544 |
|
545 |
@Override |
546 |
public void sync() throws BackingStoreException { |
547 |
AuxiliaryConfigBasedPreferencesProvider.this.sync(); |
548 |
} |
549 |
|
550 |
@Override |
551 |
protected void syncSpi() throws BackingStoreException { |
552 |
throw new UnsupportedOperationException("Should never be called."); |
553 |
} |
554 |
|
555 |
@Override |
556 |
public void flush() throws BackingStoreException { |
557 |
AuxiliaryConfigBasedPreferencesProvider.this.flush(); |
558 |
} |
559 |
|
560 |
@Override |
561 |
protected void flushSpi() throws BackingStoreException { |
562 |
throw new UnsupportedOperationException("Should never be called."); |
563 |
} |
564 |
|
565 |
private Collection<String> getChildrenNames() { |
566 |
Collection<String> result = new LinkedHashSet<String>(); |
567 |
|
568 |
if (!isRemovedNode(path)) { |
569 |
result.addAll(list(EL_NODE)); |
570 |
} |
571 |
|
572 |
for (String removed : removedNodes) { |
573 |
int slash = removed.lastIndexOf('/'); |
574 |
|
575 |
if (path.equals(removed.substring(slash))) { |
576 |
result.remove(removed.substring(slash + 1)); |
577 |
} |
578 |
} |
579 |
|
580 |
if (ap != null) { |
581 |
String prefix = toPropertyName(path, ""); |
582 |
|
583 |
for (String key : ap.listKeys(shared)) { |
584 |
if (key.startsWith(prefix)) { |
585 |
String name = key.substring(prefix.length()); |
586 |
|
587 |
if (name.length() > 0 && name.indexOf('.') != (-1)) { |
588 |
name = name.substring(0, name.indexOf('.')); |
589 |
result.add(decodeString(name)); |
590 |
} |
591 |
} |
592 |
} |
593 |
} |
594 |
|
595 |
for (String created : createdNodes) { |
596 |
int slash = created.lastIndexOf('/'); |
597 |
|
598 |
if (path.equals(created.substring(slash))) { |
599 |
result.add(created.substring(slash + 1)); |
600 |
} |
601 |
} |
602 |
|
603 |
return result; |
604 |
} |
605 |
|
606 |
private Collection<String> list(String elementName) throws DOMException { |
607 |
Element dom = findRelative(path, false); |
608 |
|
609 |
if (dom == null) { |
610 |
return Collections.emptyList(); |
611 |
} |
612 |
|
613 |
List<String> names = new LinkedList<String>(); |
614 |
NodeList nl = dom.getElementsByTagNameNS(NAMESPACE, elementName); |
615 |
|
616 |
for (int cntr = 0; cntr < nl.getLength(); cntr++) { |
617 |
Node n = nl.item(cntr); |
618 |
|
619 |
names.add(((Element) n).getAttribute(ATTR_NAME)); |
620 |
} |
621 |
|
622 |
return names; |
623 |
} |
624 |
|
625 |
} |
626 |
|
627 |
private static final class FallbackAuxiliaryPropertiesImpl implements AuxiliaryProperties { |
628 |
|
629 |
private static final String PREFIX = "auxiliary."; |
630 |
private FileObject projectDir; |
631 |
|
632 |
public FallbackAuxiliaryPropertiesImpl(FileObject projectDir) { |
633 |
this.projectDir = projectDir; |
634 |
} |
635 |
|
636 |
public String get(String key, boolean shared) { |
637 |
assert !shared; |
638 |
|
639 |
Object v = projectDir.getAttribute(PREFIX + key); |
640 |
|
641 |
return v instanceof String ? (String) v : null; |
642 |
} |
643 |
|
644 |
public void put(String key, String value, boolean shared) { |
645 |
assert !shared; |
646 |
|
647 |
try { |
648 |
projectDir.setAttribute(PREFIX + key, value); |
649 |
} catch (IOException ex) { |
650 |
Exceptions.printStackTrace(ex); |
651 |
} |
652 |
} |
653 |
|
654 |
public Iterable<String> listKeys(boolean shared) { |
655 |
assert !shared; |
656 |
|
657 |
List<String> result = new LinkedList<String>(); |
658 |
|
659 |
for (Enumeration<String> en = projectDir.getAttributes(); en.hasMoreElements(); ) { |
660 |
String key = en.nextElement(); |
661 |
|
662 |
if (key.startsWith(PREFIX)) { |
663 |
key = key.substring(PREFIX.length()); |
664 |
|
665 |
if (get(key, shared) != null) { |
666 |
result.add(key); |
667 |
} |
668 |
} |
669 |
} |
670 |
|
671 |
return result; |
672 |
} |
673 |
|
674 |
} |
675 |
|
676 |
private static final class CleaningWeakReference<T> extends WeakReference<T> implements Runnable { |
677 |
private final Map<?, ?> map; |
678 |
private final Object key; |
679 |
|
680 |
public CleaningWeakReference(T data, Map<?, ?> map, Object key) { |
681 |
super(data, Utilities.activeReferenceQueue()); |
682 |
this.map = map; |
683 |
this.key = key; |
684 |
} |
685 |
|
686 |
public void run() { |
687 |
map.remove(key); |
688 |
} |
689 |
} |
690 |
} |