/*
* 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.DocumentImpl;
import org.exist.dom.DocumentSet;
import org.exist.dom.QName;
import org.exist.dom.StoredNode;
import org.exist.numbering.NodeId;
import org.exist.storage.UpdateListener;
import org.exist.xquery.*;
import org.exist.xquery.functions.xmldb.XMLDBModule;
import org.exist.xquery.util.DocUtils;
import org.exist.xquery.value.FunctionReturnSequenceType;
import org.exist.xquery.value.FunctionParameterSequenceType;
import org.exist.xquery.value.Item;
import org.exist.xquery.value.Sequence;
import org.exist.xquery.value.SequenceType;
import org.exist.xquery.value.Type;
/**
* Implements the built-in fn:doc() function.
*
* This will be replaced by XQuery's fn:doc() function.
*
* @author wolf
*/
public class FunDoc extends Function {
protected static final Logger logger = Logger.getLogger(FunDoc.class);
public final static FunctionSignature signature =
new FunctionSignature(
new QName("doc", Function.BUILTIN_FUNCTION_NS),
"Returns the document node of $document-uri. " +
XMLDBModule.ANY_URI,
new SequenceType[] {
new FunctionParameterSequenceType("document-uri", Type.STRING,
Cardinality.ZERO_OR_ONE, "The document URI")
},
new FunctionReturnSequenceType(Type.NODE, Cardinality.ZERO_OR_ONE,
"the document node of $document-uri")
);
// fixit! - security warning
private UpdateListener listener = null;
/**
* @param context
*/
public FunDoc(XQueryContext context) {
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());}
}
Sequence result;
final Sequence arg = getArgument(0).eval(contextSequence, contextItem);
if (arg.isEmpty())
{result = Sequence.EMPTY_SEQUENCE;}
else {
final String path = arg.itemAt(0).getStringValue();
// TODO: disabled cache for now as it may cause concurrency issues
// better use compile-time inspection and maybe a pragma to mark those
// sections in the query that can be safely cached.
// check if we can return a cached sequence
//if (cached != null && path.equals(cachedPath)) {
//return cached;
//}
try {
result = DocUtils.getDocument(this.context, path);
if (result.isEmpty() && context.isRaiseErrorOnFailedRetrieval()) {
throw new XPathException(this, ErrorCodes.FODC0002,
"Can not access '" + path + "'", arg);
}
//TODO: we still need a final decision about this. Also check base-uri.
//if (result == Sequence.EMPTY_SEQUENCE)
//throw new XPathException(this, path + " is not an XML document");
final DocumentSet docs = result.getDocumentSet();
if (docs != null && DocumentSet.EMPTY_DOCUMENT_SET != docs) {
// only cache node sets (which have a non-empty document set)
registerUpdateListener();
}
} catch (final Exception e) {
throw new XPathException(this, ErrorCodes.FODC0005, e.getMessage(), arg);
}
}
if (context.getProfiler().isEnabled())
{context.getProfiler().end(this, "", result);}
return result;
}
protected void registerUpdateListener() {
if (listener == null) {
listener = new UpdateListener() {
public void documentUpdated(DocumentImpl document, int event) {
// clear all
}
public void unsubscribe() {
FunDoc.this.listener = null;
}
public void nodeMoved(NodeId oldNodeId, StoredNode newNode) {
// not relevant
}
public void debug() {
logger.debug("UpdateListener: Line: " + getLine() +
": " + FunDoc.this.toString());
}
};
context.registerUpdateListener(listener);
}
}
/**
* @see org.exist.xquery.Expression#resetState(boolean)
* @param postOptimization
*/
public void resetState(boolean postOptimization) {
super.resetState(postOptimization);
if (!postOptimization) {
listener = null;
}
getArgument(0).resetState(postOptimization);
}
}