Package org.exist.xquery.functions.fn

Source Code of org.exist.xquery.functions.fn.FunId

/*
* eXist Open Source Native XML Database
* Copyright (C) 2001-2009 The eXist Project
* http://exist-db.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*  $Id$
*/
package org.exist.xquery.functions.fn;

import org.apache.log4j.Logger;

import org.exist.dom.DefaultDocumentSet;
import org.exist.dom.DocumentSet;
import org.exist.dom.ExtArrayNodeSet;
import org.exist.dom.MutableDocumentSet;
import org.exist.dom.NodeProxy;
import org.exist.dom.NodeSet;
import org.exist.dom.QName;
import org.exist.memtree.DocumentImpl;
import org.exist.memtree.NodeImpl;
import org.exist.util.XMLChar;
import org.exist.xquery.Cardinality;
import org.exist.xquery.Constants;
import org.exist.xquery.Dependency;
import org.exist.xquery.Expression;
import org.exist.xquery.Function;
import org.exist.xquery.FunctionSignature;
import org.exist.xquery.Profiler;
import org.exist.xquery.XPathException;
import org.exist.xquery.XQueryContext;
import org.exist.xquery.value.FunctionReturnSequenceType;
import org.exist.xquery.value.FunctionParameterSequenceType;
import org.exist.xquery.value.Item;
import org.exist.xquery.value.NodeValue;
import org.exist.xquery.value.Sequence;
import org.exist.xquery.value.SequenceIterator;
import org.exist.xquery.value.SequenceType;
import org.exist.xquery.value.StringValue;
import org.exist.xquery.value.Type;
import org.exist.xquery.value.ValueSequence;
import org.w3c.dom.Node;

import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import org.exist.xquery.ErrorCodes;

/**
*
* @author wolf
* @author perig
*
*/
public class FunId extends Function {
  protected static final Logger logger = Logger.getLogger(FunId.class);
  public final static FunctionSignature signature[] = {
      new FunctionSignature(
        new QName("id", Function.BUILTIN_FUNCTION_NS),
        "Returns the sequence of element nodes that have an ID value " +
        "matching the value of one or more of the IDREF values supplied in $idrefs. " +
        "If none is matching or $idrefs is the empty sequence, returns the empty sequence.",
        new SequenceType[] {
                    new FunctionParameterSequenceType("idrefs", Type.STRING, Cardinality.ZERO_OR_MORE, "The IDREF sequence")
                },
        new FunctionReturnSequenceType(Type.ELEMENT, Cardinality.ZERO_OR_MORE, "the elements with IDs  matching IDREFs from $idref-sequence")),
            new FunctionSignature(
                    new QName("id", Function.BUILTIN_FUNCTION_NS),
                    "Returns the sequence of element nodes that have an ID value " +
                    "matching the value of one or more of the IDREF values supplied in $idrefs and is in the same document as $node-in-document. " +
                    "If none is matching or $idrefs is the empty sequence, returns the empty sequence.",
                    new SequenceType[] {
                        new FunctionParameterSequenceType("idrefs", Type.STRING, Cardinality.ZERO_OR_MORE, "The IDREF sequence"),
                        new FunctionParameterSequenceType("node-in-document", Type.NODE, Cardinality.EXACTLY_ONE, "The node in document")
                    },
                    new FunctionReturnSequenceType(Type.ELEMENT, Cardinality.ZERO_OR_MORE, "the elements with IDs matching IDREFs from $idrefs in the same document as $node-in-document"))
    };

  /**
   * Constructor for FunId.
   */
  public FunId(XQueryContext context, FunctionSignature signature) {
    super(context, signature);
  }

  /**
   * @see org.exist.xquery.Expression#eval(Sequence, Item)
   */
  public Sequence eval(Sequence contextSequence, Item contextItem) throws XPathException {
        if (context.getProfiler().isEnabled()) {
            context.getProfiler().start(this);
            context.getProfiler().message(this, Profiler.DEPENDENCIES, "DEPENDENCIES", Dependency.getDependenciesName(this.getDependencies()));
            if (contextSequence != null)
                {context.getProfiler().message(this, Profiler.START_SEQUENCES, "CONTEXT SEQUENCE", contextSequence);}
            if (contextItem != null)
                {context.getProfiler().message(this, Profiler.START_SEQUENCES, "CONTEXT ITEM", contextItem.toSequence());}
        }

        if (getArgumentCount() < 1) {
            logger.error("function id requires one argument");
      throw new XPathException(this, ErrorCodes.XPST0017, "function id requires one argument");
        }
        if(contextItem != null)
      {contextSequence = contextItem.toSequence();}

        Sequence result;
        boolean processInMem = false;
        final Expression arg = getArgument(0);
    final Sequence idval = arg.eval(contextSequence);
   
    if(idval.isEmpty() || (getArgumentCount() == 1 && contextSequence != null && contextSequence.isEmpty()))
            {result = Sequence.EMPTY_SEQUENCE;}
        else {
        String nextId;
        DocumentSet docs = null;
            if (getArgumentCount() == 2) {
                // second argument should be a node, whose owner document will be
                // searched for the id
                final Sequence nodes = getArgument(1).eval(contextSequence);
                if (nodes.isEmpty()) {
                    logger.error(ErrorCodes.XPDY0002 + " No node or context item for fn:id");
                    throw new XPathException(this, ErrorCodes.XPDY0002, "XPDY0002: no node or context item for fn:id", nodes);
                }
                if (!Type.subTypeOf(nodes.itemAt(0).getType(), Type.NODE)) {
                    logger.error(ErrorCodes.XPTY0004 + " fn:id() argument is not a node");
                  throw new XPathException(this, ErrorCodes.XPTY0004, "XPTY0004: fn:id() argument is not a node", nodes);
                }
                NodeValue node = (NodeValue)nodes.itemAt(0);
                if (node.getImplementationType() == NodeValue.IN_MEMORY_NODE)
                  //TODO : how to enforce this ?
                  //If $node, or the context item if the second argument is omitted,
                  //is a node in a tree whose root is not a document node [err:FODC0001] is raised                    processInMem = true;
                    {processInMem = true;}
                else {
                    MutableDocumentSet ndocs = new DefaultDocumentSet();
                    ndocs.add(((NodeProxy)node).getDocument());
                    docs = ndocs;
                }
                contextSequence = node;
            } else if (contextSequence == null) {
                logger.error(ErrorCodes.XPDY0002 +  " No context item specified");
                throw new XPathException(this, ErrorCodes.XPDY0002, "No context item specified");
           } else if(!Type.subTypeOf(contextSequence.getItemType(), Type.NODE)) {
                logger.error(ErrorCodes.XPTY0004 + " Context item is not a node");
          throw new XPathException(this, ErrorCodes.XPTY0004, "Context item is not a node", contextSequence);
        } else {
          if (contextSequence.isPersistentSet())
                    {docs = contextSequence.toNodeSet().getDocumentSet();}
                else
                    {processInMem = true;}
            }

            if (processInMem)
                {result = new ValueSequence();}
            else
                {result = new ExtArrayNodeSet();}

            for(final SequenceIterator i = idval.iterate(); i.hasNext(); ) {
          nextId = i.nextItem().getStringValue();
                if (nextId.length() == 0)
                    {continue;}
          if(nextId.indexOf(" ") != Constants.STRING_NOT_FOUND) {
            // parse idrefs
            final StringTokenizer tok = new StringTokenizer(nextId, " ");
            while(tok.hasMoreTokens()) {
              nextId = tok.nextToken();
              if(XMLChar.isValidNCName(nextId)) {
                            if (processInMem)
                                {getId(result, contextSequence, nextId);}
                            else
                                {getId((NodeSet)result, docs, nextId);}
                        }
            }
          } else {
            if(XMLChar.isValidNCName(nextId)) {
                        if (processInMem)
                            {getId(result, contextSequence, nextId);}
                        else
                            {getId((NodeSet)result, docs, nextId);}
                    }
          }
        }
        }

    result.removeDuplicates();

        if (context.getProfiler().isEnabled())
            {context.getProfiler().end(this, "", result);}

        return result;

  }

  private void getId(NodeSet result, DocumentSet docs, String id) throws XPathException {
    final NodeSet attribs = context.getBroker().getValueIndex().find(context.getWatchDog(), Constants.EQ, docs, null, -1, null, new StringValue(id, Type.ID));
    NodeProxy p;
    for (final NodeProxy n : attribs) {
      p = new NodeProxy(n.getDocument(), n.getNodeId().getParentId(), Node.ELEMENT_NODE);
      result.add(p);
    }
  }

    private void getId(Sequence result, Sequence seq, String id) throws XPathException {
        final Set<DocumentImpl> visitedDocs = new TreeSet<DocumentImpl>();
        for (final SequenceIterator i = seq.iterate(); i.hasNext();) {
            final NodeImpl v = (NodeImpl) i.nextItem();
            final DocumentImpl doc = v.getDocument();
            if (!visitedDocs.contains(doc)) {
                final NodeImpl elem = doc.selectById(id);
                if (elem != null)
                    {result.add(elem);}
                visitedDocs.add(doc);
            }
        }
    }

    @Override
    public int getDependencies() {
        // fn:id can operate on the entire context sequence at once - unless the
        // argument depends on the context item
        return getArgument(0).getDependencies();
    }
}
TOP

Related Classes of org.exist.xquery.functions.fn.FunId

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.