alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 *
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 *
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.netbeans.modules.xsl.grammar;

import java.io.IOException;
import java.util.*;
import javax.swing.Icon;
//import org.apache.xpath.XPathAPI;

import org.netbeans.api.xml.services.UserCatalog;
import org.netbeans.modules.xml.api.model.*;
import org.netbeans.modules.xml.dtd.grammar.*;
import org.netbeans.modules.xml.spi.dom.*;
import org.netbeans.modules.xml.api.cookies.ScenarioCookie;
import org.netbeans.modules.xsl.api.XSLCustomizer;
import org.netbeans.modules.xsl.api.XSLScenario;
import org.openide.filesystems.Repository;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileSystem;
import org.openide.loaders.FolderLookup;
import org.openide.loaders.DataFolder;
import org.openide.loaders.DataObject;
import org.openide.loaders.DataObjectNotFoundException;
import org.openide.loaders.InstanceDataObject;
import org.openide.nodes.PropertySupport;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;

import org.w3c.dom.*;
import org.w3c.dom.NodeList;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
 * This class implements code completion for XSL transformation files.
 * XSL elements in the completion are hardcoded from the XSLT spec, but the
 * result elements are gathered from the "doctype-public" and "doctype-system"
 * attributes of the xsl:output element.
 *
 * @author  asgeir@dimonsoftware.com
 */
public final class XSLGrammarQuery implements GrammarQuery{

    private DataObject dataObject;

    private ScenarioCookie scenarioCookie;

    /** Contains a mapping from XSL namespace element names to set of names of
     * allowed XSL children. Neither the element name keys nor the names in the
     * value set should contain the namespace prefix.
     */
    private static Map elementDecls;

    /** Contains a mapping from XSL namespace element names to set of names of
     * allowed XSL attributes for that element.  The element name keys should
     * not contain the namespace prefix.
     */
    private static Map attrDecls;

    /** A Set of XSL attributes which should be allowd for result elements*/
    private static Set resultElementAttr;

    /** An object which indicates that result element should be allowed in a element Set */
    private static String resultElements = "RESULT_ELEMENTS_DUMMY_STRING"; // NOI18N

    /** A Set of elements which should be allowed at template level in XSL stylesheet */
    private static Set template;

    /** Contains a mapping from XSL namespace element names to an attribute name which
     * should contain XPath expression.  The element name keys should
     * not contain the namespace prefix.
     */
    private static Map exprAttributes;

    /** A set containing all functions allowed in XSLT */
    private static Set xslFunctions;

    /** A set containing XPath axes */
    private static Set xpathAxes;

    /** A list of prefixes using the "http://www.w3.org/1999/XSL/Transform" namespace
     * defined in the context XSL document.  The first prefix in the list is the actual XSL
     * transformation prefix, which is normally defined on the xsl:stylesheet element.
     */
    private List prefixList = new LinkedList();

    /** A GrammarQuery for the result elements created for the doctype-public" and
     * "doctype-system" attributes of the xsl:output element.*/
    private GrammarQuery resultGrammarQuery;

    /** The value of the system identifier of the DTD which was used when
     * resultGrammarQuery was previously created */
    private String lastDoctypeSystem;

    /** The value of the public identifier of the DTD which was used when
     * resultGrammarQuery was previously created */
    private String lastDoctypePublic;

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

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

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

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

    private XSLCustomizer customizer = null;

    private ResourceBundle bundle = NbBundle.getBundle(XSLGrammarQuery.class);

    /** Creates a new instance of XSLGrammarQuery */
    public XSLGrammarQuery(DataObject dataObject) {
        this.dataObject = dataObject;
        scenarioCookie = (ScenarioCookie)dataObject.getCookie(ScenarioCookie.class);
    }

    //////////////////////////////////////////7
    // Getters for the static members

    private static Map getElementDecls() {
        if (elementDecls == null) {
            elementDecls = new HashMap();
            attrDecls = new HashMap();

            // Commonly used variables
            Set emptySet = new TreeSet();
            String spaceAtt = "xml:space";  // NOI18N
            Set tmpSet;

            ////////////////////////////////////////////////
            // Initialize common sets

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

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

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

            template = new TreeSet(instructions);
            template.add(resultElements);

            Set topLevel = new TreeSet(Arrays.asList(new String[]{"import","include","strip-space", // NOI18N
            "preserve-space","output","key","decimal-format","attribute-set", // NOI18N
            "variable","param","template","namespace-alias"})); // NOI18N

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

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

            ////////////////////////////////////////////////
            // Add items to elementDecls and attrDecls maps

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

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

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

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

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

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

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

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

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

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

            // xsl:template
            tmpSet = new TreeSet(instructions);
            tmpSet.add(resultElements);
            tmpSet.add("param"); // NOI18N
            elementDecls.put("template", tmpSet); // NOI18N
            attrDecls.put("template", new TreeSet(Arrays.asList(new String[]{ // NOI18N
                "match","name","priority","mode",spaceAtt}))); // NOI18N

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

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

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

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

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

            // xsl:for-each
            tmpSet = new TreeSet(instructions);
            tmpSet.add(resultElements);
            tmpSet.add("sort"); // NOI18N
            elementDecls.put("for-each", tmpSet); // NOI18N
            attrDecls.put("for-each", new TreeSet(Arrays.asList(new String[]{ // NOI18N
            "select",spaceAtt}))); // NOI18N

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            // xsl:fallback
            elementDecls.put("fallback", template); // NOI18N
            attrDecls.put("fallback", new TreeSet(Arrays.asList(new String[]{spaceAtt}))); // NOI18N
        }
        return elementDecls;
    }

    private static Map getAttrDecls() {
        if (attrDecls == null) {
            getElementDecls();
        }
        return attrDecls;
    }

    private static Set getResultElementAttr() {
        if (resultElementAttr == null) {
            getElementDecls();
        }
        return resultElementAttr;
    }

    private static Set getTemplate() {
        if (template == null) {
            getElementDecls();
        }
        return template;
    }

    private static Set getXslFunctions() {
        if (xslFunctions == null) {
            xslFunctions = new TreeSet(Arrays.asList(new String[]{
                "boolean(","ceiling(","concat(", "contains(","count(","current()","document(", // NOI18N
                "false()", "floor(","format-number(","generate-id(", // NOI18N
                "id(","local-name(","key(","lang(","last()","name(","namespace-uri(", "normalize-space(", // NOI18N
                "not(","number(","position()","round(","starts-with(","string(", // NOI18N
                "string-length(", "substring(","substring-after(","substring-before(", "sum(", // NOI18N
                "system-property(","translate(",   "true()","unparsed-entity-uri("})); // NOI18N
        }
        return xslFunctions;
    }

    private static Set getXPathAxes() {
        if (xpathAxes == null) {
            xpathAxes = new TreeSet(Arrays.asList(new String[]{"ancestor::", "ancestor-or-self::", // NOI18N
            "attribute::", "child::", "descendant::", "descendant-or-self::", "following::", // NOI18N
            "following-sibling::", "namespace::", "parent::", "preceding::", // NOI18N
            "preceding-sibling::", "self::"})); // NOI18N
        }
        return xpathAxes;
    }

    private static Map getExprAttributes() {
        if (exprAttributes == null) {
            exprAttributes = new HashMap();
            exprAttributes.put("key", "use"); // NOI18N
            exprAttributes.put("value-of", "select"); // NOI18N
            exprAttributes.put("copy-of", "select"); // NOI18N
            exprAttributes.put("number", "value"); // NOI18N
            //??? what about match one
            exprAttributes.put("apply-templates", "select"); // NOI18N
            exprAttributes.put("for-each", "select"); // NOI18N
            exprAttributes.put("sort", "select"); // NOI18N
            exprAttributes.put("if", "test"); // NOI18N
            exprAttributes.put("when", "test"); // NOI18N
            exprAttributes.put("with-param", "select"); // NOI18N
            exprAttributes.put("variable", "select"); // NOI18N
            exprAttributes.put("param", "select"); // NOI18N
        }
        return exprAttributes;
    }



    ////////////////////////////////////////////////////////////////////////////////
    // GrammarQuery interface fulfillment

    /**
     * Support completions of elements defined by XSLT spec and by the 
     * doctype attribute (in result space).
     */
    public Enumeration queryElements(HintContext ctx) {
        Node node = ((Node)ctx).getParentNode();

        String prefix = ctx.getCurrentPrefix();
        QueueEnumeration list = new QueueEnumeration();

        if (node instanceof Element) {
            Element el = (Element) node;
            updateProperties(el);
            if (prefixList.size() == 0) return org.openide.util.Enumerations.empty();

            String firstXslPrefixWithColon = prefixList.get(0) + ":"; // NOI18N
            Set elements;
            if (el.getTagName().startsWith(firstXslPrefixWithColon)) {
                String parentNCName = el.getTagName().substring(firstXslPrefixWithColon.length());
                elements = (Set) getElementDecls().get(parentNCName);
            } else {
                // Children of result elements should always be the template set
                elements = getTemplate();
            }

            // First we add the Result elements
            if (elements != null  && resultGrammarQuery != null && elements.contains(resultElements)) {
                ResultHintContext resultHintContext = new ResultHintContext(ctx, firstXslPrefixWithColon, null);
                Enumeration resultEnum = resultGrammarQuery.queryElements(resultHintContext);
                while (resultEnum.hasMoreElements()) {
                    list.put(resultEnum.nextElement());
                }
            }

            // Then we add the XSLT elements of the first prefix (normally of the stylesheet node).
            addXslElementsToEnum(list, elements, prefixList.get(0) + ":", prefix); // NOI18N

            // Finally we add xsl namespace elements with other prefixes than the first one
            for (int prefixInd = 1; prefixInd < prefixList.size(); prefixInd++) {
                String curPrefix = (String)prefixList.get(prefixInd) + ":"; // NOI18N
                Node curNode = el;
                String curName = null;
                while(curNode != null && null != (curName = curNode.getNodeName()) && !curName.startsWith(curPrefix)) {
                    curNode = curNode.getParentNode();
                }

                if (curName == null) {
                    // This must be the document node
                    addXslElementsToEnum(list, getElementDecls().keySet(), curPrefix, prefix);
                } else {
                    String parentName = curName.substring(curPrefix.length());
                    elements = (Set) getElementDecls().get(parentName);
                    addXslElementsToEnum(list, elements, curPrefix, prefix);
                }
            }

        } else if (node instanceof Document) {
            //??? it should be probably only root element name
            if (prefixList.size() == 0) return org.openide.util.Enumerations.empty();
            addXslElementsToEnum(list, getElementDecls().keySet(), prefixList.get(0) + ":", prefix); // NOI18N
        } else {
            return org.openide.util.Enumerations.empty();
        }

        return list;
    }

    public Enumeration queryAttributes(HintContext ctx) {
        Element el = null;
        // Support two versions of GrammarQuery contract
        if (ctx.getNodeType() == Node.ATTRIBUTE_NODE) {
            el = ((Attr)ctx).getOwnerElement();
        } else if (ctx.getNodeType() == Node.ELEMENT_NODE) {
            el = (Element) ctx;
        }
        if (el == null) return org.openide.util.Enumerations.empty();

        String elTagName = el.getTagName();
        NamedNodeMap existingAttributes = el.getAttributes();

        updateProperties(el);


        String curXslPrefix = null;
        for (int ind = 0; ind < prefixList.size(); ind++) {
            if (elTagName.startsWith((String)prefixList.get(ind) + ":")){ // NOI18N
                curXslPrefix = (String)prefixList.get(ind) + ":"; // NOI18N
                break;
            }
        }

        Set possibleAttributes;
        if (curXslPrefix != null) {
            // Attributes of XSL element
            possibleAttributes = (Set) getAttrDecls().get(el.getTagName().substring(curXslPrefix.length()));
        } else {
            // XSL Attributes of Result element
            possibleAttributes = new TreeSet();
            if (prefixList.size() > 0) {
                Iterator it = getResultElementAttr().iterator();
                while ( it.hasNext()) {
                    possibleAttributes.add((String)prefixList.get(0) + ":" + (String) it.next()); // NOI18N
                }
            }
        }
        if (possibleAttributes == null) return org.openide.util.Enumerations.empty();

        String prefix = ctx.getCurrentPrefix();

        QueueEnumeration list = new QueueEnumeration();

        if (resultGrammarQuery != null) {
            Enumeration enum2 = resultGrammarQuery.queryAttributes(ctx);
            while(enum2.hasMoreElements()) {
                GrammarResult resNode = (GrammarResult)enum2.nextElement();
                if (!possibleAttributes.contains(resNode.getNodeName())) {
                    list.put(resNode);
                }
            }
        }

        Iterator it = possibleAttributes.iterator();
        while ( it.hasNext()) {
            String next = (String) it.next();
            if (next.startsWith(prefix)) {
                if (existingAttributes.getNamedItem(next) == null) {
                    list.put(new MyAttr(next));
                }
            }
        }

        return list;
    }

    public Enumeration queryValues(HintContext ctx) {
       if (ctx.getNodeType() == Node.ATTRIBUTE_NODE) {
            updateProperties(((Attr)ctx).getOwnerElement());
            if (prefixList.size() == 0) return org.openide.util.Enumerations.empty();
            String xslNamespacePrefix = prefixList.get(0) + ":"; // NOI18N

            String prefix = ctx.getCurrentPrefix();

            Attr attr = (Attr)ctx;

            boolean isXPath = false;
            String elName = attr.getOwnerElement().getNodeName();
            if (elName.startsWith(xslNamespacePrefix)) {
                String key = elName.substring(xslNamespacePrefix.length());
                String xpathAttrName = (String)getExprAttributes().get(key);
                if (xpathAttrName != null && xpathAttrName.equals(attr.getNodeName())) {
                    // This is an XSLT element which should contain XPathExpression
                    isXPath = true;
                }

                // consult awailable public IDs with users catalog
                if ("output".equals(key)) {                             // NOI18N
                    if ("doctype-public".equals(attr.getName())) {      // NOI18N
                        UserCatalog catalog = UserCatalog.getDefault();
                        if (catalog == null) return org.openide.util.Enumerations.empty();
                        QueueEnumeration en = new QueueEnumeration();
                        Iterator it = catalog.getPublicIDs();
                        while (it.hasNext()) {
                            String next = (String) it.next();
                            if (next != null && next.startsWith(prefix)) {
                                en.put(new  MyText(next));
                            }
                        }
                        return en;
                    }
                }
            }

            String preExpression = ""; // NOI18N

            if (!isXPath) {
                // Check if we are inside { } for attribute value
                String nodeValue = attr.getNodeValue();
                int exprStart = nodeValue.lastIndexOf('{', prefix.length() - 1); // NOI18N
                int exprEnd = nodeValue.indexOf('}', prefix.length()); // NOI18N
                Util.THIS.debug("exprStart: " + exprStart); // NOI18N
                Util.THIS.debug("exprEnd: " + exprEnd); // NOI18N
                if (exprStart != -1 && exprEnd != -1) {
                    isXPath = true;
                    preExpression = prefix.substring(0, exprStart + 1);
                    prefix = prefix.substring(exprStart + 1);
                }

            }

            if (isXPath) {
                // This is an XPath expression
                QueueEnumeration list = new QueueEnumeration();

                int curIndex = prefix.length();
                while (curIndex > 0) {
                    curIndex--;
                    char curChar = prefix.charAt(curIndex);
                    if (curChar == '(' || curChar == ',' || curChar == ' ') { // NOI18N
                        curIndex++;
                        break;
                    }
                }

                preExpression += prefix.substring(0, curIndex);
                String subExpression = prefix.substring(curIndex);

                int lastDiv = subExpression.lastIndexOf('/'); // NOI18N
                String subPre = ""; // NOI18N
                String subRest = ""; // NOI18N
                if (lastDiv != -1) {
                    subPre = subExpression.substring(0, lastDiv + 1);
                    subRest = subExpression.substring(lastDiv + 1);
                } else {
                    subRest = subExpression;
                }

                // At this point we need to consult transformed document or
                // its grammar.

                Object selScenarioObj = scenarioCookie.getModel().getSelectedItem();
                /*
                if (selScenarioObj instanceof XSLScenario) {
                    XSLScenario scenario = (XSLScenario)selScenarioObj;
                    Document doc = null;
                    try {
                        doc = scenario.getSourceDocument(dataObject, false);
                    } catch(Exception e) {
                        // We don't care, ignore
                    }

                    if (doc != null) {
                        Element docElement = doc.getDocumentElement();

                        Set childNodeNames = new TreeSet();

                        String combinedXPath;
                        if (subPre.startsWith("/")) { // NOI18N
                            // This is an absolute XPath
                            combinedXPath = subPre;
                        } else {
                            // This is a relative XPath

                            // Traverse up the documents tree looking for xsl:for-each
                            String xslForEachName = xslNamespacePrefix + "for-each"; // NOI18N
                            List selectAttrs = new LinkedList();
                            Node curNode = attr.getOwnerElement();
                            if (curNode != null) {
                                // We don't want to add select of our selfs
                                curNode = curNode.getParentNode();
                            }

                            while (curNode != null && !(curNode instanceof Document)) {
                                if (curNode.getNodeName().equals(xslForEachName)) {
                                    selectAttrs.add(0, ((Element)curNode).getAttribute("select")); // NOI18N
                                }

                                curNode = curNode.getParentNode();
                            }

                            combinedXPath = ""; // NOI18N
                            for (int ind = 0; ind < selectAttrs.size(); ind++) {
                                combinedXPath += selectAttrs.get(ind) + "/"; // NOI18N
                            }
                            combinedXPath += subPre;
                        }

                        try {
                            NodeList nodeList = XPathAPI.selectNodeList(doc, combinedXPath + "child::*"); // NOI18N
                            for (int ind = 0; ind < nodeList.getLength(); ind++) {
                                Node curResNode = nodeList.item(ind);
                                childNodeNames.add(curResNode.getNodeName());
                            }

                            nodeList = XPathAPI.selectNodeList(doc, combinedXPath + "@*"); // NOI18N
                            for (int ind = 0; ind < nodeList.getLength(); ind++) {
                                Node curResNode = nodeList.item(ind);
                                childNodeNames.add("@" + curResNode.getNodeName()); // NOI18N
                            }
                        } catch (Exception e) {
                            Util.THIS.debug("Ignored during XPathAPI operations", e); // NOI18N
                            // We don't care, ignore
                        }

                        addItemsToEnum(list, childNodeNames, subRest, preExpression + subPre);
                    }
                }*/

                addItemsToEnum(list, getXPathAxes(), subRest, preExpression + subPre);
                addItemsToEnum(list, getXslFunctions(), subExpression, preExpression);

                return list;
            }
        }

        return org.openide.util.Enumerations.empty();
    }

    public GrammarResult queryDefault(HintContext ctx) {
        //??? XSLT defaults are missing
        if (resultGrammarQuery == null) return null;
        return resultGrammarQuery.queryDefault(ctx);
    }

    public boolean isAllowed(Enumeration en) {
        return true; //!!! not implemented
    }

    public Enumeration queryEntities(String prefix) {
        QueueEnumeration list = new QueueEnumeration();

        // add well-know build-in entity names

        if ("lt".startsWith(prefix)) list.put(new MyEntityReference("lt"));     // NOI18N
        if ("gt".startsWith(prefix)) list.put(new MyEntityReference("gt"));     // NOI18N
        if ("apos".startsWith(prefix)) list.put(new MyEntityReference("apos")); // NOI18N
        if ("quot".startsWith(prefix)) list.put(new MyEntityReference("quot")); // NOI18N
        if ("amp".startsWith(prefix)) list.put(new MyEntityReference("amp"));   // NOI18N

        return list;
    }

    public Enumeration queryNotations(String prefix) {
        return org.openide.util.Enumerations.empty();
    }

    public java.awt.Component getCustomizer(HintContext ctx) {
        if (customizer == null) {
            customizer = lookupCustomizerInstance();
            if (customizer == null) {
                return null;
            }
        }

        return customizer.getCustomizer(ctx, dataObject);
    }

    public boolean hasCustomizer(HintContext ctx) {
        if (customizer == null) {
            customizer = lookupCustomizerInstance();
            if (customizer == null) {
                return false;
            }
        }

        return customizer.hasCustomizer(ctx);
    }

    public org.openide.nodes.Node.Property[] getProperties(final HintContext ctx) {

        if (ctx.getNodeType() != Node.ATTRIBUTE_NODE || ctx.getNodeValue() == null) {
            return null;
        }

        PropertySupport attrNameProp = new PropertySupport("Attribute name", String.class,  // NOI18N
        bundle.getString("BK0001"), bundle.getString("BK0002"), true, false) {
            public void setValue(Object value) {
                // Dummy
            }
            public Object getValue() {
                return ctx.getNodeName();
            }

        };

        PropertySupport attrValueProp = new PropertySupport("Attribute value", String.class, // NOI18N
        bundle.getString("BK0003"), bundle.getString("BK0004"), true, true) {
            public void setValue(Object value) {
                ctx.setNodeValue((String)value);
            }
            public Object getValue() {
                return ctx.getNodeValue();
            }

        };

        return new org.openide.nodes.Node.Property[]{attrNameProp, attrValueProp};
    }

    ////////////////////////////////////////////////////////////////////////////////
    // Private helper methods

    /**
     * Looks up registered XSLCustomizer objects which will be used by this object
     */
    private static XSLCustomizer lookupCustomizerInstance() {
        try {
            // Load the XSLCustomizer from the XML layer
            FileSystem fs = Repository.getDefault().getDefaultFileSystem();
            FileObject fo = fs.findResource(CUSTOMIZER_FOLDER);
            if (fo == null) return null;
            DataObject df = DataObject.find(fo);
            if (!(df instanceof DataObject.Container)) {
                return null;
            }

            // ??? What is it? Should not we use naming instead?

            FolderLookup lookup =
                new FolderLookup((DataObject.Container) df);
            Lookup.Template template =
                new Lookup.Template(XSLCustomizer.class);

            Lookup.Item lookupItem = lookup.getLookup().lookupItem(template);
            if (lookupItem == null) {
                return null;
            }

            return (XSLCustomizer)lookupItem.getInstance();
        } catch (DataObjectNotFoundException e) {
            return null;
        }
    }

    /**
     * @param enumX the Enumeration which the element should be added to
     * @param elements a set containing strings which should be added (with prefix) to the enum or null
     * @param namespacePrefix a prefix at the form "xsl:" which should be added in front
     *          of the names in the elements.
     * @param startWith Elements should only be added to enum if they start with this string
     */
    private static void addXslElementsToEnum(QueueEnumeration enumX, Set elements, String namespacePrefix, String startWith) {
        if (elements == null) return;
        if (startWith.startsWith(namespacePrefix) || namespacePrefix.startsWith(startWith)) {
            Iterator it = elements.iterator();
            while ( it.hasNext()) {
                Object next = it.next();
                if (next != resultElements) {
                    String nextText = namespacePrefix + (String)next;
                    if (nextText.startsWith(startWith)) {
                        // TODO pass true for empty elements
                        enumX.put(new MyElement(nextText, false));
                    }
                }
            }
        }
    }

    private static void addItemsToEnum(QueueEnumeration enumX, Set set, String startWith, String prefix) {
        Iterator it = set.iterator();
        while ( it.hasNext()) {
            String nextText = (String)it.next();
            if (nextText.startsWith(startWith)) {
                enumX.put(new MyText(prefix + nextText));
            }
        }
    }

    /**
     * This method traverses up the document tree, investigates it and updates
     * prefixList, resultGrammarQuery, lastDoctypeSystem or lastDoctypePublic
     * members if necessery.
     * @param curNode the node which from wich the traversing should start.
     */
    private void updateProperties(Node curNode) {
        prefixList.clear();

        // Traverse up the documents tree
        Node rootNode = curNode;
        while (curNode != null && !(curNode instanceof Document)) {

            // Update the xsl namespace prefix list
            NamedNodeMap attributes = curNode.getAttributes();
            for (int ind = 0; ind < attributes.getLength(); ind++) {
                Attr attr = (Attr)attributes.item(ind);
                String attrName = attr.getName();
                if (attrName != null && attrName.startsWith("xmlns:")) {  // NOI18N
                    if (attr.getValue().equals(XSLT_NAMESPACE_URI)) {
                        prefixList.add(0, attrName.substring(6));
                    }
                }
            }


            rootNode = curNode;
            curNode = rootNode.getParentNode();
        }

        boolean outputFound = false;
        if (prefixList.size() > 0) {
            String outputElName = (String)prefixList.get(0) + ":output"; // NOI18N
            Node childOfRoot = rootNode.getFirstChild();
            while (childOfRoot != null) {
                String childNodeName = childOfRoot.getNodeName();
                if (childNodeName != null && childNodeName.equals(outputElName)) {
                    Element outputEl = (Element)childOfRoot;
                    String outputMethod = outputEl.getAttribute("method"); // NOI18N

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

                    if ("html".equals(outputMethod)  // NOI18N
                    	&& (curDoctypePublic == null || curDoctypePublic.length() == 0)
                    	&& (curDoctypeSystem == null || curDoctypeSystem.length() == 0)) {                          // NOI18N
                        // html is special case that can be emulated using XHTML
                        curDoctypePublic = XHTML_PUBLIC_ID;
                        curDoctypeSystem = XHTML_SYSTEM_ID;
                    } else if ("text".equals(outputMethod)) {                   // NOI18N
                        // user error, ignore
                        break;
                    }

                    if (curDoctypePublic != null && !curDoctypePublic.equals(lastDoctypePublic) ||
                    curDoctypePublic == null && lastDoctypePublic != null ||
                    curDoctypeSystem != null && !curDoctypeSystem.equals(lastDoctypeSystem) ||
                    curDoctypeSystem == null && lastDoctypeSystem != null) {
                        setOutputDoctype(curDoctypePublic, curDoctypeSystem);
                    }

                    outputFound = true;
                    break;
                }
                childOfRoot = childOfRoot.getNextSibling();
            }
        }

        if (!outputFound) {
            setOutputDoctype(null, null);
        }
    }

    /**
     * Updates resultGrammarQuery by parsing the DTD specified by publicId and
     * systemId. lastDoctypeSystem and lastDoctypePublic are assigned to the new values.
     * @param publicId the public identifier of the DTD
     * @param publicId the system identifier of the DTD
     */
    private void setOutputDoctype(String publicId, String systemId) {
        lastDoctypePublic = publicId;
        lastDoctypeSystem = systemId;

        if (publicId == null && systemId == null) {
            resultGrammarQuery = null;
            return;
        }

        InputSource inputSource = null;
        UserCatalog catalog = UserCatalog.getDefault();
        if (catalog != null) {
            EntityResolver resolver = catalog.getEntityResolver();
            if (resolver != null) {
                try {
                    inputSource = resolver.resolveEntity(publicId, systemId);
                } catch(SAXException e) {
                } catch(IOException e) {
                } // Will be handled below
            }
        }

        if (inputSource == null) {
            try {
                java.net.URL url = new java.net.URL(systemId);
                inputSource = new InputSource(url.openStream());
                inputSource.setPublicId(publicId);
                inputSource.setSystemId(systemId);
            } catch(IOException e) {
                resultGrammarQuery = null;
                return;
            }
        }

        DTDParser dtdParser = new DTDParser(true);
        resultGrammarQuery = dtdParser.parse(inputSource);

    }

    ////////////////////////////////////////////////////////////////////////////////
    // Private helper classes

    private class ResultHintContext extends ResultNode implements HintContext {
        private String currentPrefix;

        public ResultHintContext(HintContext peer, String ignorePrefix, String onlyUsePrefix) {
            super(peer, ignorePrefix, onlyUsePrefix);
            currentPrefix = peer.getCurrentPrefix();
        }

        public String getCurrentPrefix() {
            return currentPrefix;
        }
    }

    // Result classes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


    private static abstract class AbstractResultNode extends AbstractNode implements GrammarResult {

        public Icon getIcon(int kind) {
            return null;
        }

        /**
         * @return provide additional information simplifiing decision
         */
        public String getDescription() {
            return NbBundle.getMessage(XSLGrammarQuery.class, "BK0005");
        }

        /**
         * @return text representing name of suitable entity
         * //??? is it really needed
         */
        public String getText() {
            return getNodeName();
        }

        /**
         * @return name that is presented to user
         */
        public String getDisplayName() {
            return null;
        }

        public boolean isEmptyElement() {
            return false;
        }

    }

    private static class MyEntityReference extends AbstractResultNode implements EntityReference {

        private String name;

        MyEntityReference(String name) {
            this.name = name;
        }

        public short getNodeType() {
            return Node.ENTITY_REFERENCE_NODE;
        }

        public String getNodeName() {
            return name;
        }

    }

    private static class MyElement extends AbstractResultNode implements Element {

        private String name;
        private boolean empty;

        MyElement(String name, boolean empty) {
            this.name = name;
            this.empty = empty;
        }

        public short getNodeType() {
            return Node.ELEMENT_NODE;
        }

        public String getNodeName() {
            return name;
        }

        public String getTagName() {
            return name;
        }

        public boolean isEmptyElement() {
            return empty;
        }

    }

    private static class MyAttr extends AbstractResultNode implements Attr {

        private String name;

        MyAttr(String name) {
            this.name = name;
        }

        public short getNodeType() {
            return Node.ATTRIBUTE_NODE;
        }

        public String getNodeName() {
            return name;
        }

        public String getName() {
            return name;
        }

        public String getValue() {
            return null;  //??? what spec says
        }


    }


    private static class MyText extends AbstractResultNode implements Text {

        private String data;

        MyText(String data) {
            this.data = data;
        }

        public short getNodeType() {
            return Node.TEXT_NODE;
        }

        public String getNodeValue() {
            return getData();
        }

        public String getData() throws DOMException {
            return data;
        }

        public int getLength() {
            return data == null ? -1 : data.length();
        }
    }

    private static class QueueEnumeration implements Enumeration {
        private java.util.LinkedList list = new LinkedList ();
        
        public boolean hasMoreElements () {
            return !list.isEmpty ();
        }        
        
        public Object nextElement () {
            return list.removeFirst ();
        }        

        public void put (Object[] arr) {
            list.addAll (Arrays.asList (arr));
        }
        public void put (Object o) {
            list.add (o);
        }
        
    } // end of QueueEnumeration
}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.