View as source file or XML.
(: Evil import statement :) import module namespace utils = "http://www.example.com/modules/utils" at "/home/foo/xquery/utils.xq";In this import statement, the user specifies that the file containing the module is physically located at "/home/foo/xquery/utils.xq".Having the physical location hard-coded isn't desirable for two reasons: First, the physical location of a module or schema may change during the development process; also, frequently the module will be located in a different directory after the application has been deployed. Second, the user may want to package and distribute the application without having other developers to perform code changes in order to have location hints which are valid on their system.Out of the box, Zorba will attempt to map URIs to local filesystem locations. It does so in two steps:
/usr/share/zorba/modules/com/example/www/modules/utils.xq
If you have modules or schemas installed in other locations on your system, you may provide additional search directories either by passing the --uri-path command-line argument to Zorba, or by setting the ZORBA_URI_PATH environment variable. In both cases, the value is an ordered list of filesystem directories separated by ":" (on Unix/MacOS X) or ";" (on Windows). Zorba will search each directory on the URI path in the order specified, and the first match found will be used. So, for example, if you invoke Zorba as follows (example is for a Unix system):zorba --uri-path '/home/foo/xquery/uris:/opt/share/xquery/uris' -q 'import module namespace utils="http://www.example.com/modules/utils"; 1'Zorba will attempt to load the module from the following locations, in order:
/home/foo/xquery/uris/com/example/www/modules/utils.xq /opt/share/xquery/uris/com/example/www/modules/utils.xq /usr/share/zorba/uris/com/example/www/modules/utils.xqIf, after searching all URI path directories, no match is found for a given URI, Zorba will by default fall back to interpretting the URI as a URL and loading the resource via HTTP / HTTPS / FTP (depending on the URI scheme). This behaviour can be defeated by disabling the http-uri-resolution Zorba option (see Enabling or Disabling Features).
ZORBA_NONCORE_URI_DIR ZORBA_CORE_URI_DIR ZORBA_NONCORE_LIB_DIR ZORBA_CORE_LIB_DIRNote that these are relative directories, and will be resolved relative to CMAKE_INSTALL_PREFIX.
// Create a new static context zorba::StaticContext_t staticCtx = zorba->createStaticContext(); // Set the URI Path std::vector<zorba::String> uriPath(2); uriPath[0] = "/home/foo/xquery/uris"; uriPath[1] = "/opt/share/xquery/uris"; staticCtx->setURIPath(uriPath); // Compile a query using the static context created above zorba::XQuery_t query = zorba->compileQuery( "import module namespace m='http://example.com/module'; m:foo()", staticCtx); // execute the compiled query printing the result to standard out std::cout << query << std::endl;
virtual void mapURI(const zorba::String aUri, EntityData const* aEntityData, std::vector<zorba::String>& oUris);and then register an instance of your subclass with the static context using the method registerURIMapper():
StaticContext_t lContext = aZorba->createStaticContext(); MyURIMapperSubclass* lMapper = new MyURIMapperSubclass(); lContext->registerURIMapper(lMapper);Note that the memory ownership of the URIMapper instance remains with the client program; it must de-allocate it appropriately when the static context is no longer used.In your mapURI() implementation, aUri is the input URI. aEntityData is a pointer to additional information about the URI being resolved. As of Zorba 2.0, the only method on EntityData is getKind(), which will return an enumerated value describing what kind of URI is being resolved: SCHEMA, MODULE, THESAURUS, STOP_WORDS, COLLECTION, or DOCUMENT.oUris is where you place any candidate URIs, by calling the method push_back(). (You should not look at any existing contents of the vector.) If you push any candidate URIs onto the vector, Zorba will replace the input URI with the set of candidate URIs you provide. That means that if you want Zorba to consider the original URI in addition to the alternative URIs you provide, you must push the original URI onto the vector as well.The mapURI() method should not throw any exceptions.As a limited but functional example, here is a full class which will change the URI of a specific schema to an alternative URI. You could use this, for example, if you have a large body of XQueries using a particular schema that you do want Zorba to download from the web (hence the built-in filesystem mapping mechanism is not appropriate), but the URI that the schema is available from has changed and you do not wish to modify all the import schema statements.
class MySchemaURIMapper : public URIMapper { public: virtual ~MySchemaURIMapper() {} virtual void mapURI(const zorba::String aUri, EntityData const* aEntityData, std::vector<zorba::String>& oUris) { if (aEntityData->getKind() != EntityData::SCHEMA) { return; } if(aUri == "http://www.example.com/helloworld") { oUris.push_back("http://example.com/schemas/helloworld.xsd"); } } };Note that the first thing mapURI() does is check that the URI being resolved is in fact for a schema. This is generally good practice to prevent surprises in the off-chance that the same URI is also used to identify some other kind of resource.
virtual Resource* resolveURL(const zorba::String& aUrl, EntityData const* aEntityData);and then register an instance of your subclass with the static context using the method registerURLResolver():
StaticContext_t lContext = aZorba->createStaticContext(); MyURLResolverSubclass* lResolver = new MyURLResolverSubclass(); lContext->registerURLResolver(lResolver);You will note that this mechanism is exactly parallel to the URI Mapper mechanism. Also, as with URI Mappers, the memory ownership of the URLResolver instance remains with the client program; it must de-allocate it appropriately when the static context is no longer used.In your resolveURL() method, the aUri and aEntityData arguments have exactly the same meanings as they do for mapURI().If your code recognizes the URL and wishes to return content for it, it must return a newly-allocated instance of some subclass of the abstract class Resource. In Zorba 2.0's public API, there is only one such subclass, StreamResource, which wraps around a std::istream.It is important that all URL Resolvers check the EntityData's Kind and only return Resources for the appropriate kind of URIs, for two reasons:
using namespace zorba; static void streamReleaser(std::istream* aStream) { delete aStream; } class FoobarModuleURLResolver : public URLResolver { public: virtual ~FoobarModuleURLResolver() {} virtual Resource* resolveURL(const String& aUrl, EntityData const* aEntityData) { // we have only one module if (aEntityData->getKind() == EntityData::MODULE && aUrl == "http://www.example.com/foobar") { return StreamResource::create (new std::istringstream ("module namespace lm = 'http://www.example.com/foobar'; " "declare function lm:foo() { 'foo' };"), &streamReleaser); } else { return NULL; } } };A more realistic example would be a resolver that takes URLs (possibly with a non-standard scheme, such as db:) and loads the content for those URLs from a database. As long as the database API allows you to obtain the information as a std::istream, you may stream this data directly to Zorba.Two notes about memory management: First, when a user-defined URLResolver returns a Resource, Zorba will take memory ownership of the Resource, and will free it when it is no longer needed. Second, when user code creates a StreamResource, the StreamResource assumes memory ownership of the std::istream that is wrapped. However, Zorba cannot free the std::istream itself, because it was instantiated inside user code rather than inside Zorba's own library. On Windows, in some circumstances, if a DLL deletes an object that was not instantiated inside that DLL, the application will crash. Therefore, the StreamResource factory function create() also takes a StreamReleaser, which is a function pointer. Zorba will call this function pointer, passing the std::istream, when it is no longer needed; the function is expected to free the std::istream.Unlike mapURI(), it is acceptable for resolveURL to throw exceptions. A URL Resolver should throw an exception if it believes that it is canonical for the URL (that is, it "should be able" to resolve it) but had some error during the attempt to resolve it. However, because Zorba may be attempting to resolve a number of candidate URIs, any exceptions thrown from a URL Resolver will be caught and consumed by Zorba. It will never re-throw any of these exceptions. It will merely remember the message of the first exception (assuming that it extends std::exception). If and only if no URL Resolver ever returns a valid Resource, Zorba will then throw a new exception with the saved message from the first-thrown exception.
class MyModuleURIMapper : public URIMapper { public: virtual ~MyModuleURIMapper() {} virtual URIMapper::Kind mapperKind() throw() { return URIMapper::COMPONENT; } virtual void mapURI(const zorba::String aUri, EntityData const* aEntityData, std::vector<zorba::String>& oUris) { if (aEntityData->getKind() != EntityData::MODULE) { return; } if(aUri == "http://www.example.com/mymodule") { oUris.push_back("http://www.example.com/mymodule/mod1"); oUris.push_back("http://www.example.com/mymodule/mod2"); } } };As mentioned, each of the component URIs will be treated to the full URI resolution mechanism, including Zorba's built-in mechanisms. So, given the code above and a default Unix installation, Zorba will proceed to attempt to load the following files:
/usr/share/zorba-2.0.0/modules/com/example/www/mymodule/mod1.xq /usr/share/zorba-2.0.0/modules/com/example/www/mymodule/mod2.xqIf both are found, then the two together will be taken to form the complete definition for the module .
class DenyFileAccessURIMapper : public URIMapper { public: virtual ~DenyFileAccessURIMapper() {} virtual void mapURI(const zorba::String aUri, EntityData const* aEntityData, std::vector<zorba::String>& oUris) { if(aEntityData->getKind() == EntityData::MODULE && aUri == "http://www.zorba-xquery.com/modules/file") { oUris.push_back(URIMapper::DENY_ACCESS); } } };