KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > ant > grammar > AntGrammar


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.ant.grammar;
21
22 import java.text.Collator JavaDoc;
23 import java.util.*;
24 import javax.swing.Icon JavaDoc;
25
26 import org.apache.tools.ant.module.api.IntrospectedInfo;
27
28 import org.w3c.dom.*;
29
30 import org.netbeans.modules.xml.api.model.*;
31 import org.netbeans.modules.xml.spi.dom.*;
32
33 /**
34  * Rather simple query implemetation based on static Ant introspection info.
35  * Hints given by this grammar cannot guarantee that valid XML document is created.
36  *
37  * @author Petr Kuzel, Jesse Glick
38  */

39 class AntGrammar implements GrammarQuery {
40
41     private static Enumeration empty;
42     private static Enumeration empty () {
43         if (empty == null) {
44             empty = Collections.enumeration (Collections.EMPTY_LIST);
45         }
46         return empty;
47     }
48     
49     /**
50      * Allow to get names of <b>parsed general entities</b>.
51      * @return list of <code>CompletionResult</code>s (ENTITY_REFERENCE_NODEs)
52      */

53     public Enumeration queryEntities(String JavaDoc prefix) {
54         ArrayList list = new ArrayList ();
55         
56         // add well-know build-in entity names
57

58         if ("lt".startsWith(prefix)) list.add(new MyEntityReference("lt"));
59         if ("gt".startsWith(prefix)) list.add(new MyEntityReference("gt"));
60         if ("apos".startsWith(prefix)) list.add(new MyEntityReference("apos"));
61         if ("quot".startsWith(prefix)) list.add(new MyEntityReference("quot"));
62         if ("amp".startsWith(prefix)) list.add(new MyEntityReference("amp"));
63         
64         return java.util.Collections.enumeration (list);
65     }
66
67     /*
68     private static String getTaskClassFor(String elementName) {
69         Map defs = getAntGrammar().getDefs("task");
70         return (String) defs.get(elementName);
71     }
72
73     private static String getTypeClassFor(String elementName) {
74         Map defs = getAntGrammar().getDefs("type");
75         return (String) defs.get(elementName);
76     }
77      */

78     
79     private static IntrospectedInfo getAntGrammar() {
80         return IntrospectedInfo.getKnownInfo();
81     }
82     
83     /** this element is a special thing, like the root project element */
84     static final String JavaDoc KIND_SPECIAL = "special"; // NOI18N
85
/** this element is a task */
86     static final String JavaDoc KIND_TASK = "task"; // NOI18N
87
/** this element is a data type */
88     static final String JavaDoc KIND_TYPE = "type"; // NOI18N
89
/** this element is part of some other structure (task or type) */
90     static final String JavaDoc KIND_DATA = "data"; // NOI18N
91
/** tag for root project element */
92     static final String JavaDoc SPECIAL_PROJECT = "project"; // NOI18N
93
/** tag for a target element */
94     static final String JavaDoc SPECIAL_TARGET = "target"; // NOI18N
95
/** tag for a project description element */
96     static final String JavaDoc SPECIAL_DESCRIPTION = "description"; // NOI18N
97
/** tag for an import statement */
98     static final String JavaDoc SPECIAL_IMPORT = "import"; // NOI18N
99

100     /**
101      * Determine what a particular element in a build script represents,
102      * based on its name and the names of all of its parents.
103      * Returns a pair of the kind of the element (one of the KIND_* constants)
104      * and the details (a class name suitable for {@link IntrospectedInfo}, or
105      * in the case of {@link KIND_SPECIAL}, one of the SPECIAL_* constants).
106      * @param e an element
107      * @return a two-element string (kind and details), or null if this element is anomalous
108      */

109     static final String JavaDoc[] typeOf(Element e) {
110         String JavaDoc name = e.getNodeName();
111         Node p = e.getParentNode();
112         if (p == null) {
113             throw new IllegalArgumentException JavaDoc("Detached node: " + e); // NOI18N
114
}
115         if (p.getNodeType() == Node.DOCUMENT_NODE) {
116             if (name.equals("project")) { // NOI18N
117
return new String JavaDoc[] {KIND_SPECIAL, SPECIAL_PROJECT};
118             } else {
119                 // Weird root element? Ignore.
120
return null;
121             }
122         } else if (p.getNodeType() == Node.ELEMENT_NODE) {
123             // Find ourselves in context.
124
String JavaDoc[] ptype = typeOf((Element)p);
125             if (ptype == null) {
126                 // Unknown parent, therefore this is unknown too.
127
return null;
128             }
129             if (ptype[0] == KIND_SPECIAL) {
130                 if (ptype[1] == SPECIAL_PROJECT) {
131                     // <project> may have <description>, or types, or targets, or tasks
132
if (name.equals("description")) { // NOI18N
133
return new String JavaDoc[] {KIND_SPECIAL, SPECIAL_DESCRIPTION};
134                     } else if (name.equals("target")) { // NOI18N
135
return new String JavaDoc[] {KIND_SPECIAL, SPECIAL_TARGET};
136                     } else if (name.equals("import")) { // NOI18N
137
return new String JavaDoc[] {KIND_SPECIAL, SPECIAL_IMPORT};
138                     } else {
139                         String JavaDoc taskClazz = (String JavaDoc)getAntGrammar().getDefs("task").get(name); // NOI18N
140
if (taskClazz != null) {
141                             return new String JavaDoc[] {KIND_TASK, taskClazz};
142                         } else {
143                             String JavaDoc typeClazz = (String JavaDoc)getAntGrammar().getDefs("type").get(name); // NOI18N
144
if (typeClazz != null) {
145                                 return new String JavaDoc[] {KIND_TYPE, typeClazz};
146                             } else {
147                                 return null;
148                             }
149                         }
150                     }
151                 } else if (ptype[1] == SPECIAL_TARGET) {
152                     // <target> may have tasks and types
153
String JavaDoc taskClazz = (String JavaDoc)getAntGrammar().getDefs("task").get(name); // NOI18N
154
if (taskClazz != null) {
155                         return new String JavaDoc[] {KIND_TASK, taskClazz};
156                     } else {
157                         String JavaDoc typeClazz = (String JavaDoc)getAntGrammar().getDefs("type").get(name); // NOI18N
158
if (typeClazz != null) {
159                             return new String JavaDoc[] {KIND_TYPE, typeClazz};
160                         } else {
161                             return null;
162                         }
163                     }
164                 } else if (ptype[1] == SPECIAL_DESCRIPTION) {
165                     // <description> should have no children!
166
return null;
167                 } else if (ptype[1] == SPECIAL_IMPORT) {
168                     // <import> should have no children!
169
return null;
170                 } else {
171                     throw new IllegalStateException JavaDoc(ptype[1]);
172                 }
173             } else {
174                 // We must be data.
175
String JavaDoc pclazz = ptype[1];
176                 String JavaDoc clazz = (String JavaDoc)getAntGrammar().getElements(pclazz).get(name);
177                 if (clazz != null) {
178                     return new String JavaDoc[] {KIND_DATA, clazz};
179                 } else {
180                     // Unknown data.
181
return null;
182                 }
183             }
184         } else {
185             throw new IllegalArgumentException JavaDoc("Bad parent for " + e.toString() + ": " + p); // NOI18N
186
}
187     }
188     
189     /**
190      * @stereotype query
191      * @output list of results that can be queried on name, and attributes
192      * @time Performs fast up to 300 ms.
193      * @param ctx represents virtual attribute <code>Node</code> to be replaced. Its parent is a element node.
194      * @return list of <code>CompletionResult</code>s (ATTRIBUTE_NODEs) that can be queried on name, and attributes.
195      * Every list member represents one possibility.
196      */

197     public Enumeration queryAttributes(HintContext ctx) {
198         
199         Element ownerElement = null;
200         // Support both versions of GrammarQuery contract
201
if (ctx.getNodeType() == Node.ATTRIBUTE_NODE) {
202             ownerElement = ((Attr)ctx).getOwnerElement();
203         } else if (ctx.getNodeType() == Node.ELEMENT_NODE) {
204             ownerElement = (Element) ctx;
205         }
206         if (ownerElement == null) return empty ();
207         
208         NamedNodeMap existingAttributes = ownerElement.getAttributes();
209         List possibleAttributes;
210         String JavaDoc[] typePair = typeOf(ownerElement);
211         if (typePair == null) {
212             return empty ();
213         }
214         String JavaDoc kind = typePair[0];
215         String JavaDoc clazz = typePair[1];
216         
217         if (kind == KIND_SPECIAL && clazz == SPECIAL_PROJECT) {
218             possibleAttributes = new LinkedList();
219             possibleAttributes.add("default");
220             possibleAttributes.add("name");
221             possibleAttributes.add("basedir");
222         } else if (kind == KIND_SPECIAL && clazz == SPECIAL_TARGET) {
223             possibleAttributes = new LinkedList();
224             possibleAttributes.add("name");
225             possibleAttributes.add("depends");
226             possibleAttributes.add("description");
227             possibleAttributes.add("if");
228             possibleAttributes.add("unless");
229         } else if (kind == KIND_SPECIAL && clazz == SPECIAL_DESCRIPTION) {
230             return empty ();
231         } else if (kind == KIND_SPECIAL && clazz == SPECIAL_IMPORT) {
232             possibleAttributes = new LinkedList();
233             possibleAttributes.add("file");
234             possibleAttributes.add("optional");
235         } else {
236             // task, type, or data; anyway, we have the defining class
237
possibleAttributes = new LinkedList();
238             if (kind == KIND_TYPE) {
239                 possibleAttributes.add("id");
240             }
241             if (getAntGrammar().isKnown(clazz)) {
242                 possibleAttributes.addAll(new TreeSet(getAntGrammar().getAttributes(clazz).keySet()));
243             }
244             if (kind == KIND_TASK) {
245                 // Can have an ID too, but less important; leave at end.
246
possibleAttributes.add("id");
247                 // Currently IntrospectedInfo includes this in the props for a type,
248
// though it excludes it for tasks. So for now add it explicitly
249
// only to tasks.
250
possibleAttributes.add("description");
251                 // Also useful sometimes:
252
possibleAttributes.add("taskname");
253             }
254         }
255         
256         String JavaDoc prefix = ctx.getCurrentPrefix();
257         
258         ArrayList list = new ArrayList ();
259         Iterator it = possibleAttributes.iterator();
260         while ( it.hasNext()) {
261             String JavaDoc next = (String JavaDoc) it.next();
262             if (next.startsWith(prefix)) {
263                 if (existingAttributes.getNamedItem(next) == null) {
264                     list.add(new MyAttr(next));
265                 }
266             }
267         }
268         
269         return Collections.enumeration (list);
270     }
271     
272     /**
273      * @semantics Navigates through read-only Node tree to determine context and provide right results.
274      * @postconditions Let ctx unchanged
275      * @time Performs fast up to 300 ms.
276      * @stereotype query
277      * @param ctx represents virtual element Node that has to be replaced, its own attributes does not name sense, it can be used just as the navigation start point.
278      * @return list of <code>CompletionResult</code>s (ELEMENT_NODEs) that can be queried on name, and attributes
279      * Every list member represents one possibility.
280      */

281     public Enumeration queryElements(HintContext ctx) {
282         
283         Node parent = ((Node)ctx).getParentNode();
284         if (parent == null) return empty ();
285         if (parent.getNodeType() != Node.ELEMENT_NODE) {
286             return empty ();
287         }
288         
289         List elements;
290         String JavaDoc[] typePair = typeOf((Element)parent);
291         if (typePair == null) {
292             return empty ();
293         }
294         String JavaDoc kind = typePair[0];
295         String JavaDoc clazz = typePair[1];
296         
297         if (kind == KIND_SPECIAL && clazz == SPECIAL_PROJECT) {
298             elements = new LinkedList();
299             elements.add("target");
300             elements.add("import");
301             elements.add("property");
302             elements.add("description");
303             SortedSet/*<String>*/ tasks = getSortedDefs("task");
304             tasks.remove("property");
305             tasks.remove("import");
306             elements.addAll(tasks); // Ant 1.6 permits any tasks at top level
307
elements.addAll(getSortedDefs("type"));
308         } else if (kind == KIND_SPECIAL && clazz == SPECIAL_TARGET) {
309             elements = new ArrayList(getSortedDefs("task"));
310             // targets can have embedded types too, though less common:
311
elements.addAll(getSortedDefs("type")); // NOI18N
312
} else if (kind == KIND_SPECIAL && clazz == SPECIAL_DESCRIPTION) {
313             return empty ();
314         } else if (kind == KIND_SPECIAL && clazz == SPECIAL_IMPORT) {
315             return empty ();
316         } else {
317             // some introspectable class
318
if (getAntGrammar().isKnown(clazz)) {
319                 elements = new ArrayList(new TreeSet(getAntGrammar().getElements(clazz).keySet()));
320             } else {
321                 elements = Collections.EMPTY_LIST;
322             }
323         }
324                 
325         String JavaDoc prefix = ctx.getCurrentPrefix();
326         
327         ArrayList list = new ArrayList ();
328         Iterator it = elements.iterator();
329         while ( it.hasNext()) {
330             String JavaDoc next = (String JavaDoc) it.next();
331             if (next.startsWith(prefix)) {
332                 list.add (new MyElement(next));
333             }
334         }
335         
336         return Collections.enumeration (list);
337     }
338     
339     private static SortedSet/*<String>*/ getSortedDefs(String JavaDoc kind) {
340         SortedSet/*<String>*/ defs = new TreeSet(Collator.getInstance());
341         defs.addAll(getAntGrammar().getDefs(kind).keySet());
342         return defs;
343     }
344     
345     /**
346      * Allow to get names of <b>declared notations</b>.
347      * @return list of <code>CompletionResult</code>s (NOTATION_NODEs)
348      */

349     public Enumeration queryNotations(String JavaDoc prefix) {
350         return empty ();
351     }
352     
353     public Enumeration queryValues(HintContext ctx) {
354         // #38341: ctx is apparently instanceof Attr or Text
355
// (actually never instanceof Text, just TEXT_NODE: #38339)
356
Attr ownerAttr;
357         if (canCompleteProperty(ctx.getCurrentPrefix())) {
358             return completeProperties(ctx);
359         } else if (ctx.getNodeType() == Node.ATTRIBUTE_NODE) {
360             ownerAttr = (Attr)ctx;
361         } else {
362             return empty ();
363         }
364         Element ownerElement = ownerAttr.getOwnerElement();
365         String JavaDoc attrName = ownerAttr.getName();
366         String JavaDoc[] typePair = typeOf(ownerElement);
367         if (typePair == null) {
368             return empty ();
369         }
370         List/*<String>*/ choices = new ArrayList();
371         
372         if (typePair[0].equals(KIND_SPECIAL)) {
373             if (typePair[1].equals(SPECIAL_PROJECT)) {
374                 if (attrName.equals("default")) {
375                     // XXX list known targets?
376
} else if (attrName.equals("basedir")) {
377                     // XXX file completion?
378
}
379                 // freeform: name
380
} else if (typePair[1].equals(SPECIAL_TARGET)) {
381                 if (attrName.equals("depends")) {
382                     // XXX list known targets?
383
} else if (attrName.equals("if") || attrName.equals("unless")) {
384                     choices.addAll(Arrays.asList(likelyPropertyNames(ctx)));
385                 }
386                 // freeform: description
387
} else if (typePair[1].equals(SPECIAL_DESCRIPTION)) {
388                 // nothing applicable
389
} else if (typePair[1].equals(SPECIAL_IMPORT)) {
390                 if (attrName.equals("file")) {
391                     // freeform
392
} else if (attrName.equals("optional")) {
393                     choices.add("true");
394                     choices.add("false");
395                 }
396             } else {
397                 assert false : typePair[1];
398             }
399         } else {
400             String JavaDoc elementClazz = typePair[1];
401             if (getAntGrammar().isKnown(elementClazz)) {
402                 String JavaDoc attrClazzName = (String JavaDoc)getAntGrammar().getAttributes(elementClazz).get(attrName);
403                 if (attrClazzName != null) {
404                     if (getAntGrammar().isKnown(attrClazzName)) {
405                         String JavaDoc[] enumTags = getAntGrammar().getTags(attrClazzName);
406                         if (enumTags != null) {
407                             choices.addAll(Arrays.asList(enumTags));
408                         }
409                     }
410                     if (attrClazzName.equals("boolean")) {
411                         choices.add("true");
412                         choices.add("false");
413                     } else if (attrClazzName.equals("org.apache.tools.ant.types.Reference")) {
414                         // XXX add names of ids
415
} else if (attrClazzName.equals("org.apache.tools.ant.types.Path") ||
416                                attrClazzName.equals("java.io.File")
417                                /* || "path" attr on Path or Path.Element */
418                               ) {
419                         // XXX complete filenames
420
} else if (attrClazzName.equals("java.lang.String") &&
421                                Arrays.asList(PROPERTY_NAME_VALUED_PROPERTY_NAMES).contains(attrName)) {
422                         // <isset property="..."/>, <include name="*" unless="..."/>, etc.
423
choices.addAll(Arrays.asList(likelyPropertyNames(ctx)));
424                     }
425                 }
426             }
427         }
428         
429         // Create the completion:
430
String JavaDoc prefix = ctx.getCurrentPrefix();
431         ArrayList list = new ArrayList ();
432         Iterator it = choices.iterator();
433         while (it.hasNext()) {
434             String JavaDoc next = (String JavaDoc)it.next();
435             if (next.startsWith(prefix)) {
436                 list.add (new MyText(next));
437             }
438         }
439         return Collections.enumeration (list);
440     }
441     
442     /**
443      * Check whether a given content string (of an attribute value or of an element's
444      * content) has an uncompleted "${" sequence in it, i.e. one that has not been matched
445      * with a corresponding "}".
446      * E.g.:
447      * <pathelement location="${foo
448      * ^ caret
449      * Also if the last character is "$" it can be completed.
450      * @param content the current content of the attribute value or element
451      * @return true if there is an uncompleted property here
452      */

453     private static boolean canCompleteProperty(String JavaDoc content) {
454         content = deletedEscapedShells(content);
455         if (content.length() == 0) {
456             return false;
457         }
458         if (content.charAt(content.length() - 1) == '$') {
459             return true;
460         }
461         int idx = content.lastIndexOf("${");
462         return idx != -1 && content.indexOf('}', idx) == -1;
463     }
464     
465     private static Enumeration completeProperties(HintContext ctx) {
466         String JavaDoc content = ctx.getCurrentPrefix();
467         assert content.length() > 0;
468         String JavaDoc header;
469         String JavaDoc propPrefix;
470         if (content.charAt(content.length() - 1) == '$') {
471             header = content + '{';
472             propPrefix = "";
473         } else {
474             int idx = content.lastIndexOf("${");
475             assert idx != -1;
476             header = content.substring(0, idx + 2);
477             propPrefix = content.substring(idx + 2);
478         }
479         String JavaDoc[] props = likelyPropertyNames(ctx);
480         // completion on text works differently from attrs:
481
// the context should not be returned (#38342)
482
boolean shortHeader = ctx.getNodeType() == Node.TEXT_NODE;
483         ArrayList list = new ArrayList ();
484         for (int i = 0; i < props.length; i++) {
485             if (props[i].startsWith(propPrefix)) {
486                 String JavaDoc text = header + props[i] + '}';;
487                 if (shortHeader) {
488                     assert text.startsWith(content) : "text=" + text + " content=" + content;
489                     text = text.substring(content.length());
490                 }
491                 list.add (new MyText(text));
492             }
493         }
494         return Collections.enumeration (list);
495     }
496     
497     /**
498      * Names of Ant properties that are generally present and defined in any script.
499      */

500     private static final String JavaDoc[] STOCK_PROPERTY_NAMES = {
501         // Present in most Ant installations:
502
"ant.home", // NOI18N
503
// Defined by Ant as standard properties:
504
"basedir", // NOI18N
505
"ant.file", // NOI18N
506
"ant.project.name", // NOI18N
507
"ant.java.version", // NOI18N
508
"ant.version", // NOI18N
509
// Defined by System.getProperties as standard system properties:
510
"java.version", // NOI18N
511
"java.vendor", // NOI18N
512
"java.vendor.url", // NOI18N
513
"java.home", // NOI18N
514
"java.vm.specification.version", // NOI18N
515
"java.vm.specification.vendor", // NOI18N
516
"java.vm.specification.name", // NOI18N
517
"java.vm.version", // NOI18N
518
"java.vm.vendor", // NOI18N
519
"java.vm.name", // NOI18N
520
"java.specification.version", // NOI18N
521
"java.specification.vendor", // NOI18N
522
"java.specification.name", // NOI18N
523
"java.class.version", // NOI18N
524
"java.class.path", // NOI18N
525
"java.library.path", // NOI18N
526
"java.io.tmpdir", // NOI18N
527
"java.compiler", // NOI18N
528
"java.ext.dirs", // NOI18N
529
"os.name", // NOI18N
530
"os.arch", // NOI18N
531
"os.version", // NOI18N
532
"file.separator", // NOI18N
533
"path.separator", // NOI18N
534
"line.separator", // NOI18N
535
"user.name", // NOI18N
536
"user.home", // NOI18N
537
"user.dir", // NOI18N
538
};
539     
540     private static String JavaDoc[] likelyPropertyNames(HintContext ctx) {
541         // #38343: ctx.getOwnerDocument returns some bogus unusable empty thing
542
// so find the root element manually
543
Element parent;
544         // #38341: docs for queryValues says Attr or Element, but really Attr or Text
545
// (and CDataSection never seems to permit completion at all...)
546
if (ctx.getNodeType() == Node.ATTRIBUTE_NODE) {
547             parent = ((Attr)ctx).getOwnerElement();
548         } else if (ctx.getNodeType() == Node.TEXT_NODE) {
549             Node p = ctx.getParentNode();
550             if (p != null && p.getNodeType() == Node.ELEMENT_NODE) {
551                 parent = (Element)p;
552             } else {
553                 System.err.println("strange parent of text node: " + p.getNodeType() + " " + p);
554                 return new String JavaDoc[0];
555             }
556         } else {
557             System.err.println("strange context type: " + ctx.getNodeType() + " " + ctx);
558             return new String JavaDoc[0];
559         }
560         while (parent.getParentNode() != null && parent.getParentNode().getNodeType() == Node.ELEMENT_NODE) {
561             parent = (Element)parent.getParentNode();
562         }
563         // #38343: getElementsByTagName just throws an exception, you can't use it...
564
Set/*<String>*/ choices = new TreeSet(Arrays.asList(STOCK_PROPERTY_NAMES));
565         visitForLikelyPropertyNames(parent, choices);
566         Iterator it = choices.iterator();
567         while (it.hasNext()) {
568             String JavaDoc propname = (String JavaDoc)it.next();
569             if (propname.indexOf("${") != -1) {
570                 // Not actually a direct property name, rather a computed name.
571
// Skip it as it cannot be used here.
572
it.remove();
573             }
574         }
575         return (String JavaDoc[])choices.toArray(new String JavaDoc[choices.size()]);
576     }
577     
578     private static final String JavaDoc[] PROPERTY_NAME_VALUED_PROPERTY_NAMES = {
579         "if",
580         "unless",
581         // XXX accept any *property
582
"property",
583         "failureproperty",
584         "errorproperty",
585         "addproperty",
586     };
587     
588     private static void visitForLikelyPropertyNames(Node n, Set/*<String>*/ choices) {
589         int type = n.getNodeType();
590         switch (type) {
591             case Node.ELEMENT_NODE:
592                 // XXX would be more precise to use typeOf here, but maybe slower?
593
// Look for <property name="propname" .../> and similar
594
Element el = (Element)n;
595                 String JavaDoc tagname = el.getTagName();
596                 if (tagname.equals("property")) {
597                     String JavaDoc propname = el.getAttribute("name");
598                     // #38343: Element impl is broken and can return null from getAttribute
599
if (propname != null && propname.length() > 0) {
600                         choices.add(propname);
601                     }
602                     // XXX handle <property file="..."/> with a resolvable filename
603
} else if (tagname.equals("buildnumber")) {
604                     // This task always defines ${build.number}
605
choices.add("build.number");
606                 } else if (tagname.equals("tstamp")) {
607                     // XXX handle prefix="whatever" -> ${whatever.TODAY} etc.
608
// XXX handle nested <format property="foo" .../> -> ${foo}
609
choices.add("DSTAMP");
610                     choices.add("TSTAMP");
611                     choices.add("TODAY");
612                 }
613                 // <available>, <dirname>, <pathconvert>, <uptodate>, <target>, <isset>, <include>, etc.
614
for (int i = 0; i < PROPERTY_NAME_VALUED_PROPERTY_NAMES.length; i++) {
615                     String JavaDoc propname = el.getAttribute(PROPERTY_NAME_VALUED_PROPERTY_NAMES[i]);
616                     if (propname != null && propname.length() > 0) {
617                         choices.add(propname);
618                     }
619                 }
620                 break;
621             case Node.ATTRIBUTE_NODE:
622             case Node.TEXT_NODE:
623                 // Look for ${propname}
624
String JavaDoc text = deletedEscapedShells(n.getNodeValue());
625                 int idx = 0;
626                 while (true) {
627                     int start = text.indexOf("${", idx);
628                     if (start == -1) {
629                         break;
630                     }
631                     int end = text.indexOf('}', start + 2);
632                     if (end == -1) {
633                         break;
634                     }
635                     String JavaDoc propname = text.substring(start + 2, end);
636                     if (propname.length() > 0) {
637                         choices.add(propname);
638                     }
639                     idx = end + 1;
640                 }
641                 break;
642             default:
643                 // ignore
644
break;
645         }
646         NodeList l = n.getChildNodes();
647         for (int i = 0; i < l.getLength(); i++) {
648             visitForLikelyPropertyNames(l.item(i), choices);
649         }
650         // Element attributes are not considered child nodes as such.
651
NamedNodeMap m = n.getAttributes();
652         if (m != null) {
653             for (int i = 0; i < m.getLength(); i++) {
654                 visitForLikelyPropertyNames(m.item(i), choices);
655             }
656         }
657     }
658     
659     /**
660      * Remove pairs of '$$' to avoid being confused by them.
661      * They do not introduce property references.
662      */

663     private static String JavaDoc deletedEscapedShells(String JavaDoc text) {
664         // XXX could be faster w/o regexps
665
return text.replaceAll("\\$\\$", "");
666     }
667
668     // return defaults, no way to query them
669
public GrammarResult queryDefault(final HintContext ctx) {
670         return null;
671     }
672     
673     // it is not yet implemented
674
public boolean isAllowed(Enumeration en) {
675         return true;
676     }
677     
678     // customizers section ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
679

680     public java.awt.Component JavaDoc getCustomizer(HintContext ctx) {
681         return null;
682     }
683     
684     public boolean hasCustomizer(HintContext ctx) {
685         return false;
686     }
687
688     public org.openide.nodes.Node.Property[] getProperties(HintContext ctx) {
689         return null;
690     }
691     
692
693     // Result classes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
694

695     private static abstract class AbstractResultNode extends AbstractNode implements GrammarResult {
696         
697         public Icon JavaDoc getIcon(int kind) {
698             return null;
699         }
700         
701         public String JavaDoc getDescription() {
702             return null;
703         }
704         
705         public String JavaDoc getDisplayName() {
706             return null;
707         }
708        
709         // TODO in MyElement return true for really empty elements such as "pathelement"
710
public boolean isEmptyElement() {
711             return false;
712         }
713     }
714     
715     private static class MyEntityReference extends AbstractResultNode implements EntityReference {
716         
717         private String JavaDoc name;
718         
719         MyEntityReference(String JavaDoc name) {
720             this.name = name;
721         }
722         
723         public short getNodeType() {
724             return Node.ENTITY_REFERENCE_NODE;
725         }
726         
727         public String JavaDoc getNodeName() {
728             return name;
729         }
730                 
731     }
732     
733     private static class MyElement extends AbstractResultNode implements Element {
734         
735         private String JavaDoc name;
736         
737         MyElement(String JavaDoc name) {
738             this.name = name;
739         }
740         
741         public short getNodeType() {
742             return Node.ELEMENT_NODE;
743         }
744         
745         public String JavaDoc getNodeName() {
746             return name;
747         }
748         
749         public String JavaDoc getTagName() {
750             return name;
751         }
752         
753     }
754
755     private static class MyAttr extends AbstractResultNode implements Attr {
756         
757         private String JavaDoc name;
758         
759         MyAttr(String JavaDoc name) {
760             this.name = name;
761         }
762         
763         public short getNodeType() {
764             return Node.ATTRIBUTE_NODE;
765         }
766         
767         public String JavaDoc getNodeName() {
768             return name;
769         }
770         
771         public String JavaDoc getName() {
772             return name;
773         }
774
775         public String JavaDoc getValue() {
776             return null; //??? what spec says
777
}
778         
779         
780     }
781
782     private static class MyText extends AbstractResultNode implements Text {
783         
784         private String JavaDoc data;
785         
786         MyText(String JavaDoc data) {
787             this.data = data;
788         }
789         
790         public short getNodeType() {
791             return Node.TEXT_NODE;
792         }
793
794         public String JavaDoc getNodeValue() {
795             return data;
796         }
797         
798         public String JavaDoc getData() throws DOMException {
799             return data;
800         }
801
802         public int getLength() {
803             return data == null ? -1 : data.length();
804         }
805     }
806         
807 }
808
Popular Tags