This file shows some simple examples of external functions.
/* * Copyright 2006-2008 The FLWOR Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <vector> #include <iostream> #include <sstream> #include <cassert> #include <zorba/zorba.h> #include <zorba/external_module.h> #include <zorba/user_exception.h> #include <zorba/function.h> #include <zorba/empty_sequence.h> #include <zorba/vector_item_sequence.h> #include <zorba/uri_resolvers.h> #include <zorba/store_manager.h> #include <zorba/xquery_exception.h> #include <zorba/diagnostic_list.h> using namespace zorba; class MySimpleExternalFunction; class MyLazySimpleExternalFunction; class MyErrorReportingExternalFunction; class MyParametrizedExternalFunction; /***************************************************************************//** External module with target namespace "urn:foo", storing 4 external function objects. ********************************************************************************/ class MyExternalModule : public ExternalModule { protected: MySimpleExternalFunction * bar1; MyLazySimpleExternalFunction * bar2; MyErrorReportingExternalFunction * bar3; MyParametrizedExternalFunction * bar4; public: MyExternalModule() : bar1(0), bar2(0), bar3(0), bar4(0) { } ~MyExternalModule(); String getURI() const { return "urn:foo"; } ExternalFunction* getExternalFunction(const String& aLocalname); }; /***************************************************************************//** An example that shows that an external module cannot be registered more than once. ********************************************************************************/ bool func_example_0(Zorba* aZorba) { try { StaticContext_t lContext = aZorba->createStaticContext(); MyExternalModule lModule; lContext->registerModule(&lModule); lContext->registerModule(&lModule); // only allowed to register it once } catch (ZorbaException const &e) { std::cerr << "some exception " << e << std::endl; return true; } return false; } /***************************************************************************//** A first simple external function. declare function bar1($a1, $a2) external The function implements a concatenation of two sequences passed as arguments. The concatenation is done eagerly, i.e. the result is fully materialized before any of its items are returned. ********************************************************************************/ class MySimpleExternalFunction : public ContextualExternalFunction { protected: const ExternalModule* theModule; public: MySimpleExternalFunction(const ExternalModule* aModule) : theModule(aModule) { } String getURI() const { return theModule->getURI(); } String getLocalName() const { return "bar1"; } ItemSequence_t evaluate( const ExternalFunction::Arguments_t& args, const StaticContext* sctx, const DynamicContext* dctx) const { std::cout << "namespace " << sctx->getNamespaceURIByPrefix("foo") << std::endl << "current datetime " << dctx->getCurrentDateTime().getStringValue() << std::endl; ItemVector vec; for (int i = 0; i < 2; ++i) { Iterator_t iter = args[i]->getIterator(); iter->open(); Item item; while(iter->next(item)) { vec.push_back(item); } iter->close(); } // transfer ownership of the IteratorBackedItemSequence to Zorba (using an auto_ptr) return ItemSequence_t(new IteratorBackedItemSequence(vec)); } private: typedef std::vector<Item> ItemVector; typedef ItemVector::iterator ItemVectorIte; class IteratorBackedItemSequence : public ItemSequence { class InternalIterator : public Iterator { private: IteratorBackedItemSequence *theItemSequence; ItemVectorIte theIte; ItemVectorIte theEnd; bool is_open; public: InternalIterator(IteratorBackedItemSequence *item_sequence) : theItemSequence(item_sequence), is_open(false) { } void open() { is_open = true; theIte = theItemSequence->theItems.begin(); theEnd = theItemSequence->theItems.end(); } void close() { is_open = false; } bool isOpen() const { return is_open; } bool next(Item& val) { assert(is_open); if (theIte == theEnd) { return false; } val = *theIte; ++theIte; return true; } }; private: ItemVector theItems; public: IteratorBackedItemSequence(ItemVector& vec) : theItems(vec) { } Iterator_t getIterator () {return new InternalIterator(this);} }; }; bool func_example_1(Zorba* aZorba) { StaticContext_t sctx = aZorba->createStaticContext(); MyExternalModule module; sctx->registerModule(&module); std::ostringstream queryText; queryText << "declare namespace foo=\"urn:foo\";" << std::endl << "declare function foo:bar1($a1, $a2) external;" << std::endl << "foo:bar1((1,2,3), (4,5,6))" << std::endl; XQuery_t query = aZorba->compileQuery(queryText.str(), sctx); std::cout << query << std::endl; return true; } /***************************************************************************//** A second simple external function. declare function bar2($a1, $a2) external The function implements a concatenation of two sequences passed as arguments. The concatenation is done lazily, i.e. the result is computed on the fly. ********************************************************************************/ class MyLazySimpleExternalFunction : public NonContextualExternalFunction { protected: const ExternalModule* theModule; public: MyLazySimpleExternalFunction(const ExternalModule* aModule) : theModule(aModule) { } String getURI() const { return theModule->getURI(); } String getLocalName() const { return "bar2"; } ItemSequence_t evaluate(const Arguments_t& args) const { // transfer ownership of the IteratorBackedItemSequence to Zorba (using an auto_ptr) return ItemSequence_t(new LazyConcatItemSequence(args)); } private: class LazyConcatItemSequence : public ItemSequence { class InternalIterator : public Iterator { private: LazyConcatItemSequence *theItemSequence; size_t theCurrentArg; Iterator_t args_iter; bool is_open; public: InternalIterator(LazyConcatItemSequence *item_sequence) : theItemSequence(item_sequence), theCurrentArg(0), is_open(false) { } virtual void open() { is_open = true; theCurrentArg = 0; if(theCurrentArg < theItemSequence->theArgs.size()) { args_iter = theItemSequence->theArgs[theCurrentArg]->getIterator(); args_iter->open(); } } virtual void close() { if(theCurrentArg < theItemSequence->theArgs.size()) { args_iter->close(); } is_open = false; } virtual bool isOpen() const { return is_open; } bool next(Item& result) { assert(is_open); if(theCurrentArg == theItemSequence->theArgs.size()) return false; while(theCurrentArg < 2 && !args_iter->next(result)) { args_iter->close(); ++theCurrentArg; if(theCurrentArg == theItemSequence->theArgs.size()) return false; args_iter = theItemSequence->theArgs[theCurrentArg]->getIterator(); args_iter->open(); } return !result.isNull(); } }; private: Arguments_t theArgs; public: LazyConcatItemSequence(const ExternalFunction::Arguments_t& args) : theArgs(args) { } Iterator_t getIterator() {return new InternalIterator(this);} }; }; bool func_example_2_1(Zorba* aZorba) { StaticContext_t sctx = aZorba->createStaticContext(); MyExternalModule module; sctx->registerModule(&module); std::ostringstream queryText; queryText << "declare namespace foo=\"urn:foo\";" << std::endl << "declare function foo:bar2($a1, $a2) external;" << std::endl << "foo:bar2((1,2,3), (4,5,6))" << std::endl; XQuery_t query = aZorba->compileQuery(queryText.str(), sctx); std::cout << query << std::endl; return true; } bool func_example_2_2(Zorba* aZorba) { StaticContext_t lContext = aZorba->createStaticContext(); MyExternalModule lModule; lContext->registerModule(&lModule); std::ostringstream lText; lText << "declare namespace foo=\"urn:foo\";" << std::endl << "declare function foo:bar2($a1, $a2) external;" << std::endl << "let $s1 := (1,2,3)" << std::endl << "let $s2 := (4,5,6)" << std::endl << "for $x in 1 to 6 return (foo:bar2($s1, $s2)[7-$x])" << std::endl; XQuery_t lQuery = aZorba->compileQuery(lText.str(), lContext); std::cout << lQuery << std::endl; return true; } /***************************************************************************//** A third simple external function. declare function bar3($a) external; The function takes a single argument and checks if that argument is the empty sequence. If so, it raises an error. Otherwise, it returns back the argument. ********************************************************************************/ class MyErrorReportingExternalFunction : public NonContextualExternalFunction { protected: const ExternalModule* theModule; public: MyErrorReportingExternalFunction(const ExternalModule* aModule) : theModule(aModule) { } String getURI() const { return theModule->getURI(); } String getLocalName() const { return "bar3"; } ItemSequence_t evaluate(const ExternalFunction::Arguments_t& args) const { // transfer ownership of the IteratorBackedItemSequence to Zorba (using an auto_ptr) return ItemSequence_t(new LazyErrorReportingItemSequence(args)); } private: class LazyErrorReportingItemSequence : public ItemSequence { class InternalIterator : public Iterator { private: LazyErrorReportingItemSequence *theItemSequence; bool is_open; bool theIsEmpty; Iterator_t arg0_iter; public: InternalIterator(LazyErrorReportingItemSequence *item_sequence) : theItemSequence(item_sequence), is_open(false), theIsEmpty(true) { } virtual void open() { is_open = true; arg0_iter = theItemSequence->theArgs[0]->getIterator(); arg0_iter->open(); } virtual void close() { if(is_open) arg0_iter->close(); is_open = false; } virtual bool isOpen() const { return is_open; } bool next(Item& result) { assert(is_open); bool done = !arg0_iter->next(result); if (done && theIsEmpty) { throw USER_EXCEPTION( err::XPTY0004, "argument must not be the empty sequence" ); } theIsEmpty = false; return !done; } }; private: ExternalFunction::Arguments_t theArgs; public: LazyErrorReportingItemSequence(const ExternalFunction::Arguments_t& args) : theArgs(args) { } Iterator_t getIterator() {return new InternalIterator(this);} }; }; bool func_example_3_1(Zorba* aZorba) { StaticContext_t sctx = aZorba->createStaticContext(); MyExternalModule module; sctx->registerModule(&module); std::ostringstream queryText; queryText << "declare namespace foo=\"urn:foo\";" << std::endl << "declare function foo:bar3($a1) external;" << std::endl << "let $s1 := ()" << std::endl << "for $x in 1 to 6 return (foo:bar3($s1))" << std::endl; XQuery_t query = aZorba->compileQuery(queryText.str(), sctx); try { std::cout << query << std::endl; } catch (ZorbaException const& ex) { std::cerr << ex << std::endl; return ex.diagnostic().kind() == diagnostic::XQUERY_TYPE; } return false; } /***************************************************************************//** ********************************************************************************/ class MyParametrizedExternalFunction : public ContextualExternalFunction { protected: const ExternalModule* theModule; public: MyParametrizedExternalFunction(const ExternalModule* aModule) : theModule(aModule) { } String getURI() const { return theModule->getURI(); } String getLocalName() const { return "bar4"; } ItemSequence_t evaluate( const ExternalFunction::Arguments_t& args, const StaticContext* sctx, const DynamicContext* dctx) const { void* lParam; std::string lParamName("myparam"); if (!dctx->getExternalFunctionParam(lParamName, lParam)) { assert(false); } std::cout << "the function param: " << *static_cast<std::string*>(lParam) << std::endl; return ItemSequence_t(new EmptySequence()); } }; bool func_example_4_1(Zorba* aZorba) { StaticContext_t sctx = aZorba->createStaticContext(); MyExternalModule module; sctx->registerModule(&module); std::ostringstream queryText; queryText << "declare namespace foo=\"urn:foo\";" << std::endl << "declare function foo:bar4() external;" << std::endl << "for $x in 1 to 6 return (foo:bar4())" << std::endl; XQuery_t query = aZorba->compileQuery(queryText.str(), sctx); DynamicContext* lDynContext = query->getDynamicContext(); std::string lFunctionParam("foo:bar"); lDynContext->addExternalFunctionParam("myparam", &lFunctionParam); try { std::cout << query << std::endl; } catch (ZorbaException const &ex) { std::cerr << ex << std::endl; return ex.diagnostic().kind() == diagnostic::XQUERY_STATIC; } return true; } /***************************************************************************//** ********************************************************************************/ MyExternalModule::~MyExternalModule() { delete bar1; delete bar2; delete bar3; delete bar4; } ExternalFunction* MyExternalModule::getExternalFunction(const String& aLocalname) { if (aLocalname == "bar1") { if (!bar1) { bar1 = new MySimpleExternalFunction(this); } return bar1; } else if (aLocalname == "bar2") { if (!bar2) { bar2 = new MyLazySimpleExternalFunction(this); } return bar2; } else if (aLocalname == "bar3") { if (!bar3) { bar3 = new MyErrorReportingExternalFunction(this); } return bar3; } else if (aLocalname == "bar4") { if (!bar3) { bar4 = new MyParametrizedExternalFunction(this); } return bar4; } return 0; } /***************************************************************************//** ********************************************************************************/ static void releaseStream(std::istream* aStream) { delete aStream; } class MyModuleURLResolver : public URLResolver { public: ~MyModuleURLResolver() {} virtual Resource* resolveURL( const String& aUrl, EntityData const* aEntityData) { if (aEntityData->getKind() == EntityData::MODULE && aUrl == "http://www.zorba-xquery.com/mymodule") { // we have only one module std::auto_ptr<std::istream> lQuery (new std::istringstream ("module namespace lm = 'http://www.zorba-xquery.com/mymodule'; " "declare function lm:foo() { 'foo' }; " "declare function lm:ext() external;")); return StreamResource::create(lQuery.release(), &releaseStream); } else { return NULL; } } }; class MyModuleExternalFunction; class MyModuleExternal : public ExternalModule { protected: Zorba * theZorba; MyModuleExternalFunction * theExtFunc; public: MyModuleExternal(Zorba* aZorba) : theZorba(aZorba), theExtFunc(NULL) {} ~MyModuleExternal() {} ItemFactory* getItemFactory() const { return theZorba->getItemFactory(); } String getURI() const { return "http://www.zorba-xquery.com/mymodule"; } void setExternalFunction(MyModuleExternalFunction* f) { theExtFunc = f; } ExternalFunction* getExternalFunction(const String& aLocalname) { if (aLocalname == "ext") { return reinterpret_cast<ExternalFunction*>(theExtFunc); } return NULL; } }; class MyModuleExternalFunction : public NonContextualExternalFunction { protected: MyModuleExternal * theModule; std::vector<Item> theItems; public: MyModuleExternalFunction(MyModuleExternal* aModule) : theModule(aModule) { Item item = aModule->getItemFactory()->createString("ext"); theItems.push_back(item); theModule->setExternalFunction(this); } String getURI() const { return theModule->getURI(); } String getLocalName() const { return "ext"; } ItemSequence_t evaluate(const ExternalFunction::Arguments_t& args) const { // transfer ownership of the IteratorBackedItemSequence to Zorba (using an auto_ptr) return ItemSequence_t(new VectorItemSequence(theItems)); } }; bool func_example_5(Zorba* aZorba) { StaticContext_t sctx = aZorba->createStaticContext(); MyModuleURLResolver moduleResolver; sctx->registerURLResolver(&moduleResolver); MyModuleExternal lExternalModule(aZorba); sctx->registerModule(&lExternalModule); MyModuleExternalFunction lExtFunc(&lExternalModule); std::ostringstream queryText; queryText << "import module namespace lm=\"http://www.zorba-xquery.com/mymodule\";" << "concat(lm:foo(), lm:ext())" << std::endl; try { XQuery_t query = aZorba->compileQuery(queryText.str(), sctx); std::cout << query << std::endl; } catch (ZorbaException const &ex) { std::cerr << ex << std::endl; return false; // type exception expected } return true; } /***************************************************************************//** ********************************************************************************/ int external_functions(int argc, char* argv[]) { void* lStore = zorba::StoreManager::getStore(); Zorba* lZorba = Zorba::getInstance(lStore); bool res = false; std::cout << std::endl << "executing simple external function test 0" << std::endl; res = func_example_0(lZorba); if (!res) return 1; std::cout << std::endl; std::cout << std::endl << "executing simple external function test 1.1" << std::endl; res = func_example_1(lZorba); if (!res) return 1; std::cout << std::endl; std::cout << std::endl << "executing simple external function test 2.1" << std::endl; res = func_example_2_1(lZorba); if (!res) return 1; std::cout << std::endl; std::cout << std::endl << "executing simple external function test 2.2" << std::endl; res = func_example_2_2(lZorba); if (!res) return 1; std::cout << std::endl; std::cout << std::endl << "executing simple external function test 3.1" << std::endl; res = func_example_3_1(lZorba); if (!res) return 1; std::cout << std::endl; std::cout << std::endl << "executing simple external function test 4.1" << std::endl; res = func_example_4_1(lZorba); if (!res) return 1; std::cout << std::endl; std::cout << std::endl << "executing simple external function test 5" << std::endl; res = func_example_5(lZorba); if (!res) return 1; std::cout << std::endl; lZorba->shutdown(); zorba::StoreManager::shutdownStore(lStore); return 0; }