001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements. See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership. The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the  "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    /*
019     * $Id: FuncId.java 468655 2006-10-28 07:12:06Z minchau $
020     */
021    package org.apache.xpath.functions;
022    
023    import java.util.StringTokenizer;
024    
025    import org.apache.xml.dtm.DTM;
026    import org.apache.xml.dtm.DTMIterator;
027    import org.apache.xml.utils.StringVector;
028    import org.apache.xpath.NodeSetDTM;
029    import org.apache.xpath.XPathContext;
030    import org.apache.xpath.objects.XNodeSet;
031    import org.apache.xpath.objects.XObject;
032    import org.apache.xpath.res.XPATHErrorResources;
033    
034    /**
035     * Execute the Id() function.
036     * @xsl.usage advanced
037     */
038    public class FuncId extends FunctionOneArg
039    {
040        static final long serialVersionUID = 8930573966143567310L;
041    
042      /**
043       * Fill in a list with nodes that match a space delimited list if ID 
044       * ID references.
045       *
046       * @param xctxt The runtime XPath context.
047       * @param docContext The document where the nodes are being looked for.
048       * @param refval A space delimited list of ID references.
049       * @param usedrefs List of references for which nodes were found.
050       * @param nodeSet Node set where the nodes will be added to.
051       * @param mayBeMore true if there is another set of nodes to be looked for.
052       *
053       * @return The usedrefs value.
054       */
055      private StringVector getNodesByID(XPathContext xctxt, int docContext,
056                                        String refval, StringVector usedrefs,
057                                        NodeSetDTM nodeSet, boolean mayBeMore)
058      {
059    
060        if (null != refval)
061        {
062          String ref = null;
063    //      DOMHelper dh = xctxt.getDOMHelper();
064          StringTokenizer tokenizer = new StringTokenizer(refval);
065          boolean hasMore = tokenizer.hasMoreTokens();
066          DTM dtm = xctxt.getDTM(docContext);
067    
068          while (hasMore)
069          {
070            ref = tokenizer.nextToken();
071            hasMore = tokenizer.hasMoreTokens();
072    
073            if ((null != usedrefs) && usedrefs.contains(ref))
074            {
075              ref = null;
076    
077              continue;
078            }
079    
080            int node = dtm.getElementById(ref);
081    
082            if (DTM.NULL != node)
083              nodeSet.addNodeInDocOrder(node, xctxt);
084    
085            if ((null != ref) && (hasMore || mayBeMore))
086            {
087              if (null == usedrefs)
088                usedrefs = new StringVector();
089    
090              usedrefs.addElement(ref);
091            }
092          }
093        }
094    
095        return usedrefs;
096      }
097    
098      /**
099       * Execute the function.  The function must return
100       * a valid object.
101       * @param xctxt The current execution context.
102       * @return A valid XObject.
103       *
104       * @throws javax.xml.transform.TransformerException
105       */
106      public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
107      {
108    
109        int context = xctxt.getCurrentNode();
110        DTM dtm = xctxt.getDTM(context);
111        int docContext = dtm.getDocument();
112    
113        if (DTM.NULL == docContext)
114          error(xctxt, XPATHErrorResources.ER_CONTEXT_HAS_NO_OWNERDOC, null);
115    
116        XObject arg = m_arg0.execute(xctxt);
117        int argType = arg.getType();
118        XNodeSet nodes = new XNodeSet(xctxt.getDTMManager());
119        NodeSetDTM nodeSet = nodes.mutableNodeset();
120    
121        if (XObject.CLASS_NODESET == argType)
122        {
123          DTMIterator ni = arg.iter();
124          StringVector usedrefs = null;
125          int pos = ni.nextNode();
126    
127          while (DTM.NULL != pos)
128          {
129            DTM ndtm = ni.getDTM(pos);
130            String refval = ndtm.getStringValue(pos).toString();
131    
132            pos = ni.nextNode();
133            usedrefs = getNodesByID(xctxt, docContext, refval, usedrefs, nodeSet,
134                                    DTM.NULL != pos);
135          }
136          // ni.detach();
137        }
138        else if (XObject.CLASS_NULL == argType)
139        {
140          return nodes;
141        }
142        else
143        {
144          String refval = arg.str();
145    
146          getNodesByID(xctxt, docContext, refval, null, nodeSet, false);
147        }
148    
149        return nodes;
150      }
151    }