KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > xsl > grammar > XSLGrammarQuery


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.xsl.grammar;
21
22 import java.io.IOException JavaDoc;
23 import java.util.*;
24 import javax.swing.Icon JavaDoc;
25 //import org.apache.xpath.XPathAPI;
26

27 import org.netbeans.api.xml.services.UserCatalog;
28 import org.netbeans.modules.xml.api.model.*;
29 import org.netbeans.modules.xml.spi.dom.*;
30 import org.netbeans.modules.xml.api.cookies.ScenarioCookie;
31 import org.netbeans.modules.xsl.api.XSLCustomizer;
32 import org.netbeans.modules.xsl.api.XSLScenario;
33 import org.openide.filesystems.Repository;
34 import org.openide.filesystems.FileObject;
35 import org.openide.filesystems.FileSystem;
36 import org.openide.loaders.FolderLookup;
37 import org.openide.loaders.DataFolder;
38 import org.openide.loaders.DataObject;
39 import org.openide.loaders.DataObjectNotFoundException;
40 import org.openide.loaders.InstanceDataObject;
41 import org.openide.nodes.PropertySupport;
42 import org.openide.util.Lookup;
43 import org.openide.util.NbBundle;
44
45 import org.w3c.dom.*;
46 import org.w3c.dom.NodeList JavaDoc;
47 import org.xml.sax.EntityResolver JavaDoc;
48 import org.xml.sax.InputSource JavaDoc;
49 import org.xml.sax.SAXException JavaDoc;
50
51 /**
52  * This class implements code completion for XSL transformation files.
53  * XSL elements in the completion are hardcoded from the XSLT spec, but the
54  * result elements are gathered from the "doctype-public" and "doctype-system"
55  * attributes of the xsl:output element.
56  *
57  * @author asgeir@dimonsoftware.com
58  */

59 public final class XSLGrammarQuery implements GrammarQuery{
60
61     private DataObject dataObject;
62
63     private ScenarioCookie scenarioCookie;
64
65     /** Contains a mapping from XSL namespace element names to set of names of
66      * allowed XSL children. Neither the element name keys nor the names in the
67      * value set should contain the namespace prefix.
68      */

69     private static Map elementDecls;
70
71     /** Contains a mapping from XSL namespace element names to set of names of
72      * allowed XSL attributes for that element. The element name keys should
73      * not contain the namespace prefix.
74      */

75     private static Map attrDecls;
76
77     /** A Set of XSL attributes which should be allowd for result elements*/
78     private static Set resultElementAttr;
79
80     /** An object which indicates that result element should be allowed in a element Set */
81     private static String JavaDoc resultElements = "RESULT_ELEMENTS_DUMMY_STRING"; // NOI18N
82

83     /** A Set of elements which should be allowed at template level in XSL stylesheet */
84     private static Set template;
85
86     /** Contains a mapping from XSL namespace element names to an attribute name which
87      * should contain XPath expression. The element name keys should
88      * not contain the namespace prefix.
89      */

90     private static Map exprAttributes;
91
92     /** A set containing all functions allowed in XSLT */
93     private static Set xslFunctions;
94
95     /** A set containing XPath axes */
96     private static Set xpathAxes;
97
98     /** A list of prefixes using the "http://www.w3.org/1999/XSL/Transform" namespace
99      * defined in the context XSL document. The first prefix in the list is the actual XSL
100      * transformation prefix, which is normally defined on the xsl:stylesheet element.
101      */

102     private List JavaDoc prefixList = new LinkedList();
103
104     /** A GrammarQuery for the result elements created for the doctype-public" and
105      * "doctype-system" attributes of the xsl:output element.*/

106     private GrammarQuery resultGrammarQuery;
107
108     /** The value of the system identifier of the DTD which was used when
109      * resultGrammarQuery was previously created */

110     private String JavaDoc lastDoctypeSystem;
111
112     /** The value of the public identifier of the DTD which was used when
113      * resultGrammarQuery was previously created */

114     private String JavaDoc lastDoctypePublic;
115
116     // we cannot parse SGML DTD for HTML, let emulate it by XHTML DTD
117
private final static String JavaDoc XHTML_PUBLIC_ID =
118             System.getProperty("netbeans.xsl.html.public", "-//W3C//DTD XHTML 1.0 Transitional//EN"); // NOI18N
119

120     // we cannot parse SGML DTD for HTML, let emulate it by XHTML DTD
121
private final static String JavaDoc XHTML_SYSTEM_ID =
122             System.getProperty("netbeans.xsl.html.system", "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"); // NOI18N
123

124     // namespace that this grammar supports
125
public static final String JavaDoc XSLT_NAMESPACE_URI = "http://www.w3.org/1999/XSL/Transform"; // NOI18N
126

127     /** Folder which stores instances of custom external XSL customizers */
128     private static final String JavaDoc CUSTOMIZER_FOLDER = "Plugins/XML/XSLCustomizer"; // NOI18N
129

130     private XSLCustomizer customizer = null;
131
132     private ResourceBundle bundle = NbBundle.getBundle(XSLGrammarQuery.class);
133
134     /** Creates a new instance of XSLGrammarQuery */
135     public XSLGrammarQuery(DataObject dataObject) {
136         this.dataObject = dataObject;
137         scenarioCookie = (ScenarioCookie)dataObject.getCookie(ScenarioCookie.class);
138     }
139
140     //////////////////////////////////////////7
141
// Getters for the static members
142

143     private static Map getElementDecls() {
144         if (elementDecls == null) {
145             elementDecls = new HashMap();
146             attrDecls = new HashMap();
147
148             // Commonly used variables
149
Set emptySet = new TreeSet();
150             String JavaDoc spaceAtt = "xml:space"; // NOI18N
151
Set tmpSet;
152
153             ////////////////////////////////////////////////
154
// Initialize common sets
155

156             Set charInstructions = new TreeSet(Arrays.asList(new String JavaDoc[]{"apply-templates", // NOI18N
157
"call-template","apply-imports","for-each","value-of", // NOI18N
158
"copy-of","number","choose","if","text","copy", // NOI18N
159
"variable","message","fallback"})); // NOI18N
160

161             Set instructions = new TreeSet(charInstructions);
162             instructions.addAll(Arrays.asList(new String JavaDoc[]{"processing-instruction", // NOI18N
163
"comment","element","attribute"})); // NOI18N
164

165             Set charTemplate = charInstructions; // We don't care about PCDATA
166

167             template = new TreeSet(instructions);
168             template.add(resultElements);
169
170             Set topLevel = new TreeSet(Arrays.asList(new String JavaDoc[]{"import","include","strip-space", // NOI18N
171
"preserve-space","output","key","decimal-format","attribute-set", // NOI18N
172
"variable","param","template","namespace-alias"})); // NOI18N
173

174             Set topLevelAttr = new TreeSet(Arrays.asList(new String JavaDoc[]{"extension-element-prefixes", // NOI18N
175
"exclude-result-prefixes","id","version",spaceAtt})); // NOI18N
176

177             resultElementAttr = new TreeSet(Arrays.asList(new String JavaDoc[]{"extension-element-prefixes", // NOI18N
178
"exclude-result-prefixes","use-attribute-sets","version"})); // NOI18N
179

180             ////////////////////////////////////////////////
181
// Add items to elementDecls and attrDecls maps
182

183             // xsl:stylesheet
184
elementDecls.put("stylesheet", topLevel); // NOI18N
185
attrDecls.put("stylesheet", topLevelAttr); // NOI18N
186

187             // xsl:transform
188
elementDecls.put("transform", topLevel); // NOI18N
189
attrDecls.put("transform", topLevelAttr); // NOI18N
190

191             // xsl:import
192
elementDecls.put("import", emptySet); // NOI18N
193
attrDecls.put("import", new TreeSet(Arrays.asList(new String JavaDoc[]{"href"}))); // NOI18N
194

195             // xxsl:include
196
elementDecls.put("include", emptySet); // NOI18N
197
attrDecls.put("include", new TreeSet(Arrays.asList(new String JavaDoc[]{"href"}))); // NOI18N
198

199             // xsl:strip-space
200
elementDecls.put("strip-space", emptySet); // NOI18N
201
attrDecls.put("strip-space", new TreeSet(Arrays.asList(new String JavaDoc[]{"elements"}))); // NOI18N
202

203             // xsl:preserve-space
204
elementDecls.put("preserve-space", emptySet); // NOI18N
205
attrDecls.put("preserve-space", new TreeSet(Arrays.asList(new String JavaDoc[]{"elements"}))); // NOI18N
206

207             // xsl:output
208
elementDecls.put("output", emptySet); // NOI18N
209
attrDecls.put("output", new TreeSet(Arrays.asList(new String JavaDoc[]{"method", // NOI18N
210
"version","encoding","omit-xml-declaration","standalone","doctype-public", // NOI18N
211
"doctype-system","cdata-section-elements","indent","media-type"}))); // NOI18N
212

213             // xsl:key
214
elementDecls.put("key", emptySet); // NOI18N
215
attrDecls.put("key", new TreeSet(Arrays.asList(new String JavaDoc[]{"name","match","use"}))); // NOI18N
216

217             // xsl:decimal-format
218
elementDecls.put("decimal-format", emptySet); // NOI18N
219
attrDecls.put("decimal-format", new TreeSet(Arrays.asList(new String JavaDoc[]{"name", // NOI18N
220
"decimal-separator","grouping-separator","infinity","minus-sign","NaN", // NOI18N
221
"percent","per-mille","zero-digit","digit","pattern-separator"}))); // NOI18N
222

223             // xsl:namespace-alias
224
elementDecls.put("namespace-alias", emptySet); // NOI18N
225
attrDecls.put("namespace-alias", new TreeSet(Arrays.asList(new String JavaDoc[]{ // NOI18N
226
"stylesheet-prefix","result-prefix"}))); // NOI18N
227

228             // xsl:template
229
tmpSet = new TreeSet(instructions);
230             tmpSet.add(resultElements);
231             tmpSet.add("param"); // NOI18N
232
elementDecls.put("template", tmpSet); // NOI18N
233
attrDecls.put("template", new TreeSet(Arrays.asList(new String JavaDoc[]{ // NOI18N
234
"match","name","priority","mode",spaceAtt}))); // NOI18N
235

236             // xsl:value-of
237
elementDecls.put("value-of", emptySet); // NOI18N
238
attrDecls.put("value-of", new TreeSet(Arrays.asList(new String JavaDoc[]{ // NOI18N
239
"select","disable-output-escaping"}))); // NOI18N
240

241             // xsl:copy-of
242
elementDecls.put("copy-of", emptySet); // NOI18N
243
attrDecls.put("copy-of", new TreeSet(Arrays.asList(new String JavaDoc[]{"select"}))); // NOI18N
244

245             // xsl:number
246
elementDecls.put("number", emptySet); // NOI18N
247
attrDecls.put("number", new TreeSet(Arrays.asList(new String JavaDoc[]{ // NOI18N
248
"level","count","from","value","format","lang","letter-value", // NOI18N
249
"grouping-separator","grouping-size"}))); // NOI18N
250

251             // xsl:apply-templates
252
elementDecls.put("apply-templates", new TreeSet(Arrays.asList(new String JavaDoc[]{ // NOI18N
253
"sort","with-param"}))); // NOI18N
254
attrDecls.put("apply-templates", new TreeSet(Arrays.asList(new String JavaDoc[]{ // NOI18N
255
"select","mode"}))); // NOI18N
256

257             // xsl:apply-imports
258
elementDecls.put("apply-imports", emptySet); // NOI18N
259
attrDecls.put("apply-imports", emptySet); // NOI18N
260

261             // xsl:for-each
262
tmpSet = new TreeSet(instructions);
263             tmpSet.add(resultElements);
264             tmpSet.add("sort"); // NOI18N
265
elementDecls.put("for-each", tmpSet); // NOI18N
266
attrDecls.put("for-each", new TreeSet(Arrays.asList(new String JavaDoc[]{ // NOI18N
267
"select",spaceAtt}))); // NOI18N
268

269             // xsl:sort
270
elementDecls.put("sort", emptySet); // NOI18N
271
attrDecls.put("sort", new TreeSet(Arrays.asList(new String JavaDoc[]{ // NOI18N
272
"select","lang","data-type","order","case-order"}))); // NOI18N
273

274             // xsl:if
275
elementDecls.put("if", template); // NOI18N
276
attrDecls.put("if", new TreeSet(Arrays.asList(new String JavaDoc[]{"test",spaceAtt}))); // NOI18N
277

278             // xsl:choose
279
elementDecls.put("choose", new TreeSet(Arrays.asList(new String JavaDoc[]{ // NOI18N
280
"when","otherwise"}))); // NOI18N
281
attrDecls.put("choose", new TreeSet(Arrays.asList(new String JavaDoc[]{spaceAtt}))); // NOI18N
282

283             // xsl:when
284
elementDecls.put("when", template); // NOI18N
285
attrDecls.put("when", new TreeSet(Arrays.asList(new String JavaDoc[]{ // NOI18N
286
"test",spaceAtt}))); // NOI18N
287

288             // xsl:otherwise
289
elementDecls.put("otherwise", template); // NOI18N
290
attrDecls.put("otherwise", new TreeSet(Arrays.asList(new String JavaDoc[]{spaceAtt}))); // NOI18N
291

292             // xsl:attribute-set
293
elementDecls.put("sort", new TreeSet(Arrays.asList(new String JavaDoc[]{"attribute"}))); // NOI18N
294
attrDecls.put("attribute-set", new TreeSet(Arrays.asList(new String JavaDoc[]{ // NOI18N
295
"name","use-attribute-sets"}))); // NOI18N
296

297             // xsl:call-template
298
elementDecls.put("call-template", new TreeSet(Arrays.asList(new String JavaDoc[]{"with-param"}))); // NOI18N
299
attrDecls.put("call-template", new TreeSet(Arrays.asList(new String JavaDoc[]{"name"}))); // NOI18N
300

301             // xsl:with-param
302
elementDecls.put("with-param", template); // NOI18N
303
attrDecls.put("with-param", new TreeSet(Arrays.asList(new String JavaDoc[]{ // NOI18N
304
"name","select"}))); // NOI18N
305

306             // xsl:variable
307
elementDecls.put("variable", template); // NOI18N
308
attrDecls.put("variable", new TreeSet(Arrays.asList(new String JavaDoc[]{ // NOI18N
309
"name","select"}))); // NOI18N
310

311             // xsl:param
312
elementDecls.put("param", template); // NOI18N
313
attrDecls.put("param", new TreeSet(Arrays.asList(new String JavaDoc[]{ // NOI18N
314
"name","select"}))); // NOI18N
315

316             // xsl:text
317
elementDecls.put("text", emptySet); // NOI18N
318
attrDecls.put("text", new TreeSet(Arrays.asList(new String JavaDoc[]{ // NOI18N
319
"disable-output-escaping"}))); // NOI18N
320

321             // xsl:processing-instruction
322
elementDecls.put("processing-instruction", charTemplate); // NOI18N
323
attrDecls.put("processing-instruction", new TreeSet(Arrays.asList(new String JavaDoc[]{ // NOI18N
324
"name",spaceAtt}))); // NOI18N
325

326             // xsl:element
327
elementDecls.put("element", template); // NOI18N
328
attrDecls.put("element", new TreeSet(Arrays.asList(new String JavaDoc[]{ // NOI18N
329
"name","namespace","use-attribute-sets",spaceAtt}))); // NOI18N
330

331             // xsl:attribute
332
elementDecls.put("attribute", charTemplate); // NOI18N
333
attrDecls.put("attribute", new TreeSet(Arrays.asList(new String JavaDoc[]{ // NOI18N
334
"name","namespace",spaceAtt}))); // NOI18N
335

336             // xsl:comment
337
elementDecls.put("comment", charTemplate); // NOI18N
338
attrDecls.put("comment", new TreeSet(Arrays.asList(new String JavaDoc[]{spaceAtt}))); // NOI18N
339

340             // xsl:copy
341
elementDecls.put("copy", template); // NOI18N
342
attrDecls.put("copy", new TreeSet(Arrays.asList(new String JavaDoc[]{ // NOI18N
343
spaceAtt,"use-attribute-sets"}))); // NOI18N
344

345             // xsl:message
346
elementDecls.put("message", template); // NOI18N
347
attrDecls.put("message", new TreeSet(Arrays.asList(new String JavaDoc[]{ // NOI18N
348
spaceAtt,"terminate"}))); // NOI18N
349

350             // xsl:fallback
351
elementDecls.put("fallback", template); // NOI18N
352
attrDecls.put("fallback", new TreeSet(Arrays.asList(new String JavaDoc[]{spaceAtt}))); // NOI18N
353
}
354         return elementDecls;
355     }
356
357     private static Map getAttrDecls() {
358         if (attrDecls == null) {
359             getElementDecls();
360         }
361         return attrDecls;
362     }
363
364     private static Set getResultElementAttr() {
365         if (resultElementAttr == null) {
366             getElementDecls();
367         }
368         return resultElementAttr;
369     }
370
371     private static Set getTemplate() {
372         if (template == null) {
373             getElementDecls();
374         }
375         return template;
376     }
377
378     private static Set getXslFunctions() {
379         if (xslFunctions == null) {
380             xslFunctions = new TreeSet(Arrays.asList(new String JavaDoc[]{
381                 "boolean(","ceiling(","concat(", "contains(","count(","current()","document(", // NOI18N
382
"false()", "floor(","format-number(","generate-id(", // NOI18N
383
"id(","local-name(","key(","lang(","last()","name(","namespace-uri(", "normalize-space(", // NOI18N
384
"not(","number(","position()","round(","starts-with(","string(", // NOI18N
385
"string-length(", "substring(","substring-after(","substring-before(", "sum(", // NOI18N
386
"system-property(","translate(", "true()","unparsed-entity-uri("})); // NOI18N
387
}
388         return xslFunctions;
389     }
390
391     private static Set getXPathAxes() {
392         if (xpathAxes == null) {
393             xpathAxes = new TreeSet(Arrays.asList(new String JavaDoc[]{"ancestor::", "ancestor-or-self::", // NOI18N
394
"attribute::", "child::", "descendant::", "descendant-or-self::", "following::", // NOI18N
395
"following-sibling::", "namespace::", "parent::", "preceding::", // NOI18N
396
"preceding-sibling::", "self::"})); // NOI18N
397
}
398         return xpathAxes;
399     }
400
401     private static Map getExprAttributes() {
402         if (exprAttributes == null) {
403             exprAttributes = new HashMap();
404             exprAttributes.put("key", "use"); // NOI18N
405
exprAttributes.put("value-of", "select"); // NOI18N
406
exprAttributes.put("copy-of", "select"); // NOI18N
407
exprAttributes.put("number", "value"); // NOI18N
408
//??? what about match one
409
exprAttributes.put("apply-templates", "select"); // NOI18N
410
exprAttributes.put("for-each", "select"); // NOI18N
411
exprAttributes.put("sort", "select"); // NOI18N
412
exprAttributes.put("if", "test"); // NOI18N
413
exprAttributes.put("when", "test"); // NOI18N
414
exprAttributes.put("with-param", "select"); // NOI18N
415
exprAttributes.put("variable", "select"); // NOI18N
416
exprAttributes.put("param", "select"); // NOI18N
417
}
418         return exprAttributes;
419     }
420
421
422
423     ////////////////////////////////////////////////////////////////////////////////
424
// GrammarQuery interface fulfillment
425

426     /**
427      * Support completions of elements defined by XSLT spec and by the <output>
428      * doctype attribute (in result space).
429      */

430     public Enumeration queryElements(HintContext ctx) {
431         Node node = ((Node)ctx).getParentNode();
432
433         String JavaDoc prefix = ctx.getCurrentPrefix();
434         QueueEnumeration list = new QueueEnumeration();
435
436         if (node instanceof Element) {
437             Element el = (Element) node;
438             updateProperties(el);
439             if (prefixList.size() == 0) return org.openide.util.Enumerations.empty();
440
441             String JavaDoc firstXslPrefixWithColon = prefixList.get(0) + ":"; // NOI18N
442
Set elements;
443             if (el.getTagName().startsWith(firstXslPrefixWithColon)) {
444                 String JavaDoc parentNCName = el.getTagName().substring(firstXslPrefixWithColon.length());
445                 elements = (Set) getElementDecls().get(parentNCName);
446             } else {
447                 // Children of result elements should always be the template set
448
elements = getTemplate();
449             }
450
451             // First we add the Result elements
452
if (elements != null && resultGrammarQuery != null && elements.contains(resultElements)) {
453                 ResultHintContext resultHintContext = new ResultHintContext(ctx, firstXslPrefixWithColon, null);
454                 Enumeration resultEnum = resultGrammarQuery.queryElements(resultHintContext);
455                 while (resultEnum.hasMoreElements()) {
456                     list.put(resultEnum.nextElement());
457                 }
458             }
459
460             // Then we add the XSLT elements of the first prefix (normally of the stylesheet node).
461
addXslElementsToEnum(list, elements, prefixList.get(0) + ":", prefix); // NOI18N
462

463             // Finally we add xsl namespace elements with other prefixes than the first one
464
for (int prefixInd = 1; prefixInd < prefixList.size(); prefixInd++) {
465                 String JavaDoc curPrefix = (String JavaDoc)prefixList.get(prefixInd) + ":"; // NOI18N
466
Node curNode = el;
467                 String JavaDoc curName = null;
468                 while(curNode != null && null != (curName = curNode.getNodeName()) && !curName.startsWith(curPrefix)) {
469                     curNode = curNode.getParentNode();
470                 }
471
472                 if (curName == null) {
473                     // This must be the document node
474
addXslElementsToEnum(list, getElementDecls().keySet(), curPrefix, prefix);
475                 } else {
476                     String JavaDoc parentName = curName.substring(curPrefix.length());
477                     elements = (Set) getElementDecls().get(parentName);
478                     addXslElementsToEnum(list, elements, curPrefix, prefix);
479                 }
480             }
481
482         } else if (node instanceof Document) {
483             //??? it should be probably only root element name
484
if (prefixList.size() == 0) return org.openide.util.Enumerations.empty();
485             addXslElementsToEnum(list, getElementDecls().keySet(), prefixList.get(0) + ":", prefix); // NOI18N
486
} else {
487             return org.openide.util.Enumerations.empty();
488         }
489
490         return list;
491     }
492
493     public Enumeration queryAttributes(HintContext ctx) {
494         Element el = null;
495         // Support two versions of GrammarQuery contract
496
if (ctx.getNodeType() == Node.ATTRIBUTE_NODE) {
497             el = ((Attr)ctx).getOwnerElement();
498         } else if (ctx.getNodeType() == Node.ELEMENT_NODE) {
499             el = (Element) ctx;
500         }
501         if (el == null) return org.openide.util.Enumerations.empty();
502
503         String JavaDoc elTagName = el.getTagName();
504         NamedNodeMap existingAttributes = el.getAttributes();
505
506         updateProperties(el);
507
508
509         String JavaDoc curXslPrefix = null;
510         for (int ind = 0; ind < prefixList.size(); ind++) {
511             if (elTagName.startsWith((String JavaDoc)prefixList.get(ind) + ":")){ // NOI18N
512
curXslPrefix = (String JavaDoc)prefixList.get(ind) + ":"; // NOI18N
513
break;
514             }
515         }
516
517         Set possibleAttributes;
518         if (curXslPrefix != null) {
519             // Attributes of XSL element
520
possibleAttributes = (Set) getAttrDecls().get(el.getTagName().substring(curXslPrefix.length()));
521         } else {
522             // XSL Attributes of Result element
523
possibleAttributes = new TreeSet();
524             if (prefixList.size() > 0) {
525                 Iterator it = getResultElementAttr().iterator();
526                 while ( it.hasNext()) {
527                     possibleAttributes.add((String JavaDoc)prefixList.get(0) + ":" + (String JavaDoc) it.next()); // NOI18N
528
}
529             }
530         }
531         if (possibleAttributes == null) return org.openide.util.Enumerations.empty();
532
533         String JavaDoc prefix = ctx.getCurrentPrefix();
534
535         QueueEnumeration list = new QueueEnumeration();
536
537         if (resultGrammarQuery != null) {
538             Enumeration enum2 = resultGrammarQuery.queryAttributes(ctx);
539             while(enum2.hasMoreElements()) {
540                 GrammarResult resNode = (GrammarResult)enum2.nextElement();
541                 if (!possibleAttributes.contains(resNode.getNodeName())) {
542                     list.put(resNode);
543                 }
544             }
545         }
546
547         Iterator it = possibleAttributes.iterator();
548         while ( it.hasNext()) {
549             String JavaDoc next = (String JavaDoc) it.next();
550             if (next.startsWith(prefix)) {
551                 if (existingAttributes.getNamedItem(next) == null) {
552                     list.put(new MyAttr(next));
553                 }
554             }
555         }
556
557         return list;
558     }
559
560     public Enumeration queryValues(HintContext ctx) {
561        if (ctx.getNodeType() == Node.ATTRIBUTE_NODE) {
562             updateProperties(((Attr)ctx).getOwnerElement());
563             if (prefixList.size() == 0) return org.openide.util.Enumerations.empty();
564             String JavaDoc xslNamespacePrefix = prefixList.get(0) + ":"; // NOI18N
565

566             String JavaDoc prefix = ctx.getCurrentPrefix();
567
568             Attr attr = (Attr)ctx;
569
570             boolean isXPath = false;
571             String JavaDoc elName = attr.getOwnerElement().getNodeName();
572             if (elName.startsWith(xslNamespacePrefix)) {
573                 String JavaDoc key = elName.substring(xslNamespacePrefix.length());
574                 String JavaDoc xpathAttrName = (String JavaDoc)getExprAttributes().get(key);
575                 if (xpathAttrName != null && xpathAttrName.equals(attr.getNodeName())) {
576                     // This is an XSLT element which should contain XPathExpression
577
isXPath = true;
578                 }
579
580                 // consult awailable public IDs with users catalog
581
if ("output".equals(key)) { // NOI18N
582
if ("doctype-public".equals(attr.getName())) { // NOI18N
583
UserCatalog catalog = UserCatalog.getDefault();
584                         if (catalog == null) return org.openide.util.Enumerations.empty();
585                         QueueEnumeration en = new QueueEnumeration();
586                         Iterator it = catalog.getPublicIDs();
587                         while (it.hasNext()) {
588                             String JavaDoc next = (String JavaDoc) it.next();
589                             if (next != null && next.startsWith(prefix)) {
590                                 en.put(new MyText(next));
591                             }
592                         }
593                         return en;
594                     }
595                 }
596             }
597
598             String JavaDoc preExpression = ""; // NOI18N
599

600             if (!isXPath) {
601                 // Check if we are inside { } for attribute value
602
String JavaDoc nodeValue = attr.getNodeValue();
603                 int exprStart = nodeValue.lastIndexOf('{', prefix.length() - 1); // NOI18N
604
int exprEnd = nodeValue.indexOf('}', prefix.length()); // NOI18N
605
Util.THIS.debug("exprStart: " + exprStart); // NOI18N
606
Util.THIS.debug("exprEnd: " + exprEnd); // NOI18N
607
if (exprStart != -1 && exprEnd != -1) {
608                     isXPath = true;
609                     preExpression = prefix.substring(0, exprStart + 1);
610                     prefix = prefix.substring(exprStart + 1);
611                 }
612
613             }
614
615             if (isXPath) {
616                 // This is an XPath expression
617
QueueEnumeration list = new QueueEnumeration();
618
619                 int curIndex = prefix.length();
620                 while (curIndex > 0) {
621                     curIndex--;
622                     char curChar = prefix.charAt(curIndex);
623                     if (curChar == '(' || curChar == ',' || curChar == ' ') { // NOI18N
624
curIndex++;
625                         break;
626                     }
627                 }
628
629                 preExpression += prefix.substring(0, curIndex);
630                 String JavaDoc subExpression = prefix.substring(curIndex);
631
632                 int lastDiv = subExpression.lastIndexOf('/'); // NOI18N
633
String JavaDoc subPre = ""; // NOI18N
634
String JavaDoc subRest = ""; // NOI18N
635
if (lastDiv != -1) {
636                     subPre = subExpression.substring(0, lastDiv + 1);
637                     subRest = subExpression.substring(lastDiv + 1);
638                 } else {
639                     subRest = subExpression;
640                 }
641
642                 // At this point we need to consult transformed document or
643
// its grammar.
644
// [93792] +
645
// Object selScenarioObj = scenarioCookie.getModel().getSelectedItem();
646
// [93792] -
647
/*
648                 if (selScenarioObj instanceof XSLScenario) {
649                     XSLScenario scenario = (XSLScenario)selScenarioObj;
650                     Document doc = null;
651                     try {
652                         doc = scenario.getSourceDocument(dataObject, false);
653                     } catch(Exception e) {
654                         // We don't care, ignore
655                     }
656
657                     if (doc != null) {
658                         Element docElement = doc.getDocumentElement();
659
660                         Set childNodeNames = new TreeSet();
661
662                         String combinedXPath;
663                         if (subPre.startsWith("/")) { // NOI18N
664                             // This is an absolute XPath
665                             combinedXPath = subPre;
666                         } else {
667                             // This is a relative XPath
668
669                             // Traverse up the documents tree looking for xsl:for-each
670                             String xslForEachName = xslNamespacePrefix + "for-each"; // NOI18N
671                             List selectAttrs = new LinkedList();
672                             Node curNode = attr.getOwnerElement();
673                             if (curNode != null) {
674                                 // We don't want to add select of our selfs
675                                 curNode = curNode.getParentNode();
676                             }
677
678                             while (curNode != null && !(curNode instanceof Document)) {
679                                 if (curNode.getNodeName().equals(xslForEachName)) {
680                                     selectAttrs.add(0, ((Element)curNode).getAttribute("select")); // NOI18N
681                                 }
682
683                                 curNode = curNode.getParentNode();
684                             }
685
686                             combinedXPath = ""; // NOI18N
687                             for (int ind = 0; ind < selectAttrs.size(); ind++) {
688                                 combinedXPath += selectAttrs.get(ind) + "/"; // NOI18N
689                             }
690                             combinedXPath += subPre;
691                         }
692
693                         try {
694                             NodeList nodeList = XPathAPI.selectNodeList(doc, combinedXPath + "child::*"); // NOI18N
695                             for (int ind = 0; ind < nodeList.getLength(); ind++) {
696                                 Node curResNode = nodeList.item(ind);
697                                 childNodeNames.add(curResNode.getNodeName());
698                             }
699
700                             nodeList = XPathAPI.selectNodeList(doc, combinedXPath + "@*"); // NOI18N
701                             for (int ind = 0; ind < nodeList.getLength(); ind++) {
702                                 Node curResNode = nodeList.item(ind);
703                                 childNodeNames.add("@" + curResNode.getNodeName()); // NOI18N
704                             }
705                         } catch (Exception e) {
706                             Util.THIS.debug("Ignored during XPathAPI operations", e); // NOI18N
707                             // We don't care, ignore
708                         }
709
710                         addItemsToEnum(list, childNodeNames, subRest, preExpression + subPre);
711                     }
712                 }*/

713
714                 addItemsToEnum(list, getXPathAxes(), subRest, preExpression + subPre);
715                 addItemsToEnum(list, getXslFunctions(), subExpression, preExpression);
716
717                 return list;
718             }
719         }
720
721         return org.openide.util.Enumerations.empty();
722     }
723
724     public GrammarResult queryDefault(HintContext ctx) {
725         //??? XSLT defaults are missing
726
if (resultGrammarQuery == null) return null;
727         return resultGrammarQuery.queryDefault(ctx);
728     }
729
730     public boolean isAllowed(Enumeration en) {
731         return true; //!!! not implemented
732
}
733
734     public Enumeration queryEntities(String JavaDoc prefix) {
735         QueueEnumeration list = new QueueEnumeration();
736
737         // add well-know build-in entity names
738

739         if ("lt".startsWith(prefix)) list.put(new MyEntityReference("lt")); // NOI18N
740
if ("gt".startsWith(prefix)) list.put(new MyEntityReference("gt")); // NOI18N
741
if ("apos".startsWith(prefix)) list.put(new MyEntityReference("apos")); // NOI18N
742
if ("quot".startsWith(prefix)) list.put(new MyEntityReference("quot")); // NOI18N
743
if ("amp".startsWith(prefix)) list.put(new MyEntityReference("amp")); // NOI18N
744

745         return list;
746     }
747
748     public Enumeration queryNotations(String JavaDoc prefix) {
749         return org.openide.util.Enumerations.empty();
750     }
751
752     public java.awt.Component JavaDoc getCustomizer(HintContext ctx) {
753         if (customizer == null) {
754             customizer = lookupCustomizerInstance();
755             if (customizer == null) {
756                 return null;
757             }
758         }
759
760         return customizer.getCustomizer(ctx, dataObject);
761     }
762
763     public boolean hasCustomizer(HintContext ctx) {
764         if (customizer == null) {
765             customizer = lookupCustomizerInstance();
766             if (customizer == null) {
767                 return false;
768             }
769         }
770
771         return customizer.hasCustomizer(ctx);
772     }
773
774     public org.openide.nodes.Node.Property[] getProperties(final HintContext ctx) {
775
776         if (ctx.getNodeType() != Node.ATTRIBUTE_NODE || ctx.getNodeValue() == null) {
777             return null;
778         }
779
780         PropertySupport attrNameProp = new PropertySupport("Attribute name", String JavaDoc.class, // NOI18N
781
bundle.getString("BK0001"), bundle.getString("BK0002"), true, false) {
782             public void setValue(Object JavaDoc value) {
783                 // Dummy
784
}
785             public Object JavaDoc getValue() {
786                 return ctx.getNodeName();
787             }
788
789         };
790
791         PropertySupport attrValueProp = new PropertySupport("Attribute value", String JavaDoc.class, // NOI18N
792
bundle.getString("BK0003"), bundle.getString("BK0004"), true, true) {
793             public void setValue(Object JavaDoc value) {
794                 ctx.setNodeValue((String JavaDoc)value);
795             }
796             public Object JavaDoc getValue() {
797                 return ctx.getNodeValue();
798             }
799
800         };
801
802         return new org.openide.nodes.Node.Property[]{attrNameProp, attrValueProp};
803     }
804
805     ////////////////////////////////////////////////////////////////////////////////
806
// Private helper methods
807

808     /**
809      * Looks up registered XSLCustomizer objects which will be used by this object
810      */

811     private static XSLCustomizer lookupCustomizerInstance() {
812         try {
813             // Load the XSLCustomizer from the XML layer
814
FileSystem fs = Repository.getDefault().getDefaultFileSystem();
815             FileObject fo = fs.findResource(CUSTOMIZER_FOLDER);
816             if (fo == null) return null;
817             DataObject df = DataObject.find(fo);
818             if (!(df instanceof DataObject.Container)) {
819                 return null;
820             }
821
822             // ??? What is it? Should not we use naming instead?
823

824             FolderLookup lookup =
825                 new FolderLookup((DataObject.Container) df);
826             Lookup.Template template =
827                 new Lookup.Template(XSLCustomizer.class);
828
829             Lookup.Item lookupItem = lookup.getLookup().lookupItem(template);
830             if (lookupItem == null) {
831                 return null;
832             }
833
834             return (XSLCustomizer)lookupItem.getInstance();
835         } catch (DataObjectNotFoundException e) {
836             return null;
837         }
838     }
839
840     /**
841      * @param enumX the Enumeration which the element should be added to
842      * @param elements a set containing strings which should be added (with prefix) to the enum or <code>null</null>
843      * @param namespacePrefix a prefix at the form "xsl:" which should be added in front
844      * of the names in the elements.
845      * @param startWith Elements should only be added to enum if they start with this string
846      */

847     private static void addXslElementsToEnum(QueueEnumeration enumX, Set elements, String JavaDoc namespacePrefix, String JavaDoc startWith) {
848         if (elements == null) return;
849         if (startWith.startsWith(namespacePrefix) || namespacePrefix.startsWith(startWith)) {
850             Iterator it = elements.iterator();
851             while ( it.hasNext()) {
852                 Object JavaDoc next = it.next();
853                 if (next != resultElements) {
854                     String JavaDoc nextText = namespacePrefix + (String JavaDoc)next;
855                     if (nextText.startsWith(startWith)) {
856                         // TODO pass true for empty elements
857
enumX.put(new MyElement(nextText, false));
858                     }
859                 }
860             }
861         }
862     }
863
864     private static void addItemsToEnum(QueueEnumeration enumX, Set set, String JavaDoc startWith, String JavaDoc prefix) {
865         Iterator it = set.iterator();
866         while ( it.hasNext()) {
867             String JavaDoc nextText = (String JavaDoc)it.next();
868             if (nextText.startsWith(startWith)) {
869                 enumX.put(new MyText(prefix + nextText));
870             }
871         }
872     }
873
874     /**
875      * This method traverses up the document tree, investigates it and updates
876      * prefixList, resultGrammarQuery, lastDoctypeSystem or lastDoctypePublic
877      * members if necessery.
878      * @param curNode the node which from wich the traversing should start.
879      */

880     private void updateProperties(Node curNode) {
881         prefixList.clear();
882
883         // Traverse up the documents tree
884
Node rootNode = curNode;
885         while (curNode != null && !(curNode instanceof Document)) {
886
887             // Update the xsl namespace prefix list
888
NamedNodeMap attributes = curNode.getAttributes();
889             for (int ind = 0; ind < attributes.getLength(); ind++) {
890                 Attr attr = (Attr)attributes.item(ind);
891                 String JavaDoc attrName = attr.getName();
892                 if (attrName != null && attrName.startsWith("xmlns:")) { // NOI18N
893
if (attr.getValue().equals(XSLT_NAMESPACE_URI)) {
894                         prefixList.add(0, attrName.substring(6));
895                     }
896                 }
897             }
898
899
900             rootNode = curNode;
901             curNode = rootNode.getParentNode();
902         }
903
904         boolean outputFound = false;
905         if (prefixList.size() > 0) {
906             String JavaDoc outputElName = (String JavaDoc)prefixList.get(0) + ":output"; // NOI18N
907
Node childOfRoot = rootNode.getFirstChild();
908             while (childOfRoot != null) {
909                 String JavaDoc childNodeName = childOfRoot.getNodeName();
910                 if (childNodeName != null && childNodeName.equals(outputElName)) {
911                     Element outputEl = (Element)childOfRoot;
912                     String JavaDoc outputMethod = outputEl.getAttribute("method"); // NOI18N
913

914                     String JavaDoc curDoctypePublic = outputEl.getAttribute("doctype-public"); // NOI18N
915
String JavaDoc curDoctypeSystem = outputEl.getAttribute("doctype-system"); // NOI18N
916

917                     if ("html".equals(outputMethod) // NOI18N
918
&& (curDoctypePublic == null || curDoctypePublic.length() == 0)
919                         && (curDoctypeSystem == null || curDoctypeSystem.length() == 0)) { // NOI18N
920
// html is special case that can be emulated using XHTML
921
curDoctypePublic = XHTML_PUBLIC_ID;
922                         curDoctypeSystem = XHTML_SYSTEM_ID;
923                     } else if ("text".equals(outputMethod)) { // NOI18N
924
// user error, ignore
925
break;
926                     }
927
928                     if (curDoctypePublic != null && !curDoctypePublic.equals(lastDoctypePublic) ||
929                     curDoctypePublic == null && lastDoctypePublic != null ||
930                     curDoctypeSystem != null && !curDoctypeSystem.equals(lastDoctypeSystem) ||
931                     curDoctypeSystem == null && lastDoctypeSystem != null) {
932                         setOutputDoctype(curDoctypePublic, curDoctypeSystem);
933                     }
934
935                     outputFound = true;
936                     break;
937                 }
938                 childOfRoot = childOfRoot.getNextSibling();
939             }
940         }
941
942         if (!outputFound) {
943             setOutputDoctype(null, null);
944         }
945     }
946
947     /**
948      * Updates resultGrammarQuery by parsing the DTD specified by publicId and
949      * systemId. lastDoctypeSystem and lastDoctypePublic are assigned to the new values.
950      * @param publicId the public identifier of the DTD
951      * @param publicId the system identifier of the DTD
952      */

953     private void setOutputDoctype(String JavaDoc publicId, String JavaDoc systemId) {
954         lastDoctypePublic = publicId;
955         lastDoctypeSystem = systemId;
956
957         if (publicId == null && systemId == null) {
958             resultGrammarQuery = null;
959             return;
960         }
961
962         InputSource JavaDoc inputSource = null;
963         UserCatalog catalog = UserCatalog.getDefault();
964         if (catalog != null) {
965             EntityResolver JavaDoc resolver = catalog.getEntityResolver();
966             if (resolver != null) {
967                 try {
968                     inputSource = resolver.resolveEntity(publicId, systemId);
969                 } catch(SAXException JavaDoc e) {
970                 } catch(IOException JavaDoc e) {
971                 } // Will be handled below
972
}
973         }
974
975         if (inputSource == null) {
976             try {
977                 java.net.URL JavaDoc url = new java.net.URL JavaDoc(systemId);
978                 inputSource = new InputSource JavaDoc(url.openStream());
979                 inputSource.setPublicId(publicId);
980                 inputSource.setSystemId(systemId);
981             } catch(IOException JavaDoc e) {
982                 resultGrammarQuery = null;
983                 return;
984             }
985         }
986
987         resultGrammarQuery = DTDUtil.parseDTD(true, inputSource);
988
989     }
990
991     ////////////////////////////////////////////////////////////////////////////////
992
// Private helper classes
993

994     private class ResultHintContext extends ResultNode implements HintContext {
995         private String JavaDoc currentPrefix;
996
997         public ResultHintContext(HintContext peer, String JavaDoc ignorePrefix, String JavaDoc onlyUsePrefix) {
998             super(peer, ignorePrefix, onlyUsePrefix);
999             currentPrefix = peer.getCurrentPrefix();
1000        }
1001
1002        public String JavaDoc getCurrentPrefix() {
1003            return currentPrefix;
1004        }
1005    }
1006
1007    // Result classes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1008

1009
1010    private static abstract class AbstractResultNode extends AbstractNode implements GrammarResult {
1011
1012        public Icon JavaDoc getIcon(int kind) {
1013            return null;
1014        }
1015
1016        /**
1017         * @return provide additional information simplifiing decision
1018         */

1019        public String JavaDoc getDescription() {
1020            return NbBundle.getMessage(XSLGrammarQuery.class, "BK0005");
1021        }
1022
1023        /**
1024         * @return text representing name of suitable entity
1025         * //??? is it really needed
1026         */

1027        public String JavaDoc getText() {
1028            return getNodeName();
1029        }
1030
1031        /**
1032         * @return name that is presented to user
1033         */

1034        public String JavaDoc getDisplayName() {
1035            return null;
1036        }
1037
1038        public boolean isEmptyElement() {
1039            return false;
1040        }
1041
1042    }
1043
1044    private static class MyEntityReference extends AbstractResultNode implements EntityReference {
1045
1046        private String JavaDoc name;
1047
1048        MyEntityReference(String JavaDoc name) {
1049            this.name = name;
1050        }
1051
1052        public short getNodeType() {
1053            return Node.ENTITY_REFERENCE_NODE;
1054        }
1055
1056        public String JavaDoc getNodeName() {
1057            return name;
1058        }
1059
1060    }
1061
1062    private static class MyElement extends AbstractResultNode implements Element {
1063
1064        private String JavaDoc name;
1065        private boolean empty;
1066
1067        MyElement(String JavaDoc name, boolean empty) {
1068            this.name = name;
1069            this.empty = empty;
1070        }
1071
1072        public short getNodeType() {
1073            return Node.ELEMENT_NODE;
1074        }
1075
1076        public String JavaDoc getNodeName() {
1077            return name;
1078        }
1079
1080        public String JavaDoc getTagName() {
1081            return name;
1082        }
1083
1084        public boolean isEmptyElement() {
1085            return empty;
1086        }
1087
1088    }
1089
1090    private static class MyAttr extends AbstractResultNode implements Attr {
1091
1092        private String JavaDoc name;
1093
1094        MyAttr(String JavaDoc name) {
1095            this.name = name;
1096        }
1097
1098        public short getNodeType() {
1099            return Node.ATTRIBUTE_NODE;
1100        }
1101
1102        public String JavaDoc getNodeName() {
1103            return name;
1104        }
1105
1106        public String JavaDoc getName() {
1107            return name;
1108        }
1109
1110        public String JavaDoc getValue() {
1111            return null; //??? what spec says
1112
}
1113
1114
1115    }
1116
1117
1118    private static class MyText extends AbstractResultNode implements Text {
1119
1120        private String JavaDoc data;
1121
1122        MyText(String JavaDoc data) {
1123            this.data = data;
1124        }
1125
1126        public short getNodeType() {
1127            return Node.TEXT_NODE;
1128        }
1129
1130        public String JavaDoc getNodeValue() {
1131            return getData();
1132        }
1133
1134        public String JavaDoc getData() throws DOMException {
1135            return data;
1136        }
1137
1138        public int getLength() {
1139            return data == null ? -1 : data.length();
1140        }
1141    }
1142
1143    private static class QueueEnumeration implements Enumeration {
1144        private java.util.LinkedList JavaDoc list = new LinkedList ();
1145        
1146        public boolean hasMoreElements () {
1147            return !list.isEmpty ();
1148        }
1149        
1150        public Object JavaDoc nextElement () {
1151            return list.removeFirst ();
1152        }
1153
1154        public void put (Object JavaDoc[] arr) {
1155            list.addAll (Arrays.asList (arr));
1156        }
1157        public void put (Object JavaDoc o) {
1158            list.add (o);
1159        }
1160        
1161    } // end of QueueEnumeration
1162
}
1163
Popular Tags