KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > xml > dtd > grammar > DTDGrammar


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.xml.dtd.grammar;
21
22 import java.util.*;
23 import javax.swing.Icon JavaDoc;
24
25 import org.w3c.dom.*;
26
27 import org.openide.ErrorManager;
28
29 import org.netbeans.modules.xml.api.model.*;
30 import org.netbeans.modules.xml.spi.dom.*;
31
32 /**
33  * Rather simple query implemetation based on DTD grammar.
34  * It is produced by {@link DTDParser}.
35  * Hints given by this grammar do not guarantee that valid XML document is created.
36  *
37  * @author Petr Kuzel
38  */

39 class DTDGrammar implements ExtendedGrammarQuery {
40     
41     // element name keyed
42
private Map elementDecls, attrDecls;
43     
44     // Map<elementName:String, model:String || model:ContentModel || null>
45
// this map is filled asynchronously as it takes some time
46
private Map contentModels;
47     
48     // Map<elementname + " " + attributename, List<String>>
49
private Map attrEnumerations;
50     
51     // Map<elementname + " " + attributename, String>
52
private Map defaultAttributeValues;
53     
54     private Set entities, notations;
55
56     /** Set&lt;elementName:String> holding all emenets with <code>EMPTY</code> content model.*/
57     private Set emptyElements;
58
59     private List/*<String>*/ resolvedEntities;
60     
61     /** Creates new DTDGrammar */
62     DTDGrammar(Map elementDecls, Map contentModels, Map attrDecls, Map attrDefs, Map enums, Set entities, Set notations, Set emptyElements) {
63         this.elementDecls = elementDecls;
64         this.attrDecls = attrDecls;
65         this.entities = entities;
66         this.notations = notations;
67         this.attrEnumerations = enums;
68         this.contentModels = contentModels;
69         this.defaultAttributeValues = attrDefs;
70         this.emptyElements = emptyElements;
71     }
72
73     void setResolvedEntities(List/*<String>*/ resolvedEntities) {
74         this.resolvedEntities = resolvedEntities;
75     }
76     
77     public List/*<String>*/ getResolvedEntities() {
78         return resolvedEntities;
79     }
80     
81     /**
82      * Allow to get names of <b>parsed general entities</b>.
83      * @return list of <code>CompletionResult</code>s (ENTITY_REFERENCE_NODEs)
84      */

85     public Enumeration queryEntities(String JavaDoc prefix) {
86         if (entities == null) return org.openide.util.Enumerations.empty();
87         
88         LinkedList list = new LinkedList();
89         Iterator it = entities.iterator();
90         while ( it.hasNext()) {
91             String JavaDoc next = (String JavaDoc) it.next();
92             if (next.startsWith(prefix)) {
93                 list.add (new MyEntityReference(next));
94             }
95         }
96
97         // add well-know build-in entity names
98

99         if ("lt".startsWith(prefix)) list.add(new MyEntityReference("lt"));
100         if ("gt".startsWith(prefix)) list.add(new MyEntityReference("gt"));
101         if ("apos".startsWith(prefix)) list.add(new MyEntityReference("apos"));
102         if ("quot".startsWith(prefix)) list.add(new MyEntityReference("quot"));
103         if ("amp".startsWith(prefix)) list.add(new MyEntityReference("amp"));
104         
105         Collections.sort(list, new Comparator() {
106             public int compare(Object JavaDoc o1, Object JavaDoc o2) {
107                 return ((MyEntityReference)o1).getNodeName().compareTo(((MyEntityReference)o2).getNodeName());
108             }
109             public boolean equals(Object JavaDoc obj) {
110                 return true;
111             }
112         });
113         
114         return java.util.Collections.enumeration (list);
115     }
116     
117     /**
118      * @stereotype query
119      * @output list of results that can be queried on name, and attributes
120      * @time Performs fast up to 300 ms.
121      * @param ctx represents virtual attribute <code>Node</code> to be replaced. Its parent is a element node.
122      * @return list of <code>CompletionResult</code>s (ATTRIBUTE_NODEs) that can be queried on name, and attributes.
123      * Every list member represents one possibility.
124      */

125     public Enumeration queryAttributes(HintContext ctx) {
126         if (attrDecls == null) return org.openide.util.Enumerations.empty();
127         
128         Element el = null;
129         String JavaDoc currentAttrName = null;
130         // Support two versions of GrammarQuery contract
131
if (ctx.getNodeType() == Node.ATTRIBUTE_NODE) {
132             currentAttrName = ctx.getNodeName();
133             el = ((Attr)ctx).getOwnerElement();
134         } else if (ctx.getNodeType() == Node.ELEMENT_NODE) {
135             el = (Element) ctx;
136         }
137         if (el == null) return org.openide.util.Enumerations.empty();
138         
139         NamedNodeMap existingAttributes = el.getAttributes();
140         
141         Set possibleAttributes = (Set) attrDecls.get(el.getTagName());
142         if (possibleAttributes == null) return org.openide.util.Enumerations.empty();
143         
144         String JavaDoc prefix = ctx.getCurrentPrefix();
145         
146         LinkedList list = new LinkedList ();
147         Iterator it = possibleAttributes.iterator();
148         while ( it.hasNext()) {
149             String JavaDoc next = (String JavaDoc) it.next();
150             if (next.startsWith(prefix)) {
151                 if (existingAttributes.getNamedItem(next) == null ||
152                         next.equals(currentAttrName)) {
153                     list.add (new MyAttr(next));
154                 }
155             }
156         }
157         
158         return Collections.enumeration (list);
159     }
160     
161     /**
162      * @semantics Navigates through read-only Node tree to determine context and provide right results.
163      * @postconditions Let ctx unchanged
164      * @time Performs fast up to 300 ms.
165      * @stereotype query
166      * @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.
167      * @return list of <code>CompletionResult</code>s (ELEMENT_NODEs) that can be queried on name, and attributes
168      * Every list member represents one possibility.
169      */

170     public Enumeration queryElements(HintContext ctx) {
171         if (elementDecls == null) return org.openide.util.Enumerations.empty();;
172         
173         Node node = ((Node)ctx).getParentNode();
174         Set elements = null;
175         
176         if (node instanceof Element) {
177             Element el = (Element) node;
178             if (el == null) return org.openide.util.Enumerations.empty();;
179
180             // lazilly parse content model
181
Object JavaDoc model = null;
182             String JavaDoc prefs = System.getProperty("netbeans.xml.completion", "default"); // NOI18N
183
if ("fast".equals(prefs)) { // NO18N
184
model = null;
185             } else if ("default".equals(prefs) || "accurate".equals(prefs)) { // NO18N
186
model = contentModels.get(el.getTagName());
187             } else {
188                 model = null;
189             }
190             if (model instanceof String JavaDoc) {
191                 model = ContentModel.parseContentModel((String JavaDoc)model);
192                 contentModels.put(el.getTagName(), model);
193             }
194             if (model instanceof ContentModel) {
195                 Enumeration en = ((ContentModel)model).whatCanFollow(new PreviousEnumeration(el, ctx));
196                 if (en == null) return org.openide.util.Enumerations.empty();
197                 String JavaDoc prefix = ctx.getCurrentPrefix();
198                 elements = new TreeSet();
199                 while (en.hasMoreElements()) {
200                     String JavaDoc next = (String JavaDoc) en.nextElement();
201                     if (next.startsWith(prefix)) {
202                         elements.add(next);
203                     }
204                 }
205             }
206             // simple fallback
207
if (elements == null) {
208                 elements = (Set) elementDecls.get(el.getTagName());
209             }
210         } else if (node instanceof Document) {
211             elements = elementDecls.keySet(); //??? should be one from DOCTYPE if exist
212
} else {
213             return org.openide.util.Enumerations.empty();
214         }
215                 
216         if (elements == null) return org.openide.util.Enumerations.empty();;
217         String JavaDoc prefix = ctx.getCurrentPrefix();
218         
219         LinkedList list = new LinkedList ();
220         Iterator it = elements.iterator();
221         while ( it.hasNext()) {
222             String JavaDoc next = (String JavaDoc) it.next();
223             if (next.startsWith(prefix)) {
224                 boolean empty = emptyElements.contains(next);
225                 list.add(new MyElement(next, empty));
226             }
227         }
228         
229         return Collections.enumeration (list);
230     }
231     
232     /**
233      * Allow to get names of <b>declared notations</b>.
234      * @return list of <code>CompletionResult</code>s (NOTATION_NODEs)
235      */

236     public Enumeration queryNotations(String JavaDoc prefix) {
237         if (notations == null) return org.openide.util.Enumerations.empty();;
238         
239         LinkedList list = new LinkedList ();
240         Iterator it = notations.iterator();
241         while ( it.hasNext()) {
242             String JavaDoc next = (String JavaDoc) it.next();
243             if (next.startsWith(prefix)) {
244                 list.add (new MyNotation(next));
245             }
246         }
247         
248         return Collections.enumeration (list);
249     }
250        
251     /**
252      * @semantics Navigates through read-only Node tree to determine context and provide right results.
253      * @postconditions Let ctx unchanged
254      * @time Performs fast up to 300 ms.
255      * @stereotype query
256      * @input ctx represents virtual Node that has to be replaced (parent can be either Attr or Element), its own attributes does not name sense, it can be used just as the navigation start point.
257      * @return list of <code>CompletionResult</code>s (TEXT_NODEs) that can be queried on name, and attributes.
258      * Every list member represents one possibility.
259      */

260     public Enumeration queryValues(HintContext ctx) {
261         if (attrEnumerations.isEmpty()) return org.openide.util.Enumerations.empty();
262         
263         if (ctx.getNodeType() == Node.ATTRIBUTE_NODE) {
264             String JavaDoc attributeName = ctx.getNodeName();
265             Element element = ((Attr)ctx).getOwnerElement();
266             if (element == null) return org.openide.util.Enumerations.empty();
267             
268             String JavaDoc elementName = element.getNodeName();
269             String JavaDoc key = elementName + " " + attributeName;
270             List values = (List) attrEnumerations.get(key);
271             if (values == null) return org.openide.util.Enumerations.empty();
272             
273             String JavaDoc prefix = ctx.getCurrentPrefix();
274             LinkedList en = new LinkedList ();
275             Iterator it = values.iterator();
276             while (it.hasNext()) {
277                 String JavaDoc next = (String JavaDoc) it.next();
278                 if (next.startsWith(prefix)) {
279                     en.add(new MyText(next, next));
280                 }
281             }
282             return Collections.enumeration (en);
283         }
284         return org.openide.util.Enumerations.empty();
285     }
286
287     // return defaults for attribute values (DTD does not declare content defaults)
288
public GrammarResult queryDefault(final HintContext ctx) {
289         
290         Node node = ctx;
291         
292         if (ctx.getNodeType() == Node.TEXT_NODE) {
293             node = ctx.getParentNode();
294             if (node == null) return null;
295         }
296         
297         if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
298             Attr attr = (Attr) node;
299             Element element = attr.getOwnerElement();
300             if (element == null) return null;
301
302             String JavaDoc elementName = element.getNodeName();
303             String JavaDoc attributeName = attr.getNodeName();
304             String JavaDoc key = elementName + " " + attributeName; // NOI18N
305
String JavaDoc def = (String JavaDoc) defaultAttributeValues.get(key);
306             if (def == null) return null;
307             return new MyText(def, def);
308         }
309         
310         return null;
311     }
312     
313     // it is not yet implemented
314
public boolean isAllowed(Enumeration en) {
315         return true;
316     }
317     
318     // customizers section ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
319

320     public java.awt.Component JavaDoc getCustomizer(HintContext ctx) {
321         return null;
322     }
323     
324     public boolean hasCustomizer(HintContext ctx) {
325         return false;
326     }
327
328     public org.openide.nodes.Node.Property[] getProperties(HintContext ctx) {
329         return null;
330     }
331     
332
333     /** For debug purposes only. */
334     public String JavaDoc toString() {
335         return "DTD grammar";
336 // return "DTDGrammar:\nelements: " + elementDecls.keySet() + "\nattributes:" + attrDecls.keySet() + "";
337
}
338
339     /**
340      * Lazy evaluated enumeration of previous <b>element</b> sibling
341      * tag names.
342      */

343     private static class PreviousEnumeration implements Enumeration {
344         
345         private final Node parent;
346         private final Element lastElement;
347         private Node next;
348         private boolean eoeSeen = false;
349
350         PreviousEnumeration(Node parent, Node pointer) {
351             this.parent = parent;
352             Node last = pointer.getPreviousSibling();
353             while (last != null) {
354                 if (last.getNodeType() == Node.ELEMENT_NODE) break;
355                 last = last.getPreviousSibling();
356             }
357             lastElement = (Element) last;
358
359             // init next
360

361             if (last != null) {
362                 fetchNext(parent.getFirstChild());
363             } else {
364                 next = null;
365             }
366
367         }
368         
369         public boolean hasMoreElements() {
370             return next != null;
371         }
372
373         public Object JavaDoc nextElement() {
374             if (next == null) throw new NoSuchElementException();
375             try {
376                 return next.getNodeName();
377             } finally {
378                 fetchNext(next.getNextSibling());
379             }
380         }
381
382         /**
383          * Load next field being aware if terminating element
384          * @param next candidate
385          */

386         private void fetchNext(Node candidate) {
387             next = candidate;
388             if (eoeSeen) {
389                 next = null;
390             } else {
391                 while (next != null) {
392                     if (next.getNodeType() == Node.ELEMENT_NODE) break;
393                     next = next.getNextSibling();
394                 }
395                 //!!! how to properly implement it
396
if (lastElement.equals(next)) eoeSeen = true;
397             }
398         }
399     }
400     
401     // Result classes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
402

403     
404     private static abstract class AbstractResultNode extends AbstractNode implements GrammarResult {
405         
406         public Icon JavaDoc getIcon(int kind) {
407             return null;
408         }
409         
410         /**
411          * @output provide additional information simplifiing decision
412          */

413         public String JavaDoc getDescription() {
414             return getNodeName() + " desc";
415         }
416         
417         /**
418          * @output text representing name of suitable entity
419          * //??? is it really needed
420          */

421         public String JavaDoc getText() {
422             return getNodeName();
423         }
424         
425         /**
426          * @output name that is presented to user
427          */

428         public String JavaDoc getDisplayName() {
429             return null;
430         }
431
432         public boolean isEmptyElement() {
433             return false;
434         }
435     }
436     
437     private static class MyEntityReference extends AbstractResultNode implements EntityReference {
438         
439         private String JavaDoc name;
440         
441         MyEntityReference(String JavaDoc name) {
442             this.name = name;
443         }
444         
445         public short getNodeType() {
446             return Node.ENTITY_REFERENCE_NODE;
447         }
448         
449         public String JavaDoc getNodeName() {
450             return name;
451         }
452         
453         
454     }
455     
456     private static class MyElement extends AbstractResultNode implements Element {
457         
458         private String JavaDoc name;
459
460         private boolean empty;
461
462         MyElement(String JavaDoc name, boolean empty) {
463             this.name = name;
464             this.empty = empty;
465         }
466         
467         public short getNodeType() {
468             return Node.ELEMENT_NODE;
469         }
470         
471         public String JavaDoc getNodeName() {
472             return name;
473         }
474         
475         public String JavaDoc getTagName() {
476             return name;
477         }
478
479         public boolean isEmptyElement() {
480             return empty;
481         }
482     }
483
484     private static class MyAttr extends AbstractResultNode implements Attr {
485         
486         private String JavaDoc name;
487         
488         MyAttr(String JavaDoc name) {
489             this.name = name;
490         }
491         
492         public short getNodeType() {
493             return Node.ATTRIBUTE_NODE;
494         }
495         
496         public String JavaDoc getNodeName() {
497             return name;
498         }
499         
500         public String JavaDoc getName() {
501             return name;
502         }
503
504         public String JavaDoc getValue() {
505             return null; //??? what spec says
506
}
507         
508         
509     }
510
511     private static class MyNotation extends AbstractResultNode implements Notation {
512         
513         private String JavaDoc name;
514         
515         MyNotation(String JavaDoc name) {
516             this.name = name;
517         }
518         
519         public short getNodeType() {
520             return Node.NOTATION_NODE;
521         }
522         
523         public String JavaDoc getNodeName() {
524             return name;
525         }
526                         
527     }
528     
529     private static class MyText extends AbstractResultNode implements Text {
530         
531         private final String JavaDoc data;
532         private final String JavaDoc displayName;
533
534         MyText(String JavaDoc data, String JavaDoc displayName) {
535             this.data = data;
536             this.displayName = displayName;
537         }
538         
539         public short getNodeType() {
540             return Node.TEXT_NODE;
541         }
542
543         public String JavaDoc getNodeValue() {
544             return getData();
545         }
546         
547         public String JavaDoc getData() throws DOMException {
548             return data;
549         }
550
551         public int getLength() {
552             return data == null ? -1 : data.length();
553         }
554
555         public String JavaDoc getDisplayName() {
556             return displayName;
557         }
558     }
559         
560 }
561
Popular Tags