Source Code Cross Referenced for AntGrammar.java in  » IDE-Netbeans » ant » org » netbeans » modules » ant » grammar » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » IDE Netbeans » ant » org.netbeans.modules.ant.grammar 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003:         *
004:         * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005:         *
006:         * The contents of this file are subject to the terms of either the GNU
007:         * General Public License Version 2 only ("GPL") or the Common
008:         * Development and Distribution License("CDDL") (collectively, the
009:         * "License"). You may not use this file except in compliance with the
010:         * License. You can obtain a copy of the License at
011:         * http://www.netbeans.org/cddl-gplv2.html
012:         * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013:         * specific language governing permissions and limitations under the
014:         * License.  When distributing the software, include this License Header
015:         * Notice in each file and include the License file at
016:         * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
017:         * particular file as subject to the "Classpath" exception as provided
018:         * by Sun in the GPL Version 2 section of the License file that
019:         * accompanied this code. If applicable, add the following below the
020:         * License Header, with the fields enclosed by brackets [] replaced by
021:         * your own identifying information:
022:         * "Portions Copyrighted [year] [name of copyright owner]"
023:         *
024:         * Contributor(s):
025:         *
026:         * The Original Software is NetBeans. The Initial Developer of the Original
027:         * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028:         * Microsystems, Inc. All Rights Reserved.
029:         *
030:         * If you wish your version of this file to be governed by only the CDDL
031:         * or only the GPL Version 2, indicate your decision by adding
032:         * "[Contributor] elects to include this software in this distribution
033:         * under the [CDDL or GPL Version 2] license." If you do not indicate a
034:         * single choice of license, a recipient has the option to distribute
035:         * your version of this file under either the CDDL, the GPL Version 2 or
036:         * to extend the choice of license to its licensees as provided above.
037:         * However, if you add GPL Version 2 code and therefore, elected the GPL
038:         * Version 2 license, then the option applies only if the new code is
039:         * made subject to such option by the copyright holder.
040:         */
041:
042:        package org.netbeans.modules.ant.grammar;
043:
044:        import java.text.Collator;
045:        import java.util.ArrayList;
046:        import java.util.Arrays;
047:        import java.util.Collections;
048:        import java.util.Enumeration;
049:        import java.util.Iterator;
050:        import java.util.LinkedList;
051:        import java.util.List;
052:        import java.util.Set;
053:        import java.util.SortedSet;
054:        import java.util.TreeSet;
055:        import java.util.logging.Level;
056:        import java.util.logging.Logger;
057:        import javax.swing.Icon;
058:        import org.apache.tools.ant.module.api.IntrospectedInfo;
059:        import org.netbeans.modules.xml.api.model.GrammarQuery;
060:        import org.netbeans.modules.xml.api.model.GrammarResult;
061:        import org.netbeans.modules.xml.api.model.HintContext;
062:        import org.netbeans.modules.xml.spi.dom.AbstractNode;
063:        import org.openide.util.Enumerations;
064:        import org.w3c.dom.Attr;
065:        import org.w3c.dom.DOMException;
066:        import org.w3c.dom.Element;
067:        import org.w3c.dom.EntityReference;
068:        import org.w3c.dom.NamedNodeMap;
069:        import org.w3c.dom.Node;
070:        import org.w3c.dom.NodeList;
071:        import org.w3c.dom.Text;
072:
073:        /**
074:         * Rather simple query implemetation based on static Ant introspection info.
075:         * Hints given by this grammar cannot guarantee that valid XML document is created.
076:         *
077:         * @author Petr Kuzel, Jesse Glick
078:         */
079:        class AntGrammar implements  GrammarQuery {
080:
081:            private static final Logger LOG = Logger.getLogger(AntGrammar.class
082:                    .getName());
083:
084:            /**
085:             * Allow to get names of <b>parsed general entities</b>.
086:             * @return list of <code>CompletionResult</code>s (ENTITY_REFERENCE_NODEs)
087:             */
088:            public Enumeration<GrammarResult> queryEntities(String prefix) {
089:                List<GrammarResult> list = new ArrayList<GrammarResult>();
090:
091:                // add well-know build-in entity names
092:
093:                if ("lt".startsWith(prefix))
094:                    list.add(new MyEntityReference("lt"));
095:                if ("gt".startsWith(prefix))
096:                    list.add(new MyEntityReference("gt"));
097:                if ("apos".startsWith(prefix))
098:                    list.add(new MyEntityReference("apos"));
099:                if ("quot".startsWith(prefix))
100:                    list.add(new MyEntityReference("quot"));
101:                if ("amp".startsWith(prefix))
102:                    list.add(new MyEntityReference("amp"));
103:
104:                LOG.log(Level.FINE, "queryEntities({0}) -> {1}", new Object[] {
105:                        prefix, list });
106:                return Collections.enumeration(list);
107:            }
108:
109:            /*
110:            private static String getTaskClassFor(String elementName) {
111:                Map defs = getAntGrammar().getDefs("task");
112:                return (String) defs.get(elementName);
113:            }
114:
115:            private static String getTypeClassFor(String elementName) {
116:                Map defs = getAntGrammar().getDefs("type");
117:                return (String) defs.get(elementName);
118:            }
119:             */
120:
121:            private static IntrospectedInfo getAntGrammar() {
122:                return IntrospectedInfo.getKnownInfo();
123:            }
124:
125:            enum Kind {
126:                /** this element is a task */
127:                TASK,
128:                /** this element is a data type */
129:                TYPE,
130:                /** this element is part of some other structure (task or type) */
131:                DATA,
132:                /** tag for root project element */
133:                PROJECT,
134:                /** tag for a target element */
135:                TARGET,
136:                /** tag for a project description element */
137:                DESCRIPTION,
138:                /** tag for an import statement */
139:                IMPORT;
140:            }
141:
142:            static class ElementType {
143:                final Kind kind;
144:                final String name; // null for PROJECT, TARGET, DESCRIPTION, IMPORT
145:
146:                ElementType(Kind kind, String name) {
147:                    this .kind = kind;
148:                    this .name = name;
149:                }
150:            }
151:
152:            /**
153:             * Determine what a particular element in a build script represents,
154:             * based on its name and the names of all of its parents.
155:             * Returns a pair of the kind of the element (one of the KIND_* constants)
156:             * and the details (a class name suitable for {@link IntrospectedInfo}, or
157:             * in the case of {@link KIND_SPECIAL}, one of the SPECIAL_* constants).
158:             * @param e an element
159:             * @return a two-element string (kind and details), or null if this element is anomalous
160:             */
161:            static final ElementType typeOf(Element e) {
162:                String name = e.getNodeName();
163:                Node p = e.getParentNode();
164:                if (p == null) {
165:                    throw new IllegalArgumentException("Detached node: " + e); // NOI18N
166:                }
167:                if (p.getNodeType() == Node.DOCUMENT_NODE) {
168:                    if (name.equals("project")) { // NOI18N
169:                        return new ElementType(Kind.PROJECT, null);
170:                    } else {
171:                        // Weird root element? Ignore.
172:                        return null;
173:                    }
174:                } else if (p.getNodeType() == Node.ELEMENT_NODE) {
175:                    // Find ourselves in context.
176:                    ElementType ptype = typeOf((Element) p);
177:                    if (ptype == null) {
178:                        // Unknown parent, therefore this is unknown too.
179:                        return null;
180:                    }
181:                    switch (ptype.kind) {
182:                    case PROJECT:
183:                        // <project> may have <description>, or types, or targets, or tasks
184:                        if (name.equals("description")) { // NOI18N
185:                            return new ElementType(Kind.DESCRIPTION, null);
186:                        } else if (name.equals("target")) { // NOI18N
187:                            return new ElementType(Kind.TARGET, null);
188:                        } else if (name.equals("import")) { // NOI18N
189:                            return new ElementType(Kind.IMPORT, null);
190:                        } else {
191:                            String taskClazz = getAntGrammar().getDefs("task")
192:                                    .get(name); // NOI18N
193:                            if (taskClazz != null) {
194:                                return new ElementType(Kind.TASK, taskClazz);
195:                            } else {
196:                                String typeClazz = getAntGrammar().getDefs(
197:                                        "type").get(name); // NOI18N
198:                                if (typeClazz != null) {
199:                                    return new ElementType(Kind.TYPE, typeClazz);
200:                                } else {
201:                                    return null;
202:                                }
203:                            }
204:                        }
205:                    case TARGET:
206:                        // <target> may have tasks and types
207:                        String taskClazz = getAntGrammar().getDefs("task").get(
208:                                name); // NOI18N
209:                        if (taskClazz != null) {
210:                            return new ElementType(Kind.TASK, taskClazz);
211:                        } else {
212:                            String typeClazz = getAntGrammar().getDefs("type")
213:                                    .get(name); // NOI18N
214:                            if (typeClazz != null) {
215:                                return new ElementType(Kind.TYPE, typeClazz);
216:                            } else {
217:                                return null;
218:                            }
219:                        }
220:                    case DESCRIPTION:
221:                        // <description> should have no children!
222:                        return null;
223:                    case IMPORT:
224:                        // <import> should have no children!
225:                        return null;
226:                    default:
227:                        // We must be data.
228:                        String clazz = getAntGrammar().getElements(ptype.name)
229:                                .get(name);
230:                        if (clazz != null) {
231:                            return new ElementType(Kind.DATA, clazz);
232:                        } else {
233:                            // Unknown data.
234:                            return null;
235:                        }
236:                    }
237:                } else {
238:                    throw new IllegalArgumentException("Bad parent for "
239:                            + e.toString() + ": " + p); // NOI18N
240:                }
241:            }
242:
243:            /**
244:             * @stereotype query
245:             * @output list of results that can be queried on name, and attributes
246:             * @time Performs fast up to 300 ms.
247:             * @param ctx represents virtual attribute <code>Node</code> to be replaced. Its parent is a element node.
248:             * @return list of <code>CompletionResult</code>s (ATTRIBUTE_NODEs) that can be queried on name, and attributes.
249:             *        Every list member represents one possibility.
250:             */
251:            public Enumeration<GrammarResult> queryAttributes(HintContext ctx) {
252:
253:                Element ownerElement = null;
254:                // Support both versions of GrammarQuery contract
255:                if (ctx.getNodeType() == Node.ATTRIBUTE_NODE) {
256:                    ownerElement = ((Attr) ctx).getOwnerElement();
257:                } else if (ctx.getNodeType() == Node.ELEMENT_NODE) {
258:                    ownerElement = (Element) ctx;
259:                }
260:                if (ownerElement == null) {
261:                    return Enumerations.empty();
262:                }
263:
264:                NamedNodeMap existingAttributes = ownerElement.getAttributes();
265:                List<String> possibleAttributes;
266:                ElementType type = typeOf(ownerElement);
267:                if (type == null) {
268:                    return Enumerations.empty();
269:                }
270:
271:                switch (type.kind) {
272:                case PROJECT:
273:                    possibleAttributes = new LinkedList<String>();
274:                    possibleAttributes.add("default");
275:                    possibleAttributes.add("name");
276:                    possibleAttributes.add("basedir");
277:                    break;
278:                case TARGET:
279:                    possibleAttributes = new LinkedList<String>();
280:                    possibleAttributes.add("name");
281:                    possibleAttributes.add("depends");
282:                    possibleAttributes.add("description");
283:                    possibleAttributes.add("if");
284:                    possibleAttributes.add("unless");
285:                    break;
286:                case DESCRIPTION:
287:                    return Enumerations.empty();
288:                case IMPORT:
289:                    possibleAttributes = new LinkedList<String>();
290:                    possibleAttributes.add("file");
291:                    possibleAttributes.add("optional");
292:                    break;
293:                default:
294:                    // task, type, or data; anyway, we have the defining class
295:                    possibleAttributes = new LinkedList<String>();
296:                    if (type.kind == Kind.TYPE) {
297:                        possibleAttributes.add("id");
298:                    }
299:                    if (getAntGrammar().isKnown(type.name)) {
300:                        possibleAttributes.addAll(new TreeSet<String>(
301:                                getAntGrammar().getAttributes(type.name)
302:                                        .keySet()));
303:                    }
304:                    if (type.kind == Kind.TASK) {
305:                        // Can have an ID too, but less important; leave at end.
306:                        possibleAttributes.add("id");
307:                        // Currently IntrospectedInfo includes this in the props for a type,
308:                        // though it excludes it for tasks. So for now add it explicitly
309:                        // only to tasks.
310:                        possibleAttributes.add("description");
311:                        // Also useful sometimes:
312:                        possibleAttributes.add("taskname");
313:                    }
314:                }
315:
316:                String prefix = ctx.getCurrentPrefix();
317:
318:                List<GrammarResult> list = new ArrayList<GrammarResult>();
319:                for (String attribute : possibleAttributes) {
320:                    if (attribute.startsWith(prefix)) {
321:                        if (existingAttributes.getNamedItem(attribute) == null) {
322:                            list.add(new MyAttr(attribute));
323:                        }
324:                    }
325:                }
326:
327:                LOG.log(Level.FINE, "queryAttributes({0}) -> {1}",
328:                        new Object[] { prefix, list });
329:                return Collections.enumeration(list);
330:            }
331:
332:            /**
333:             * @semantics Navigates through read-only Node tree to determine context and provide right results.
334:             * @postconditions Let ctx unchanged
335:             * @time Performs fast up to 300 ms.
336:             * @stereotype query
337:             * @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.
338:             * @return list of <code>CompletionResult</code>s (ELEMENT_NODEs) that can be queried on name, and attributes
339:             *        Every list member represents one possibility.
340:             */
341:            public Enumeration<GrammarResult> queryElements(HintContext ctx) {
342:
343:                Node parent = ((Node) ctx).getParentNode();
344:                if (parent == null) {
345:                    return Enumerations.empty();
346:                }
347:                if (parent.getNodeType() != Node.ELEMENT_NODE) {
348:                    return Enumerations.empty();
349:                }
350:
351:                List<String> elements;
352:                ElementType type = typeOf((Element) parent);
353:                if (type == null) {
354:                    return Enumerations.empty();
355:                }
356:
357:                switch (type.kind) {
358:                case PROJECT:
359:                    elements = new LinkedList<String>();
360:                    elements.add("target");
361:                    elements.add("import");
362:                    elements.add("property");
363:                    elements.add("description");
364:                    SortedSet<String> tasks = getSortedDefs("task");
365:                    tasks.remove("property");
366:                    tasks.remove("import");
367:                    elements.addAll(tasks); // Ant 1.6 permits any tasks at top level
368:                    elements.addAll(getSortedDefs("type"));
369:                    break;
370:                case TARGET:
371:                    elements = new ArrayList<String>(getSortedDefs("task"));
372:                    // targets can have embedded types too, though less common:
373:                    elements.addAll(getSortedDefs("type")); // NOI18N
374:                    break;
375:                case DESCRIPTION:
376:                    return Enumerations.empty();
377:                case IMPORT:
378:                    return Enumerations.empty();
379:                default:
380:                    // some introspectable class
381:                    if (getAntGrammar().isKnown(type.name)) {
382:                        elements = new ArrayList<String>(
383:                                new TreeSet<String>(getAntGrammar()
384:                                        .getElements(type.name).keySet()));
385:                    } else {
386:                        elements = Collections.emptyList();
387:                    }
388:                }
389:
390:                String prefix = ctx.getCurrentPrefix();
391:
392:                List<GrammarResult> list = new ArrayList<GrammarResult>();
393:                for (String element : elements) {
394:                    if (element.startsWith(prefix)) {
395:                        list.add(new MyElement(element));
396:                    }
397:                }
398:
399:                LOG.log(Level.FINE, "queryElements({0}) -> {1}", new Object[] {
400:                        prefix, list });
401:                return Collections.enumeration(list);
402:            }
403:
404:            private static SortedSet<String> getSortedDefs(String kind) {
405:                SortedSet<String> defs = new TreeSet<String>(Collator
406:                        .getInstance());
407:                defs.addAll(getAntGrammar().getDefs(kind).keySet());
408:                return defs;
409:            }
410:
411:            /**
412:             * Allow to get names of <b>declared notations</b>.
413:             * @return list of <code>CompletionResult</code>s (NOTATION_NODEs)
414:             */
415:            public Enumeration<GrammarResult> queryNotations(String prefix) {
416:                return Enumerations.empty();
417:            }
418:
419:            public Enumeration<GrammarResult> queryValues(HintContext ctx) {
420:                LOG.log(Level.FINE, "queryValues({0})", ctx.getCurrentPrefix());
421:                // #38341: ctx is apparently instanceof Attr or Text
422:                // (actually never instanceof Text, just TEXT_NODE: #38339)
423:                Attr ownerAttr;
424:                if (canCompleteProperty(ctx.getCurrentPrefix())) {
425:                    LOG.fine("...can complete property");
426:                    return completeProperties(ctx);
427:                } else if (ctx.getNodeType() == Node.ATTRIBUTE_NODE) {
428:                    ownerAttr = (Attr) ctx;
429:                } else {
430:                    LOG.fine("...unknown node type");
431:                    return Enumerations.empty();
432:                }
433:                Element ownerElement = ownerAttr.getOwnerElement();
434:                String attrName = ownerAttr.getName();
435:                ElementType type = typeOf(ownerElement);
436:                if (type == null) {
437:                    LOG.fine("...unknown type");
438:                    return Enumerations.empty();
439:                }
440:                List<String> choices = new ArrayList<String>();
441:
442:                switch (type.kind) {
443:                case PROJECT:
444:                    if (attrName.equals("default")) {
445:                        // XXX list known targets?
446:                    } else if (attrName.equals("basedir")) {
447:                        // XXX file completion?
448:                    }
449:                    // freeform: name
450:                    break;
451:                case TARGET:
452:                    if (attrName.equals("depends")) {
453:                        // XXX list known targets?
454:                    } else if (attrName.equals("if")
455:                            || attrName.equals("unless")) {
456:                        choices.addAll(Arrays.asList(likelyPropertyNames(ctx)));
457:                    }
458:                    // freeform: description
459:                    break;
460:                case DESCRIPTION:
461:                    // nothing applicable
462:                    break;
463:                case IMPORT:
464:                    if (attrName.equals("file")) {
465:                        // freeform
466:                    } else if (attrName.equals("optional")) {
467:                        choices.add("true");
468:                        choices.add("false");
469:                    }
470:                    break;
471:                default:
472:                    String elementClazz = type.name;
473:                    if (getAntGrammar().isKnown(elementClazz)) {
474:                        String attrClazzName = getAntGrammar().getAttributes(
475:                                elementClazz).get(attrName);
476:                        if (attrClazzName != null) {
477:                            if (getAntGrammar().isKnown(attrClazzName)) {
478:                                String[] enumTags = getAntGrammar().getTags(
479:                                        attrClazzName);
480:                                if (enumTags != null) {
481:                                    choices.addAll(Arrays.asList(enumTags));
482:                                }
483:                            }
484:                            if (attrClazzName.equals("boolean")) {
485:                                choices.add("true");
486:                                choices.add("false");
487:                            } else if (attrClazzName
488:                                    .equals("org.apache.tools.ant.types.Reference")) {
489:                                // XXX add names of ids
490:                            } else if (attrClazzName
491:                                    .equals("org.apache.tools.ant.types.Path")
492:                                    || attrClazzName.equals("java.io.File")
493:                            /* || "path" attr on Path or Path.Element */
494:                            ) {
495:                                // XXX complete filenames
496:                            } else if (attrClazzName.equals("java.lang.String")
497:                                    && Arrays
498:                                            .asList(
499:                                                    PROPERTY_NAME_VALUED_PROPERTY_NAMES)
500:                                            .contains(attrName)) {
501:                                // <isset property="..."/>, <include name="*" unless="..."/>, etc.
502:                                choices.addAll(Arrays
503:                                        .asList(likelyPropertyNames(ctx)));
504:                            }
505:                        }
506:                    }
507:                }
508:
509:                // Create the completion:
510:                String prefix = ctx.getCurrentPrefix();
511:                List<GrammarResult> list = new ArrayList<GrammarResult>();
512:                for (String choice : choices) {
513:                    if (choice.startsWith(prefix)) {
514:                        list.add(new MyText(choice));
515:                    }
516:                }
517:
518:                LOG.log(Level.FINE, "queryValues({0}) -> {1}", new Object[] {
519:                        prefix, list });
520:                return Collections.enumeration(list);
521:            }
522:
523:            /**
524:             * Check whether a given content string (of an attribute value or of an element's
525:             * content) has an uncompleted "${" sequence in it, i.e. one that has not been matched
526:             * with a corresponding "}".
527:             * E.g.:
528:             * <pathelement location="${foo
529:             *                             ^ caret
530:             * Also if the last character is "$" it can be completed.
531:             * @param content the current content of the attribute value or element
532:             * @return true if there is an uncompleted property here
533:             */
534:            private static boolean canCompleteProperty(String content) {
535:                content = deletedEscapedShells(content);
536:                if (content.length() == 0) {
537:                    return false;
538:                }
539:                if (content.charAt(content.length() - 1) == '$') {
540:                    return true;
541:                }
542:                int idx = content.lastIndexOf("${");
543:                return idx != -1 && content.indexOf('}', idx) == -1;
544:            }
545:
546:            private static Enumeration<GrammarResult> completeProperties(
547:                    HintContext ctx) {
548:                String content = ctx.getCurrentPrefix();
549:                assert content.length() > 0;
550:                String header;
551:                String propPrefix;
552:                if (content.charAt(content.length() - 1) == '$') {
553:                    header = content + '{';
554:                    propPrefix = "";
555:                } else {
556:                    int idx = content.lastIndexOf("${");
557:                    assert idx != -1;
558:                    header = content.substring(0, idx + 2);
559:                    propPrefix = content.substring(idx + 2);
560:                }
561:                String[] props = likelyPropertyNames(ctx);
562:                // completion on text works differently from attrs:
563:                // the context should not be returned (#38342)
564:                boolean shortHeader = ctx.getNodeType() == Node.TEXT_NODE;
565:                List<GrammarResult> list = new ArrayList<GrammarResult>();
566:                for (int i = 0; i < props.length; i++) {
567:                    if (props[i].startsWith(propPrefix)) {
568:                        String text = header + props[i] + '}';
569:                        if (shortHeader) {
570:                            assert text.startsWith(content) : "text=" + text
571:                                    + " content=" + content;
572:                            text = text.substring(content.length());
573:                        }
574:                        list.add(new MyText(text));
575:                    }
576:                }
577:                LOG.log(Level.FINE, "completeProperties({0}) -> {1}",
578:                        new Object[] { content, list });
579:                return Collections.enumeration(list);
580:            }
581:
582:            /**
583:             * Names of Ant properties that are generally present and defined in any script.
584:             */
585:            private static final String[] STOCK_PROPERTY_NAMES = {
586:            // Present in most Ant installations:
587:                    "ant.home", // NOI18N
588:                    // Defined by Ant as standard properties:
589:                    "basedir", // NOI18N
590:                    "ant.file", // NOI18N
591:                    "ant.project.name", // NOI18N
592:                    "ant.java.version", // NOI18N
593:                    "ant.version", // NOI18N
594:                    // Defined by System.getProperties as standard system properties:
595:                    "java.version", // NOI18N
596:                    "java.vendor", // NOI18N
597:                    "java.vendor.url", // NOI18N
598:                    "java.home", // NOI18N
599:                    "java.vm.specification.version", // NOI18N
600:                    "java.vm.specification.vendor", // NOI18N
601:                    "java.vm.specification.name", // NOI18N
602:                    "java.vm.version", // NOI18N
603:                    "java.vm.vendor", // NOI18N
604:                    "java.vm.name", // NOI18N
605:                    "java.specification.version", // NOI18N
606:                    "java.specification.vendor", // NOI18N
607:                    "java.specification.name", // NOI18N
608:                    "java.class.version", // NOI18N
609:                    "java.class.path", // NOI18N
610:                    "java.library.path", // NOI18N
611:                    "java.io.tmpdir", // NOI18N
612:                    "java.compiler", // NOI18N
613:                    "java.ext.dirs", // NOI18N
614:                    "os.name", // NOI18N
615:                    "os.arch", // NOI18N
616:                    "os.version", // NOI18N
617:                    "file.separator", // NOI18N
618:                    "path.separator", // NOI18N
619:                    "line.separator", // NOI18N
620:                    "user.name", // NOI18N
621:                    "user.home", // NOI18N
622:                    "user.dir", // NOI18N
623:            };
624:
625:            private static String[] likelyPropertyNames(HintContext ctx) {
626:                // #38343: ctx.getOwnerDocument returns some bogus unusable empty thing
627:                // so find the root element manually
628:                Element parent;
629:                // #38341: docs for queryValues says Attr or Element, but really Attr or Text
630:                // (and CDataSection never seems to permit completion at all...)
631:                if (ctx.getNodeType() == Node.ATTRIBUTE_NODE) {
632:                    parent = ((Attr) ctx).getOwnerElement();
633:                } else if (ctx.getNodeType() == Node.TEXT_NODE) {
634:                    Node p = ctx.getParentNode();
635:                    if (p != null && p.getNodeType() == Node.ELEMENT_NODE) {
636:                        parent = (Element) p;
637:                    } else {
638:                        System.err.println("strange parent of text node: "
639:                                + p.getNodeType() + " " + p);
640:                        return new String[0];
641:                    }
642:                } else {
643:                    System.err.println("strange context type: "
644:                            + ctx.getNodeType() + " " + ctx);
645:                    return new String[0];
646:                }
647:                while (parent.getParentNode() != null
648:                        && parent.getParentNode().getNodeType() == Node.ELEMENT_NODE) {
649:                    parent = (Element) parent.getParentNode();
650:                }
651:                // #38343: getElementsByTagName just throws an exception, you can't use it...
652:                Set<String> choices = new TreeSet<String>(Arrays
653:                        .asList(STOCK_PROPERTY_NAMES));
654:                visitForLikelyPropertyNames(parent, choices);
655:                Iterator<String> it = choices.iterator();
656:                while (it.hasNext()) {
657:                    String propname = it.next();
658:                    if (propname.indexOf("${") != -1) {
659:                        // Not actually a direct property name, rather a computed name.
660:                        // Skip it as it cannot be used here.
661:                        it.remove();
662:                    }
663:                }
664:                return choices.toArray(new String[choices.size()]);
665:            }
666:
667:            private static final String[] PROPERTY_NAME_VALUED_PROPERTY_NAMES = {
668:                    "if", "unless",
669:                    // XXX accept any *property
670:                    "property", "failureproperty", "errorproperty",
671:                    "addproperty", };
672:
673:            private static void visitForLikelyPropertyNames(Node n,
674:                    Set<String> choices) {
675:                int type = n.getNodeType();
676:                switch (type) {
677:                case Node.ELEMENT_NODE:
678:                    // XXX would be more precise to use typeOf here, but maybe slower?
679:                    // Look for <property name="propname" .../> and similar
680:                    Element el = (Element) n;
681:                    String tagname = el.getTagName();
682:                    if (tagname.equals("property")) {
683:                        String propname = el.getAttribute("name");
684:                        // #38343: Element impl is broken and can return null from getAttribute
685:                        if (propname != null && propname.length() > 0) {
686:                            choices.add(propname);
687:                        }
688:                        // XXX handle <property file="..."/> with a resolvable filename
689:                    } else if (tagname.equals("buildnumber")) {
690:                        // This task always defines ${build.number}
691:                        choices.add("build.number");
692:                    } else if (tagname.equals("tstamp")) {
693:                        // XXX handle prefix="whatever" -> ${whatever.TODAY} etc.
694:                        // XXX handle nested <format property="foo" .../> -> ${foo}
695:                        choices.add("DSTAMP");
696:                        choices.add("TSTAMP");
697:                        choices.add("TODAY");
698:                    }
699:                    // <available>, <dirname>, <pathconvert>, <uptodate>, <target>, <isset>, <include>, etc.
700:                    for (int i = 0; i < PROPERTY_NAME_VALUED_PROPERTY_NAMES.length; i++) {
701:                        String propname = el
702:                                .getAttribute(PROPERTY_NAME_VALUED_PROPERTY_NAMES[i]);
703:                        if (propname != null && propname.length() > 0) {
704:                            choices.add(propname);
705:                        }
706:                    }
707:                    break;
708:                case Node.ATTRIBUTE_NODE:
709:                case Node.TEXT_NODE:
710:                    // Look for ${propname}
711:                    String text = deletedEscapedShells(n.getNodeValue());
712:                    int idx = 0;
713:                    while (true) {
714:                        int start = text.indexOf("${", idx);
715:                        if (start == -1) {
716:                            break;
717:                        }
718:                        int end = text.indexOf('}', start + 2);
719:                        if (end == -1) {
720:                            break;
721:                        }
722:                        String propname = text.substring(start + 2, end);
723:                        if (propname.length() > 0) {
724:                            choices.add(propname);
725:                        }
726:                        idx = end + 1;
727:                    }
728:                    break;
729:                default:
730:                    // ignore
731:                    break;
732:                }
733:                NodeList l = n.getChildNodes();
734:                for (int i = 0; i < l.getLength(); i++) {
735:                    visitForLikelyPropertyNames(l.item(i), choices);
736:                }
737:                // Element attributes are not considered child nodes as such.
738:                NamedNodeMap m = n.getAttributes();
739:                if (m != null) {
740:                    for (int i = 0; i < m.getLength(); i++) {
741:                        visitForLikelyPropertyNames(m.item(i), choices);
742:                    }
743:                }
744:            }
745:
746:            /**
747:             * Remove pairs of '$$' to avoid being confused by them.
748:             * They do not introduce property references.
749:             */
750:            private static String deletedEscapedShells(String text) {
751:                // XXX could be faster w/o regexps
752:                return text.replaceAll("\\$\\$", "");
753:            }
754:
755:            // return defaults, no way to query them
756:            public GrammarResult queryDefault(final HintContext ctx) {
757:                return null;
758:            }
759:
760:            // it is not yet implemented
761:            public boolean isAllowed(Enumeration<GrammarResult> en) {
762:                return true;
763:            }
764:
765:            // customizers section ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
766:
767:            public java.awt.Component getCustomizer(HintContext ctx) {
768:                return null;
769:            }
770:
771:            public boolean hasCustomizer(HintContext ctx) {
772:                return false;
773:            }
774:
775:            public org.openide.nodes.Node.Property[] getProperties(
776:                    HintContext ctx) {
777:                return null;
778:            }
779:
780:            // Result classes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
781:
782:            private static abstract class AbstractResultNode extends
783:                    AbstractNode implements  GrammarResult {
784:
785:                public Icon getIcon(int kind) {
786:                    return null;
787:                }
788:
789:                public String getDescription() {
790:                    return null;
791:                }
792:
793:                public String getDisplayName() {
794:                    return null;
795:                }
796:
797:                // TODO in MyElement return true for really empty elements such as "pathelement"
798:                public boolean isEmptyElement() {
799:                    return false;
800:                }
801:            }
802:
803:            private static class MyEntityReference extends AbstractResultNode
804:                    implements  EntityReference {
805:
806:                private String name;
807:
808:                MyEntityReference(String name) {
809:                    this .name = name;
810:                }
811:
812:                public short getNodeType() {
813:                    return Node.ENTITY_REFERENCE_NODE;
814:                }
815:
816:                public @Override
817:                String getNodeName() {
818:                    return name;
819:                }
820:
821:            }
822:
823:            private static class MyElement extends AbstractResultNode implements 
824:                    Element {
825:
826:                private String name;
827:
828:                MyElement(String name) {
829:                    this .name = name;
830:                }
831:
832:                public short getNodeType() {
833:                    return Node.ELEMENT_NODE;
834:                }
835:
836:                public @Override
837:                String getNodeName() {
838:                    return name;
839:                }
840:
841:                public @Override
842:                String getTagName() {
843:                    return name;
844:                }
845:
846:                public @Override
847:                String toString() {
848:                    return name;
849:                }
850:
851:            }
852:
853:            private static class MyAttr extends AbstractResultNode implements 
854:                    Attr {
855:
856:                private String name;
857:
858:                MyAttr(String name) {
859:                    this .name = name;
860:                }
861:
862:                public short getNodeType() {
863:                    return Node.ATTRIBUTE_NODE;
864:                }
865:
866:                public @Override
867:                String getNodeName() {
868:                    return name;
869:                }
870:
871:                public @Override
872:                String getName() {
873:                    return name;
874:                }
875:
876:                public @Override
877:                String getValue() {
878:                    return null; //??? what spec says
879:                }
880:
881:                public @Override
882:                String toString() {
883:                    return name;
884:                }
885:
886:            }
887:
888:            private static class MyText extends AbstractResultNode implements 
889:                    Text {
890:
891:                private String data;
892:
893:                MyText(String data) {
894:                    this .data = data;
895:                }
896:
897:                public short getNodeType() {
898:                    return Node.TEXT_NODE;
899:                }
900:
901:                public @Override
902:                String getNodeValue() {
903:                    return data;
904:                }
905:
906:                public @Override
907:                String getData() throws DOMException {
908:                    return data;
909:                }
910:
911:                public @Override
912:                int getLength() {
913:                    return data == null ? -1 : data.length();
914:                }
915:
916:                public @Override
917:                String toString() {
918:                    return data;
919:                }
920:
921:                public @Override
922:                String getDisplayName() {
923:                    return data; // #113804
924:                }
925:
926:            }
927:
928:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.