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.WeakHashMap; |
59 |
import java.util.prefs.AbstractPreferences; |
60 |
import java.util.prefs.BackingStoreException; |
61 |
import java.util.prefs.Preferences; |
62 |
import org.netbeans.api.project.Project; |
63 |
import org.netbeans.api.project.ProjectManager; |
64 |
import org.netbeans.spi.project.AuxiliaryConfiguration; |
65 |
import org.netbeans.spi.project.AuxiliaryProperties; |
66 |
import org.openide.filesystems.FileObject; |
67 |
import org.openide.modules.ModuleInfo; |
68 |
import org.openide.util.Exceptions; |
69 |
import org.openide.util.Lookup; |
70 |
import org.openide.util.RequestProcessor; |
71 |
import org.openide.util.RequestProcessor.Task; |
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 WeakHashMap<Project, Reference<AuxiliaryConfigBasedPreferencesProvider>>(); |
84 |
private static Map<Project, Reference<AuxiliaryConfigBasedPreferencesProvider>> projects2PrivatePrefs = new WeakHashMap<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 WeakReference<AuxiliaryConfigBasedPreferencesProvider>(prov = new AuxiliaryConfigBasedPreferencesProvider(p, ac, ap, shared))); |
100 |
} else { |
101 |
ap = new FallbackAuxiliaryPropertiesImpl(p.getProjectDirectory()); |
102 |
target.put(p, new WeakReference<AuxiliaryConfigBasedPreferencesProvider>(prov = new AuxiliaryConfigBasedPreferencesProvider(p, null, ap, shared))); |
103 |
} |
104 |
|
105 |
return prov; |
106 |
} |
107 |
|
108 |
public static Preferences getPreferences(Project project, Class clazz, boolean shared) { |
109 |
AuxiliaryConfigBasedPreferencesProvider provider = findProvider(project, shared); |
110 |
|
111 |
if (provider == null) { |
112 |
return null; |
113 |
} |
114 |
|
115 |
return provider.findModule(AuxiliaryConfigBasedPreferencesProvider.findCNBForClass(clazz)); |
116 |
} |
117 |
|
118 |
private static String encodeString(String s) { |
119 |
StringBuilder result = new StringBuilder(); |
120 |
|
121 |
for (char c : s.toCharArray()) { |
122 |
if (INVALID_KEY_CHARACTERS.indexOf(c) == (-1)) { |
123 |
result.append(c); |
124 |
} else { |
125 |
result.append("_"); |
126 |
result.append(Integer.toHexString((int) c)); |
127 |
result.append("_"); |
128 |
} |
129 |
} |
130 |
|
131 |
return result.toString(); |
132 |
} |
133 |
|
134 |
private static String decodeString(String s) { |
135 |
StringBuilder result = new StringBuilder(); |
136 |
String[] parts = s.split("_"); |
137 |
|
138 |
for (int cntr = 0; cntr < parts.length; cntr += 2) { |
139 |
result.append(parts[cntr]); |
140 |
|
141 |
if (cntr + 1 < parts.length) { |
142 |
result.append((char) Integer.parseInt(parts[cntr + 1], 16)); |
143 |
} |
144 |
} |
145 |
|
146 |
return result.toString(); |
147 |
} |
148 |
|
149 |
static final String NAMESPACE = "http://www.netbeans.org/ns/auxiliary-configuration-preferences/1"; |
150 |
|
151 |
static final String EL_PREFERENCES = "preferences"; |
152 |
private static final String EL_MODULE = "module"; |
153 |
private static final String EL_PROPERTY = "property"; |
154 |
private static final String EL_NODE = "node"; |
155 |
|
156 |
private static final String ATTR_NAME = "name"; |
157 |
private static final String ATTR_VALUE = "value"; |
158 |
|
159 |
private static final String INVALID_KEY_CHARACTERS = "_."; |
160 |
|
161 |
private static final RequestProcessor WORKER = new RequestProcessor("AuxiliaryConfigBasedPreferencesProvider worker", 1); |
162 |
private static final int AUTOFLUSH_TIMEOUT = 5000; |
163 |
|
164 |
private final Project project; |
165 |
private final AuxiliaryConfiguration ac; |
166 |
private final AuxiliaryProperties ap; |
167 |
private final boolean shared; |
168 |
private final Map<String, Reference<AuxiliaryConfigBasedPreferences>> module2Preferences = new HashMap<String, Reference<AuxiliaryConfigBasedPreferences>>(); |
169 |
private Element configRoot; |
170 |
private boolean modified; |
171 |
private final Task autoFlushTask = WORKER.create(new Runnable() { |
172 |
public void run() { |
173 |
flush(); |
174 |
} |
175 |
}); |
176 |
|
177 |
private final Map<String, Map<String, String>> path2Data = new HashMap<String, Map<String, String>>(); |
178 |
private final Map<String, Set<String>> path2Removed = new HashMap<String, Set<String>>(); |
179 |
private final Set<String> removedNodes = new HashSet<String>(); |
180 |
private final Set<String> createdNodes = new HashSet<String>(); |
181 |
|
182 |
AuxiliaryConfigBasedPreferencesProvider(Project project, AuxiliaryConfiguration ac, AuxiliaryProperties ap, boolean shared) { |
183 |
this.project = project; |
184 |
this.ac = ac; |
185 |
this.ap = ap; |
186 |
this.shared = shared; |
187 |
loadConfigRoot(); |
188 |
} |
189 |
|
190 |
private void loadConfigRoot() { |
191 |
if (ac == null) { |
192 |
return ; |
193 |
} |
194 |
|
195 |
Element configRootLoc = ac.getConfigurationFragment(EL_PREFERENCES, NAMESPACE, shared); |
196 |
|
197 |
if (configRootLoc == null) { |
198 |
configRootLoc = XMLUtil.createDocument(EL_PREFERENCES, NAMESPACE, null, null).createElementNS(NAMESPACE, |
199 |
EL_PREFERENCES); |
200 |
} |
201 |
|
202 |
this.configRoot = configRootLoc; |
203 |
} |
204 |
|
205 |
synchronized void flush() { |
206 |
if (!modified) { |
207 |
return ; |
208 |
} |
209 |
|
210 |
boolean domModified = false; |
211 |
|
212 |
for (String removedNode : removedNodes) { |
213 |
if (ac != null) { |
214 |
Element el = findRelative(removedNode, false); |
215 |
|
216 |
if (el != null) { |
217 |
el.getParentNode().removeChild(el); |
218 |
|
219 |
domModified = true; |
220 |
} |
221 |
} |
222 |
|
223 |
if (ap != null) { |
224 |
String propName = toPropertyName(removedNode, ""); |
225 |
|
226 |
for (String key : ap.listKeys(shared)) { |
227 |
if (key.startsWith(propName)) { |
228 |
ap.put(key, null, shared); |
229 |
} |
230 |
} |
231 |
} |
232 |
} |
233 |
|
234 |
for (String createdNode : createdNodes) { |
235 |
if (ap != null) { |
236 |
String propName = toPropertyName(createdNode, ""); |
237 |
|
238 |
ap.put(propName, "", shared); |
239 |
} else { |
240 |
findRelative(createdNode, true); |
241 |
|
242 |
domModified = true; |
243 |
} |
244 |
} |
245 |
|
246 |
for (Entry<String, Map<String, String>> e : path2Data.entrySet()) { |
247 |
if (ap != null) { |
248 |
for (Entry<String, String> value : e.getValue().entrySet()) { |
249 |
ap.put(toPropertyName(e.getKey(), value.getKey()), value.getValue(), shared); |
250 |
} |
251 |
} else { |
252 |
Element el = findRelative(e.getKey(), true); |
253 |
|
254 |
if (el != null) { |
255 |
for (Entry<String, String> value : e.getValue().entrySet()) { |
256 |
Element p = find(el, value.getKey(), EL_PROPERTY, true); |
257 |
|
258 |
p.setAttribute(ATTR_VALUE, value.getValue()); |
259 |
} |
260 |
|
261 |
domModified = true; |
262 |
} |
263 |
} |
264 |
} |
265 |
|
266 |
for (Entry<String, Set<String>> e : path2Removed.entrySet()) { |
267 |
if (ac != null) { |
268 |
Element el = findRelative(e.getKey(), false); |
269 |
|
270 |
if (el != null) { |
271 |
for (String removed : e.getValue()) { |
272 |
Element p = find(el, removed, EL_PROPERTY, true); |
273 |
|
274 |
el.removeChild(p); |
275 |
} |
276 |
|
277 |
domModified = true; |
278 |
} |
279 |
} |
280 |
|
281 |
if (ap != null) { |
282 |
for (String removed : e.getValue()) { |
283 |
ap.put(toPropertyName(e.getKey(), removed), null, shared); |
284 |
} |
285 |
} |
286 |
} |
287 |
|
288 |
if (domModified) { |
289 |
ac.putConfigurationFragment(configRoot, true); |
290 |
} |
291 |
|
292 |
try { |
293 |
ProjectManager.getDefault().saveProject(project); |
294 |
} catch (IOException ex) { |
295 |
Exceptions.printStackTrace(ex); |
296 |
} |
297 |
|
298 |
path2Data.clear(); |
299 |
path2Removed.clear(); |
300 |
removedNodes.clear(); |
301 |
createdNodes.clear(); |
302 |
modified = false; |
303 |
} |
304 |
|
305 |
synchronized void sync() { |
306 |
loadConfigRoot(); |
307 |
flush(); |
308 |
} |
309 |
|
310 |
private void markModified() { |
311 |
autoFlushTask.cancel(); |
312 |
autoFlushTask.schedule(AUTOFLUSH_TIMEOUT); |
313 |
modified = true; |
314 |
} |
315 |
|
316 |
private static String findCNBForClass(Class cls) { |
317 |
String absolutePath = null; |
318 |
ClassLoader cl = cls.getClassLoader(); |
319 |
for (ModuleInfo module : Lookup.getDefault().lookupAll(ModuleInfo.class)) { |
320 |
if (module.isEnabled() && module.getClassLoader() == cl) { |
321 |
absolutePath = module.getCodeNameBase(); |
322 |
break; |
323 |
} |
324 |
} |
325 |
if (absolutePath == null) { |
326 |
absolutePath = cls.getName().replaceFirst("(^|\\.)[^.]+$", "");//NOI18N |
327 |
} |
328 |
assert absolutePath != null; |
329 |
return absolutePath.replace('.', '-'); |
330 |
} |
331 |
|
332 |
public synchronized Preferences findModule(String moduleName) { |
333 |
Reference<AuxiliaryConfigBasedPreferences> prefRef = module2Preferences.get(moduleName); |
334 |
AuxiliaryConfigBasedPreferences pref = prefRef != null ? prefRef.get() : null; |
335 |
|
336 |
if (pref == null) { |
337 |
module2Preferences.put(moduleName, new WeakReference<AuxiliaryConfigBasedPreferences>(pref = new AuxiliaryConfigBasedPreferences(null, "", moduleName))); |
338 |
} |
339 |
|
340 |
return pref; |
341 |
} |
342 |
|
343 |
private Element findRelative(String path, boolean createIfMissing) { |
344 |
if (ac == null) { |
345 |
return null; |
346 |
} |
347 |
|
348 |
String[] sep = path.split("/"); |
349 |
|
350 |
assert sep.length > 0; |
351 |
|
352 |
Element e = find(configRoot, sep[0], EL_MODULE, createIfMissing); |
353 |
|
354 |
for (int cntr = 1; cntr < sep.length && e != null; cntr++) { |
355 |
e = find(e, sep[cntr], EL_NODE, createIfMissing); |
356 |
} |
357 |
|
358 |
return e; |
359 |
} |
360 |
|
361 |
private Map<String, String> getData(String path) { |
362 |
Map<String, String> data = path2Data.get(path); |
363 |
|
364 |
if (data == null) { |
365 |
path2Data.put(path, data = new HashMap<String, String>()); |
366 |
} |
367 |
|
368 |
return data; |
369 |
} |
370 |
|
371 |
private Set<String> getRemoved(String path) { |
372 |
Set<String> removed = path2Removed.get(path); |
373 |
|
374 |
if (removed == null) { |
375 |
path2Removed.put(path, removed = new HashSet<String>()); |
376 |
} |
377 |
|
378 |
return removed; |
379 |
} |
380 |
|
381 |
private void removeNode(String path) { |
382 |
path2Data.remove(path); |
383 |
path2Removed.remove(path); |
384 |
createdNodes.remove(path); |
385 |
removedNodes.add(path); |
386 |
} |
387 |
|
388 |
private boolean isRemovedNode(String path) { |
389 |
return removedNodes.contains(path); |
390 |
} |
391 |
|
392 |
private static Element find(Element dom, String key, String elementName, boolean createIfMissing) { |
393 |
NodeList nl = dom.getChildNodes(); |
394 |
|
395 |
for (int cntr = 0; cntr < nl.getLength(); cntr++) { |
396 |
Node n = nl.item(cntr); |
397 |
|
398 |
if (n.getNodeType() == Node.ELEMENT_NODE && NAMESPACE.equals(n.getNamespaceURI()) && elementName.equals(n.getLocalName())) { |
399 |
if (key.equals(((Element) n).getAttribute(ATTR_NAME))) { |
400 |
return (Element) n; |
401 |
} |
402 |
} |
403 |
} |
404 |
|
405 |
if (!createIfMissing) { |
406 |
return null; |
407 |
} |
408 |
|
409 |
Element el = dom.getOwnerDocument().createElementNS(NAMESPACE, elementName); |
410 |
|
411 |
el.setAttribute(ATTR_NAME, key); |
412 |
|
413 |
dom.appendChild(el); |
414 |
|
415 |
return el; |
416 |
} |
417 |
|
418 |
private String toPropertyName(String path, String propertyName) { |
419 |
return encodeString(path).replace('/', '.') + '.' + encodeString(propertyName); |
420 |
} |
421 |
|
422 |
private class AuxiliaryConfigBasedPreferences extends AbstractPreferences { |
423 |
|
424 |
private final String path; |
425 |
|
426 |
public AuxiliaryConfigBasedPreferences(AbstractPreferences parent, String name, String path) { |
427 |
super(parent, name); |
428 |
this.path = path; |
429 |
} |
430 |
|
431 |
@Override |
432 |
protected void putSpi(String key, String value) { |
433 |
synchronized (AuxiliaryConfigBasedPreferencesProvider.this) { |
434 |
getData(path).put(key, value); |
435 |
getRemoved(path).remove(key); |
436 |
|
437 |
markModified(); |
438 |
} |
439 |
} |
440 |
|
441 |
@Override |
442 |
protected String getSpi(String key) { |
443 |
synchronized (AuxiliaryConfigBasedPreferencesProvider.this) { |
444 |
if (getRemoved(path).contains(key)) { |
445 |
return null; |
446 |
} |
447 |
|
448 |
if (getData(path).containsKey(key)) { |
449 |
return getData(path).get(key); |
450 |
} |
451 |
|
452 |
if (isRemovedNode(path)) { |
453 |
return null; |
454 |
} |
455 |
|
456 |
if (ap != null ) { |
457 |
String keyProp = toPropertyName(path, key); |
458 |
String res = AuxiliaryConfigBasedPreferencesProvider.this.ap.get(keyProp, shared); |
459 |
|
460 |
if (res != null) { |
461 |
return res; |
462 |
} |
463 |
} |
464 |
Element p = findRelative(path, false); |
465 |
|
466 |
p = p != null ? AuxiliaryConfigBasedPreferencesProvider.find(p, key, EL_PROPERTY, false) : null; |
467 |
|
468 |
if (p == null) { |
469 |
return null; |
470 |
} |
471 |
|
472 |
return p.getAttribute(ATTR_VALUE); |
473 |
} |
474 |
} |
475 |
|
476 |
@Override |
477 |
protected void removeSpi(String key) { |
478 |
synchronized (AuxiliaryConfigBasedPreferencesProvider.this) { |
479 |
getData(path).remove(key); |
480 |
getRemoved(path).add(key); |
481 |
|
482 |
markModified(); |
483 |
} |
484 |
} |
485 |
|
486 |
@Override |
487 |
protected void removeNodeSpi() throws BackingStoreException { |
488 |
synchronized (AuxiliaryConfigBasedPreferencesProvider.this) { |
489 |
AuxiliaryConfigBasedPreferencesProvider.this.removeNode(path); |
490 |
markModified(); |
491 |
} |
492 |
} |
493 |
|
494 |
@Override |
495 |
protected String[] keysSpi() throws BackingStoreException { |
496 |
synchronized (AuxiliaryConfigBasedPreferencesProvider.this) { |
497 |
Collection<String> result = new LinkedHashSet<String>(); |
498 |
|
499 |
if (!isRemovedNode(path)) { |
500 |
result.addAll(list(EL_PROPERTY)); |
501 |
} |
502 |
|
503 |
if (ap != null) { |
504 |
String prefix = toPropertyName(path, ""); |
505 |
|
506 |
for (String key : ap.listKeys(shared)) { |
507 |
if (key.startsWith(prefix)) { |
508 |
String name = key.substring(prefix.length()); |
509 |
|
510 |
if (name.length() > 0 && name.indexOf('.') == (-1)) { |
511 |
result.add(decodeString(name)); |
512 |
} |
513 |
} |
514 |
} |
515 |
} |
516 |
|
517 |
result.addAll(getData(path).keySet()); |
518 |
result.removeAll(getRemoved(path)); |
519 |
|
520 |
return result.toArray(new String[0]); |
521 |
} |
522 |
} |
523 |
|
524 |
@Override |
525 |
protected String[] childrenNamesSpi() throws BackingStoreException { |
526 |
synchronized (AuxiliaryConfigBasedPreferencesProvider.this) { |
527 |
return getChildrenNames().toArray(new String[0]); |
528 |
} |
529 |
} |
530 |
|
531 |
@Override |
532 |
protected AbstractPreferences childSpi(String name) { |
533 |
synchronized (AuxiliaryConfigBasedPreferencesProvider.this) { |
534 |
String nuePath = path + "/" + name; |
535 |
if (!getChildrenNames().contains(name)) { |
536 |
AuxiliaryConfigBasedPreferencesProvider.this.createdNodes.add(nuePath); |
537 |
} |
538 |
|
539 |
return new AuxiliaryConfigBasedPreferences(this, name, nuePath); |
540 |
} |
541 |
} |
542 |
|
543 |
@Override |
544 |
public void sync() throws BackingStoreException { |
545 |
AuxiliaryConfigBasedPreferencesProvider.this.sync(); |
546 |
} |
547 |
|
548 |
@Override |
549 |
protected void syncSpi() throws BackingStoreException { |
550 |
throw new UnsupportedOperationException("Should never be called."); |
551 |
} |
552 |
|
553 |
@Override |
554 |
public void flush() throws BackingStoreException { |
555 |
AuxiliaryConfigBasedPreferencesProvider.this.flush(); |
556 |
} |
557 |
|
558 |
@Override |
559 |
protected void flushSpi() throws BackingStoreException { |
560 |
throw new UnsupportedOperationException("Should never be called."); |
561 |
} |
562 |
|
563 |
private Collection<String> getChildrenNames() { |
564 |
Collection<String> result = new LinkedHashSet<String>(); |
565 |
|
566 |
if (!isRemovedNode(path)) { |
567 |
result.addAll(list(EL_NODE)); |
568 |
} |
569 |
|
570 |
for (String removed : removedNodes) { |
571 |
int slash = removed.lastIndexOf('/'); |
572 |
|
573 |
if (path.equals(removed.substring(slash))) { |
574 |
result.remove(removed.substring(slash + 1)); |
575 |
} |
576 |
} |
577 |
|
578 |
if (ap != null) { |
579 |
String prefix = toPropertyName(path, ""); |
580 |
|
581 |
for (String key : ap.listKeys(shared)) { |
582 |
if (key.startsWith(prefix)) { |
583 |
String name = key.substring(prefix.length()); |
584 |
|
585 |
if (name.length() > 0 && name.indexOf('.') != (-1)) { |
586 |
name = name.substring(0, name.indexOf('.')); |
587 |
result.add(decodeString(name)); |
588 |
} |
589 |
} |
590 |
} |
591 |
} |
592 |
|
593 |
for (String created : createdNodes) { |
594 |
int slash = created.lastIndexOf('/'); |
595 |
|
596 |
if (path.equals(created.substring(slash))) { |
597 |
result.add(created.substring(slash + 1)); |
598 |
} |
599 |
} |
600 |
|
601 |
return result; |
602 |
} |
603 |
|
604 |
private Collection<String> list(String elementName) throws DOMException { |
605 |
Element dom = findRelative(path, false); |
606 |
|
607 |
if (dom == null) { |
608 |
return Collections.emptyList(); |
609 |
} |
610 |
|
611 |
List<String> names = new LinkedList<String>(); |
612 |
NodeList nl = dom.getElementsByTagNameNS(NAMESPACE, elementName); |
613 |
|
614 |
for (int cntr = 0; cntr < nl.getLength(); cntr++) { |
615 |
Node n = nl.item(cntr); |
616 |
|
617 |
names.add(((Element) n).getAttribute(ATTR_NAME)); |
618 |
} |
619 |
|
620 |
return names; |
621 |
} |
622 |
|
623 |
} |
624 |
|
625 |
private static final class FallbackAuxiliaryPropertiesImpl implements AuxiliaryProperties { |
626 |
|
627 |
private static final String PREFIX = "auxiliary."; |
628 |
private FileObject projectDir; |
629 |
|
630 |
public FallbackAuxiliaryPropertiesImpl(FileObject projectDir) { |
631 |
this.projectDir = projectDir; |
632 |
} |
633 |
|
634 |
public String get(String key, boolean shared) { |
635 |
assert !shared; |
636 |
|
637 |
Object v = projectDir.getAttribute(PREFIX + key); |
638 |
|
639 |
return v instanceof String ? (String) v : null; |
640 |
} |
641 |
|
642 |
public void put(String key, String value, boolean shared) { |
643 |
assert !shared; |
644 |
|
645 |
try { |
646 |
projectDir.setAttribute(PREFIX + key, value); |
647 |
} catch (IOException ex) { |
648 |
Exceptions.printStackTrace(ex); |
649 |
} |
650 |
} |
651 |
|
652 |
public Iterable<String> listKeys(boolean shared) { |
653 |
assert !shared; |
654 |
|
655 |
List<String> result = new LinkedList<String>(); |
656 |
|
657 |
for (Enumeration<String> en = projectDir.getAttributes(); en.hasMoreElements(); ) { |
658 |
String key = en.nextElement(); |
659 |
|
660 |
if (key.startsWith(PREFIX)) { |
661 |
key = key.substring(PREFIX.length()); |
662 |
|
663 |
if (get(key, shared) != null) { |
664 |
result.add(key); |
665 |
} |
666 |
} |
667 |
} |
668 |
|
669 |
return result; |
670 |
} |
671 |
|
672 |
} |
673 |
|
674 |
} |