Package com.jclark.xsl.tr

Source Code of com.jclark.xsl.tr.ProcessContextImpl$VariableBindings

// $Id: ProcessContextImpl.java 122 2005-04-05 01:22:51Z blindsey $

package com.jclark.xsl.tr;

import com.jclark.xsl.om.*;
import com.jclark.xsl.expr.VariantExpr;
import com.jclark.xsl.expr.Variant;
import com.jclark.xsl.expr.VariantBase;
import com.jclark.xsl.expr.StringVariant;
import com.jclark.xsl.expr.NumberVariant;
import com.jclark.xsl.expr.ExtensionContext;
import com.jclark.xsl.expr.CloneableNodeIterator;
import com.jclark.xsl.expr.CloneableNodeIteratorImpl;
import com.jclark.xsl.expr.SingleNodeIterator;
import com.jclark.xsl.expr.KeyValuesTable;

import com.jclark.xsl.sax.SaxFilterMaker;

import java.io.IOException;
import java.net.URL;
import java.net.MalformedURLException;
import java.util.Hashtable;
import java.util.Vector;

/**
* manages the state of a transformation
*   (and performs the transformation
* of a source document against an XSLT stylesheet)
*/
class ProcessContextImpl implements ProcessContext
{
    /**
     * variable name/value  bindings are maintained internally as a linked list
     */
    static final class VariableBindings
    {
        VariableBindings(Name name, Variant value, VariableBindings next)
        {
            this.name = name;
            this.value = value;
            this.next = next;
        }
        final VariableBindings next;
        final Variant value;
        final Name name;
    }

    static final int OPEN_ACTION_INIT_SIZE = 2;

    static StringVariant emptyStringVariant = new StringVariant("");

    private final SheetDetails sheet;

    private final ParameterSet params; // run time params,
                                       // passed in with constructor

    private Node root;
    private Hashtable variableValueTable = new Hashtable();
    private Name evalGlobalVariableName = null;

    private Name[] actionNames = new Name[OPEN_ACTION_INIT_SIZE];
    private Node[] actionNodes = new Node[OPEN_ACTION_INIT_SIZE];

    private int[] actionImportLevels = null;
    private int[] actionForEachLevels = null;

    private int nOpenActions = 0;

    private VariableBindings localVariables;
    private Name[] currentParamNames = null;
    private Variant[] currentParamValues = null;
    private int position = 1;
    private int lastPosition = 1;
    private NodeIterator currentIter = null;
    private Hashtable extensionTable = new Hashtable();
    private Hashtable documentTable = new Hashtable();

    private XMLProcessor processor;

    private Hashtable attributeSetInUseTable = new Hashtable();
    private Hashtable nameAliasTable;
    private Hashtable namespacePrefixMapAliasTable;
    private NameTable nameTable;
    private Hashtable objectTable = new Hashtable();

    // indexed by doc root ids, a hashtable of hashtables of "xsl:keys"
    private Hashtable docsKeyTables = new Hashtable();
    private int nResultFragmentNodes = 0;


    /**
     *
     */
    ProcessContextImpl(SheetDetails sheet,
                       Node root,
                       XMLProcessor processor,
                       ParameterSet params)
    {
        this.sheet = sheet;
        this.root = root;
        this.processor = processor;
        this.params = params;

        if (sheet.haveNamespaceAliases()) {
            nameAliasTable = new Hashtable();
            namespacePrefixMapAliasTable = new Hashtable();
        }
        else {
            nameAliasTable = null;
            namespacePrefixMapAliasTable = null;
        }
        nameTable = root.getNamespacePrefixMap().getNameTable();
    }

    /**
     *
     */
    public void invoke(NodeIterator iter, Action action,
                       Result result)
        throws XSLException
    {
        // save some state
        int savePosition = position;
        int saveLastPosition = lastPosition;
        NodeIterator saveCurrentIter = currentIter;

        currentIter = iter;
        position = 0;
        lastPosition = 0;

        if (actionForEachLevels == null) {
            actionForEachLevels = new int[nOpenActions];
        } else if (nOpenActions > actionForEachLevels.length) {
            int[] oldActionForEachLevels = actionForEachLevels;
            actionForEachLevels = new int[nOpenActions];
            System.arraycopy(oldActionForEachLevels, 0,
                             actionForEachLevels, 0,
                             oldActionForEachLevels.length);
        }
        actionForEachLevels[nOpenActions - 1]++;
        try {
            for (;;) {
                // getLastPosition() may change the iterator,
                //    so use currentIter not iter
                Node node = currentIter.next();
                if (node == null) {
                    break;
                }
                ++position;
                action.invoke(this, node, result);
            }
        }
        finally {
            actionForEachLevels[nOpenActions - 1]--;
            position = savePosition;
            lastPosition = saveLastPosition;
            currentIter = saveCurrentIter;
        }
    }

    /**
     * run the transformation
     */
    public void process(NodeIterator iter, Name modeName,
                        Name[] paramNames, Variant[] paramValues,
                        Result result)
        throws XSLException
    {
        int savePosition = position;
        int saveLastPosition = lastPosition;
        NodeIterator saveCurrentIter = currentIter;
        currentIter = iter;
        position = 0;
        lastPosition = 0;
        Name[] saveParamNames = currentParamNames;
        currentParamNames = paramNames;
        Variant[] saveParamValues = currentParamValues;
        currentParamValues = paramValues;

        try {
            for (;;) {
                // getLastPosition() may change the iterator, so use currentIter not iter
                Node node = currentIter.next();
                if (node == null) {
                    break;
                }
                ++position;
                if (paramValues == null) {
                    processSafe(node, modeName, result);
                }
                else {
                    processUnsafe(node, modeName, result);
                }
            }
        }
        finally {
            position = savePosition;
            lastPosition = saveLastPosition;
            currentIter = saveCurrentIter;
            currentParamNames = saveParamNames;
            currentParamValues = saveParamValues;
        }
    }

    /**
     * Process the given sourceNode using the given mode name,
     *  writing any results to <code>result</code>
     */
    private void processUnsafe(Node node, Name name, Result result)
        throws XSLException
    {
        getAction(name, node).invoke(this, node, result);
    }


    /**
     */
    void processSafe(Node node, Name name, Result result)
        throws XSLException
    {
        if (name == null) {
            for (int i = 0; i < nOpenActions; i++) {
                if (actionNames[i] == null && actionNodes[i].equals(node)) {
                    return; // loop detected
                }
            }
        } else {
            for (int i = 0; i < nOpenActions; i++) {
                if (name.equals(actionNames[i]) && actionNodes[i].equals(node)) {
                    return; // loop detected
                }
            }
        }
        if (nOpenActions == actionNames.length) {
            Name[] oldActionNames = actionNames;
            actionNames = new Name[nOpenActions * 2];
            System.arraycopy(oldActionNames, 0, actionNames,
                             0, nOpenActions);
            Node[] oldActionNodes = actionNodes;
            actionNodes = new Node[nOpenActions * 2];
            System.arraycopy(oldActionNodes, 0,
                             actionNodes, 0, nOpenActions);
        }
        actionNames[nOpenActions] = name;
        actionNodes[nOpenActions] = node;

        ++nOpenActions;
        try {
            getAction(name, node).invoke(this, node, result);
        }
        finally {
            --nOpenActions;
        }

    }
   
    //
    // From the sheet, get the TemplateRuleSet for the named mode,
    // and from that, get the appropriate template for the given Node
    //
    private final Action getAction(Name name, Node node) throws XSLException
    {
        return sheet.getModeTemplateRuleSet(name).getAction(node, this);
    }

    /**
     *
     */
    public void applyImports(Node node, Result result) throws XSLException
    {
        if (actionForEachLevels != null &&
            actionForEachLevels.length >= nOpenActions &&
            actionForEachLevels[nOpenActions - 1] > 0) {
            throw new XSLException("xsl:apply-templates inside xsl:for-each",
                                   node);
        }
        if (actionImportLevels == null) {
            actionImportLevels = new int[nOpenActions];
        }
        else if (nOpenActions > actionImportLevels.length) {
            int[] oldActionImportLevels = actionImportLevels;
            actionImportLevels = new int[nOpenActions];
            System.arraycopy(oldActionImportLevels, 0,
                             actionImportLevels, 0,
                             oldActionImportLevels.length);
        }
   // HST: apply-imports should _not_ pass params through :-(
   // I suppose we _could_ make this controlled by a switch . . .
        Name[] saveParamNames = currentParamNames;
        currentParamNames = null;
        Variant[] saveParamValues = currentParamValues;
        currentParamValues = null;
   try {
            sheet.getModeTemplateRuleSet(actionNames[nOpenActions - 1])
                .getImportAction(node,
                                 this,
                                 actionImportLevels[nOpenActions - 1]++)
                .invoke(this, node, result);
            actionImportLevels[nOpenActions - 1]--;
        }
        finally {
            currentParamNames = saveParamNames;
            currentParamValues = saveParamValues;
        }
    }
   
    //      /**
    //       *
    //       */
    //      public final boolean hasAttribute(Vector nameList, Node node, String value)
    //      {
    //          int len = nameList.size();
    //          for (int i = 0; i < len; i++) {
    //             if (value.equals(node.getAttributeValue((Name)nameList.elementAt(i)))) {
    //                  return true;
    //              }
    //          }
    //          return false;
    //      }

    /**
     *
     */
    public int getPosition()
    {
        return position;
    }

    /**
     *
     */
    public SaxFilterMaker getSaxExtensionFilter()
    {
        return sheet.getSaxExtensionFilter();
    }

    /**
     *
     */
    public int getLastPosition() throws XSLException
    {
        if (lastPosition == 0) {
            lastPosition = position;
            for (NodeIterator iter = cloneCurrentIter(); iter.next() != null;)
                lastPosition++;
        }
        return lastPosition;
    }

    /**
     *
     */
    private NodeIterator cloneCurrentIter()
    {
        if (!(currentIter instanceof CloneableNodeIterator)) {
            currentIter = new CloneableNodeIteratorImpl(currentIter);
        }
        return (NodeIterator)((CloneableNodeIterator)currentIter).clone();
    }

    /**
     *
     */
    public Variant getGlobalVariableValue(Name name)
        throws XSLException
    {
        Variant value = (Variant)variableValueTable.get(name);
        if (value != null) {
            return value;
        }
        VariableInfo info = sheet.getGlobalVariableInfo(name);
        if (info == null) {
            return null;
        }

        Object obj = params.getParameter(name);

        if (obj != null) {
            value = VariantBase.create(obj).makePermanent();
            variableValueTable.put(obj, value);
            return value;
        }

        // Avoid possibility of infinite loop
        variableValueTable.put(name, emptyStringVariant);
        Name temp = evalGlobalVariableName;

        // set this so we can save it in a memento
        evalGlobalVariableName = name;
        try {
            value = info.getExpr().eval(root, this).makePermanent();
        }
        finally {
            evalGlobalVariableName = temp;
        }
        variableValueTable.put(name, value);
        return value;
    }

    /**
     *
     */
    public Variant getLocalVariableValue(Name name)
    {
        for (VariableBindings p = localVariables; p != null; p = p.next) {
            if (p.name.equals(name)) {
                return p.value;
            }
        }
        throw new Error("no such local variable");
    }

    /**
     *
     */
    public void bindLocalVariable(Name name, Variant value)
        throws XSLException
    {
        localVariables = new VariableBindings(name, value.makePermanent(),
                                              localVariables);
    }

    /**
     *
     */
    public void unbindLocalVariables(int n)
    {
        for (; n > 0; --n) {
            localVariables = localVariables.next;
        }
    }

    /**
     *
     */
    public void invokeWithParams(Action action, Name[] paramNames,
                                 Variant[] paramValues,
                                 Node node, Result result)
        throws XSLException // really?
    {
        Name[] saveParamNames = currentParamNames;
        currentParamNames = paramNames;
        Variant[] saveParamValues = currentParamValues;
        currentParamValues = paramValues;
        try {
            action.invoke(this, node, result);
        }
        finally {
            currentParamNames = saveParamNames;
            currentParamValues = saveParamValues;
        }
    }

    /**
     *
     */
    public Variant getParam(Name name)
    {
        if (currentParamNames != null) {
            for (int i = 0; i < currentParamNames.length; i++) {
                if (name.equals(currentParamNames[i])) {
                    return currentParamValues[i];
                }
            }
        }
        return null;
    }

    /**
     * save some state
     */
    public ProcessContext.Memento createMemento()
    {
        final VariableBindings rememberLocalVariables = localVariables;
        final int rememberPosition = position;
        final int rememberLastPosition = lastPosition;
        final NodeIterator rememberCurrentIter
            = lastPosition == 0 ? cloneCurrentIter() : null;
        final Name rememberEvalGlobalVariableName = evalGlobalVariableName;

        return new ProcessContext.Memento()
            {
               
                public void invoke(Action action, Node node,
                                   Result result)
                    throws XSLException
                {
                    // save some state
                    Name[] saveParamNames = currentParamNames;
                    currentParamNames = null;
                    Variant[] saveParamValues = currentParamValues;
                    currentParamValues = null;
                    int savePosition = position;
                    position = rememberPosition;
                    int saveLastPosition = lastPosition;
                    lastPosition = rememberLastPosition;
                    NodeIterator saveCurrentIter = currentIter;
                    currentIter = rememberCurrentIter;
                    VariableBindings saveLocalVariables = localVariables;
                    localVariables = rememberLocalVariables;
                    Object saveGlobalVariableValue = null;
                    if (rememberEvalGlobalVariableName != null) {
                        saveGlobalVariableValue
                            = variableValueTable.get(rememberEvalGlobalVariableName);
                        variableValueTable.put(rememberEvalGlobalVariableName,
                                               emptyStringVariant);
                    }
                    try {
                        action.invoke(ProcessContextImpl.this, node, result);
                    }
                    finally {
                        // restore the saved state
                        currentParamNames = saveParamNames;
                        currentParamValues = saveParamValues;
                        localVariables = saveLocalVariables;
                        position = savePosition;
                        lastPosition = saveLastPosition;
                        currentIter = saveCurrentIter;
                        if (rememberEvalGlobalVariableName != null) {
                            variableValueTable.put(rememberEvalGlobalVariableName,
                                                   saveGlobalVariableValue);
                        }
                    }
                }
            };
    }

    /**
     *
     */
    public ExtensionContext getExtensionContext(String namespace)
        throws XSLException
    {
        ExtensionContext extension =
            (ExtensionContext) extensionTable.get(namespace);
        if (extension == null) {
            extension = sheet.createExtensionContext(namespace);
            if (extension == null) {
                extension = new ExtensionContext()
                    {
                        public boolean available(String name) {
                            return false;
                        }

                        public Object call(String name, Node currentNode,
                                           Object[] args)
                            throws XSLException
                        {
                            throw new XSLException("implementation of extension namespace not available");
                        }
                    };
            }
            extensionTable.put(namespace, extension);
        }
        return extension;
    }

    /**
     *
     */
    public Variant getSystemProperty(Name name)
    {
        return sheet.getSystemProperty(name);
    }

    /**
     *
     */
    public Node getCurrent(Node node)
    {
        return node;
    }

    /**
     *
     */
    public void useAttributeSet(Name name, Node node,
                                Result result)
        throws XSLException
    {
        try {
            Action action = sheet.getAttributeSet(name);
            if (action == null)
                return;
            boolean[] inUse = (boolean[])attributeSetInUseTable.get(name);
            if (inUse == null) {
                inUse = new boolean[1];
                attributeSetInUseTable.put(name, inUse);
            }
            if (inUse[0])
                throw new XSLException("circular attribute set usage", node);
            inUse[0] = true;
            try {
                action.invoke(this, node, result);
            }
            finally {
                inUse[0] = false;
            }
        }
        catch (ClassCastException e) {
        }
    }

    /**
     * load an object model representation of the XML document at
     *  a url constructed from the two arguments
     */
    public NodeIterator getDocument(URL baseURL, String uriRef)
        throws XSLException
    {
        int fragmentIndex = uriRef.indexOf('#');
        String fragment = null;
        if (fragmentIndex >= 0) {
            fragment = uriRef.substring(fragmentIndex + 1);
            uriRef = uriRef.substring(0, fragmentIndex);
        }
        try {
            // Handling of empty relative specs is broken on JDK 1.2 for
            // the file protocol, so workaround this.
            URL url = uriRef.length() == 0 ? baseURL : new URL(baseURL, uriRef);
            Node node = (Node)documentTable.get(url);
            if (node == null) {
                node = processor.load(url,
                                      documentTable.size() + 1 +
                                      nResultFragmentNodes,
                                      sheet.getSourceLoadContext(),
                                      root.getNamespacePrefixMap().getNameTable());
                documentTable.put(url, node);
            }

            // return an iterator starting at the document's root
            return new SingleNodeIterator(node);
        }
        catch (MalformedURLException e) {
            throw new XSLException(e);
        }
        catch (IOException e) {
            throw new XSLException(e);
        }
    }

    /**
     *  @return the table of indexed nodes for the named key in the node's document
     */
    public KeyValuesTable getKeyValuesTable(Name keyName, Node contextNode)
    {
       
        // FIXME: hashtables are probably a bit too heavyweight
        //          for docsKeysTable and docKeys
       
        // get root nodes ID
        String docsTablesKey = contextNode.getRoot().getGeneratedId();

        // find out if we've built any keys for this doc
        Hashtable docKeys = (Hashtable) docsKeyTables.get(docsTablesKey);
        if (docKeys == null) {
            docKeys = new Hashtable();
            docsKeyTables.put(docsTablesKey, docKeys);
        }

        // find if we've already indexed the nodes in this doc for this key
        KeyValuesTable kvt = (KeyValuesTable) docKeys.get(keyName);

        if (kvt == null) {
            KeyDefinition kd = sheet.getKeyDefinition(keyName);
            if (kd == null) {
                // FIXME: throw an exception?
                System.err.println("No key definition element for: " +
                                   keyName.toString());
                return null;
            }
           
            kvt = new KeyValuesTable(kd.getMatchPattern(),
                                     kd.getUseExpression(),
                                     contextNode,
                                     this);
            docKeys.put(keyName, kvt);
           
        }

        return kvt;
    }

    /**
     *
     */
    public Name unaliasName(Name name)
    {
        if (nameAliasTable == null) {
            return name;
        }
        Name unaliasedName = (Name)nameAliasTable.get(name);
        if (unaliasedName == null) {
            String ns = sheet.getNamespaceAlias(name.getNamespace());
            if (ns == null) {
                unaliasedName = name;
            } else {
                unaliasedName = nameTable.createName(name.toString(), ns);
            }
        }
        nameAliasTable.put(name, unaliasedName);
        return unaliasedName;
    }
 
    /**
     *
     */
    public NamespacePrefixMap unaliasNamespacePrefixMap(NamespacePrefixMap map)
    {
        if (namespacePrefixMapAliasTable == null) {
            return map;
        }
        NamespacePrefixMap unaliasedMap
            = (NamespacePrefixMap)namespacePrefixMapAliasTable.get(map);
        if (unaliasedMap == null) {
            unaliasedMap = map;
            String ns = sheet.getNamespaceAlias(map.getDefaultNamespace());
            if (ns != null) {
                unaliasedMap = unaliasedMap.bindDefault(ns);
      }
            int size = map.getSize();
            for (int i = 0; i < size; i++) {
                ns = sheet.getNamespaceAlias(map.getNamespace(i));
                if (ns != null) {
                    unaliasedMap = unaliasedMap.bind(map.getPrefix(i), ns);
    }
            }
        }
        namespacePrefixMapAliasTable.put(map, unaliasedMap);
        return unaliasedMap;
    }

    /**
     *
     */
    public void put(Object key, Object value)
    {
        objectTable.put(key, value);
    }

    /**
     *
     */
    public Object get(Object key)
    {
        return objectTable.get(key);
    }
   
    /**
     *
     */
    public Node getTree(Variant variant) throws XSLException
    {
        // only know how to do this with ResultFragmentVariants
        if (!(variant instanceof ResultFragmentVariant)) {
            return null;
        }
        return ((ResultFragmentVariant)variant).getTree(this);
    }
   
    /**
     *
     */
    public Result createNodeResult(Node baseNode,
                                   Node[] rootNodeRef)
        throws XSLException
    {
        return processor.createResult(baseNode,
                                      documentTable.size() +
                                      ++nResultFragmentNodes,
                                      sheet.getSourceLoadContext(),
                                      rootNodeRef);
    }
}
TOP

Related Classes of com.jclark.xsl.tr.ProcessContextImpl$VariableBindings

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.