Browse Source

Add support for Include statements in manifest files

Will improve ability to organize and extend visualitation files.

Precalculate a new universal path for resources in the finalized manifest
in order to support the include call back mechanisms for both libxslt
and xalan across various client and server combinations on
multiple operating systems.

Signed-off-by: Anthony Fishbeck <Anthony.Fishbeck@lexisnexis.com>
Anthony Fishbeck 13 years ago
parent
commit
2145127605

+ 57 - 38
common/wuwebview/wuwebview.cpp

@@ -302,6 +302,8 @@ public:
 
     void calculateResourceIncludePaths();
     virtual bool getInclude(const char *includename, MemoryBuffer &includebuf, bool &pathOnly);
+    bool getEspInclude(const char *includename, MemoryBuffer &includebuf, bool &pathOnly);
+
 
     void addVariableFromPTree(IWorkUnit *w, IConstWUResult &vardef, IResultSetMetaData &metadef, const char *varname, IPropertyTree *valtree);
     void addInputsFromPTree(IPropertyTree *pt);
@@ -339,13 +341,46 @@ void WuWebView::calculateResourceIncludePaths()
         Owned<IPropertyTreeIterator> iter = ensureManifest()->getElements("Resource[@filename]");
         ForEach(*iter)
         {
-            StringBuffer abspath;
-            iter->query().setProp("@res_include_path", makeAbsolutePath(iter->query().queryProp("@filename"), dir.get(), abspath).str());
+            if (!iter->query().hasProp("@resourcePath")) //backward compatible
+            {
+                StringBuffer abspath;
+                makeAbsolutePath(iter->query().queryProp("@filename"), dir.get(), abspath);
+
+                StringBuffer respath;
+                makePathUniversal(abspath.str(), respath);
+                iter->query().setProp("@resourcePath", respath.str());
+            }
         }
         manifestIncludePathsSet=true;
     }
 }
 
+bool WuWebView::getEspInclude(const char *includename, MemoryBuffer &includebuf, bool &pathOnly)
+{
+    StringBuffer absPath;
+    makeAbsolutePath(includename, dir.get(), absPath);
+    if (checkFileExists(absPath.str()))
+    {
+        Owned <IFile> f = createIFile(absPath.str());
+        Owned <IFileIO> fio = f->open(IFOread);
+        read(fio, 0, (size32_t) f->size(), includebuf);
+    }
+    //esp looks in two places for path starting with "xslt/"
+    else if (mapEspDirectories && !strncmp(includename, "xslt/", 5))
+    {
+        absPath.clear().append(dir.get());
+        absPath.append("smc_").append(includename);;
+        makeAbsolutePath(absPath);
+        if (checkFileExists(absPath.str()))
+        {
+            Owned <IFile> f = createIFile(absPath.str());
+            Owned <IFileIO> fio = f->open(IFOread);
+            read(fio, 0, (size32_t) f->size(), includebuf);
+        }
+    }
+    return true;
+}
+
 bool WuWebView::getInclude(const char *includename, MemoryBuffer &includebuf, bool &pathOnly)
 {
     int len=strlen(includename);
@@ -357,18 +392,13 @@ bool WuWebView::getInclude(const char *includename, MemoryBuffer &includebuf, bo
     //eliminate extra '/' for windows absolute paths
     if (len>9 && includename[2]==':')
         includename++;
-    StringBuffer relpath;
     if (mapEspDirectories && !strnicmp(includename, "/esp/", 5))
-        relpath.append(includename+=5);
-    else
-        relpath.append(includename); //still correct for OS
-    StringBuffer abspath;
-    makeAbsolutePath(relpath.str(), dir.get(), abspath);
+        return getEspInclude(includename+5, includebuf, pathOnly);
 
     IPropertyTree *res = NULL;
     if (manifest)
     {
-        VStringBuffer xpath("Resource[@res_include_path='%s']", abspath.str());
+        VStringBuffer xpath("Resource[@resourcePath='%s']", includename);
         res = manifest->queryPropTree(xpath.str());
     }
     if (res)
@@ -377,25 +407,12 @@ bool WuWebView::getInclude(const char *includename, MemoryBuffer &includebuf, bo
         getResource(res, xslt);
         includebuf.append(xslt.str());
     }
-    else if (checkFileExists(abspath.str()))
+    else if (checkFileExists(includename))
     {
-        Owned <IFile> f = createIFile(abspath.str());
+        Owned <IFile> f = createIFile(includename);
         Owned <IFileIO> fio = f->open(IFOread);
         read(fio, 0, (size32_t) f->size(), includebuf);
     }
-    //esp looks in two places for path starting with "xslt/"
-    else if (mapEspDirectories && !strncmp(includename, "xslt/", 5))
-    {
-        StringBuffer relpath(dir.get());
-        relpath.append("smc_").append(includename);;
-        makeAbsolutePath(relpath.str(), abspath.clear());
-        if (checkFileExists(abspath.str()))
-        {
-            Owned <IFile> f = createIFile(abspath.str());
-            Owned <IFileIO> fio = f->open(IFOread);
-            read(fio, 0, (size32_t) f->size(), includebuf);
-        }
-    }
     return true;
 }
 
@@ -436,7 +453,7 @@ void WuWebView::getResource(const char *name, StringBuffer &content, StringBuffe
         xpath.append("[@type='").append(type).append("']");
     IPropertyTree *res = ensureManifest()->queryPropTree(xpath.str());
     calculateResourceIncludePaths();
-    includepath.append(res->queryProp("@res_include_path"));
+    includepath.append(res->queryProp("@resourcePath"));
     if (res)
         getResource(res, content);
 }
@@ -461,6 +478,8 @@ void WuWebView::getResultXSLT(const char *viewName, StringBuffer &xslt, StringBu
 void WuWebView::renderExpandedResults(const char *viewName, WuExpandedResultBuffer &expanded, StringBuffer &out)
 {
     IPropertyTree *mf = ensureManifest();
+    calculateResourceIncludePaths();
+
     VStringBuffer xpath("Views/Results[@name='%s']", viewName);
     IPropertyTree *view = mf->queryPropTree(xpath.str());
     if (!view)
@@ -471,24 +490,24 @@ void WuWebView::renderExpandedResults(const char *viewName, WuExpandedResultBuff
     const char *type=view->queryProp("@type");
     if (!type)
         throw MakeStringException(WUWEBERR_UnknownViewType, "No type defined for view %s", viewName);
-    else if (strieq(type, "xml"))
-    {
-        out.swapWith(expanded.buffer);
-        return;
-    }
-    else if (!strieq(type, "xslt"))
+    if (strieq(type, "xml"))
+        return out.swapWith(expanded.buffer);
+    if (!strieq(type, "xslt"))
         throw MakeStringException(WUWEBERR_UnknownViewType, "View %s has an unknown type of %s", viewName, type);
 
-    StringBuffer xslt;
-    StringBuffer rootpath;
-    getResultXSLT(viewName, xslt, rootpath);
-    if (!xslt.length())
-        throw MakeStringException(WUWEBERR_ViewResourceNotFound, "XSLT for %s view not found", viewName);
+    const char *resname = view->queryProp("@resource");
+    if (!resname || !*resname)
+        throw MakeStringException(WUWEBERR_ViewResourceNotFound, "resource for %s view not defined", viewName);
+    xpath.clear().appendf("Resource[@name='%s']/@resourcePath", resname);
+    const char *respath = ensureManifest()->queryProp(xpath.str());
+    if (!respath || !*respath)
+        throw MakeStringException(WUWEBERR_ViewResourceNotFound, "resource %s not resolved", resname);
+
     Owned<IXslTransform> t = getXslProcessor()->createXslTransform();
-    t->setIncludeHandler(this);
     StringBuffer cacheId(viewName);
     cacheId.append('@').append(dllname.str()); //using dllname, cloned workunits can share cache entry
-    t->setXslSource(xslt.str(), xslt.length(), cacheId.str(), rootpath.str());
+    t->setIncludeHandler(this);
+    t->loadXslFromEmbedded(respath, cacheId.str());
     t->setXmlSource(expanded.buffer.str(), expanded.buffer.length());
     t->transform(out);
 }

+ 76 - 32
ecl/hql/hqlmanifest.cpp

@@ -27,37 +27,86 @@ class ResourceManifest : public CInterface
 {
 public:
     ResourceManifest(const char *filename)
-        : manifest(createPTreeFromXMLFile(filename)), origPath(filename)
+        : manifest(createPTreeFromXMLFile(filename))
     {
         makeAbsolutePath(filename, absFilename);
         splitDirTail(absFilename, dir);
+        expand();
     }
 
     void addToArchive(IPropertyTree *archive);
     void loadResource(const char *filepath, MemoryBuffer &content);
     bool checkResourceFilesExist();
+private:
+    bool loadInclude(IPropertyTree &include, const char *dir);
+    void expand();
 public:
     Owned<IPropertyTree> manifest;
-    StringAttr origPath;
     StringBuffer absFilename;
     StringBuffer dir;
 };
 
-bool ResourceManifest::checkResourceFilesExist()
+void updateResourcePaths(IPropertyTree &resource, const char *dir)
+{
+    StringBuffer filepath;
+    makeAbsolutePath(resource.queryProp("@filename"), dir, filepath);
+    resource.setProp("@originalFilename", filepath.str());
+
+    StringBuffer respath;
+    makePathUniversal(filepath.str(), respath);
+    resource.setProp("@resourcePath", respath.str());
+}
+
+bool ResourceManifest::loadInclude(IPropertyTree &include, const char *dir)
+{
+    const char *filename = include.queryProp("@filename");
+    StringBuffer includePath;
+    makeAbsolutePath(filename, dir, includePath);
+
+    VStringBuffer xpath("Include[@originalFilename='%s']", includePath.str());
+    if (manifest->hasProp(xpath.str()))
+        return false;
+
+    include.setProp("@originalFilename", includePath.str());
+    StringBuffer includeDir;
+    splitDirTail(includePath, includeDir);
+
+    Owned<IPropertyTree> manifestInclude = createPTreeFromXMLFile(includePath.str());
+    Owned<IPropertyTreeIterator> it = manifestInclude->getElements("*");
+    ForEach(*it)
+    {
+        IPropertyTree &item = it->query();
+        if (streq(item.queryName(), "Resource"))
+            updateResourcePaths(item, includeDir.str());
+        else if (streq(item.queryName(), "Include"))
+        {
+            if (!loadInclude(item, includeDir.str()))
+                continue;
+        }
+        manifest->addPropTree(item.queryName(), LINK(&item));
+    }
+    return true;
+}
+
+void ResourceManifest::expand()
 {
     Owned<IPropertyTreeIterator> resources = manifest->getElements("Resource[@filename]");
     ForEach(*resources)
+        updateResourcePaths(resources->query(), dir.str());
+    Owned<IPropertyTreeIterator> includes = manifest->getElements("Include[@filename]");
+    ForEach(*includes)
+        loadInclude(includes->query(), dir.str());
+}
+
+bool ResourceManifest::checkResourceFilesExist()
+{
+    Owned<IPropertyTreeIterator> resources = manifest->getElements("Resource[@originalFilename]");
+    ForEach(*resources)
     {
-        const char *filename = resources->query().queryProp("@filename");
-        StringBuffer fullpath;
-        if (!isAbsolutePath(filename))
-            fullpath.append(dir);
-        fullpath.append(filename);
-        if (!checkFileExists(fullpath.str()))
+        const char *filepath = resources->query().queryProp("@originalFilename");
+        if (!checkFileExists(filepath))
         {
-            StringBuffer absResPath;
-            makeAbsolutePath(fullpath.str(), absResPath);
-            ERRLOG("Error: RESOURCE file '%s' does not exist", absResPath.str());
+            ERRLOG("Error: RESOURCE file '%s' does not exist", filepath);
             return false;
         }
     }
@@ -79,36 +128,31 @@ void ResourceManifest::addToArchive(IPropertyTree *archive)
     if (!additionalFiles->hasProp("@xmlns:xsi"))
         additionalFiles->setProp("@xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
 
-    StringBuffer xml;
-    toXML(manifest, xml);
-    additionalFiles->setProp("Manifest", xml.str());
-    additionalFiles->setProp("Manifest/@originalFilename", origPath.sget());
-
-    Owned<IPropertyTreeIterator> resources = manifest->getElements("Resource[@filename]");
+    Owned<IPropertyTreeIterator> resources = manifest->getElements("Resource[@resourcePath]");
     ForEach(*resources)
     {
-        StringBuffer absResPath;
-        const char *filename = resources->query().queryProp("@filename");
-        VStringBuffer xpath("Resource[@originalFilename='%s']", filename);
+        IPropertyTree &item = resources->query();
+        const char *respath = item.queryProp("@resourcePath");
+
+        VStringBuffer xpath("Resource[@resourcePath='%s']", respath);
         if (!additionalFiles->hasProp(xpath.str()))
         {
-            if (!isAbsolutePath(filename))
-            {
-                StringBuffer relResPath(dir);
-                relResPath.append(filename);
-                makeAbsolutePath(relResPath.str(), absResPath);
-            }
-            else
-                absResPath.append(filename);
-
             IPropertyTree *resTree = additionalFiles->addPropTree("Resource", createPTree("Resource"));
-            resTree->setProp("@originalFilename", filename);
+
+            const char *filepath = item.queryProp("@originalFilename");
+            resTree->setProp("@originalFilename", filepath);
+            resTree->setProp("@resourcePath", respath);
 
             MemoryBuffer content;
-            loadResource(absResPath.str(), content);
+            loadResource(filepath, content);
             resTree->setPropBin(NULL, content.length(), content.toByteArray());
         }
     }
+
+    StringBuffer xml;
+    toXML(manifest, xml);
+    additionalFiles->setProp("Manifest", xml.str());
+    additionalFiles->setProp("Manifest/@originalFilename", absFilename.str());
 }
 
 void addManifestResourcesToArchive(IPropertyTree *archive, const char *filename)

+ 52 - 17
ecl/hqlcpp/hqlres.cpp

@@ -106,9 +106,13 @@ static void loadResource(const char *filepath, MemoryBuffer &content)
     read(fio, 0, (size32_t) f->size(), content);
 }
 
-bool ResourceManager::getDuplicateResourceId(const char *srctype, const char *filename, int &id)
+bool ResourceManager::getDuplicateResourceId(const char *srctype, const char *respath, const char *filepath, int &id)
 {
-    VStringBuffer xpath("Resource[@filename='%s']", filename);
+    StringBuffer xpath;
+    if (respath && *respath)
+        xpath.appendf("Resource[@resourcePath='%s']", respath);
+    else
+        xpath.appendf("Resource[@originalFilename='%s']", filepath);
     Owned<IPropertyTreeIterator> iter = manifest->getElements(xpath.str());
     ForEach (*iter)
     {
@@ -126,10 +130,8 @@ bool ResourceManager::getDuplicateResourceId(const char *srctype, const char *fi
     return false;
 }
 
-void ResourceManager::addManifest(const char *filename)
+void ResourceManager::addManifestFile(const char *filename)
 {
-    if (finalized)
-        throwError1(HQLERR_ResourceAddAfterFinalManifest, "MANIFEST");
     Owned<IPropertyTree> manifestSrc = createPTreeFromXMLFile(filename);
 
     StringBuffer dir; 
@@ -143,26 +145,30 @@ void ResourceManager::addManifest(const char *filename)
     ForEach(*iter)
     {
         IPropertyTree &item = iter->query();
-        if (streq(item.queryName(), "Resource") && item.hasProp("@filename"))
+        if (streq(item.queryName(), "Include") && item.hasProp("@filename"))
+            addManifestInclude(item, dir.str());
+        else if (streq(item.queryName(), "Resource") && item.hasProp("@filename"))
         {
+            StringBuffer filepath;
+            StringBuffer respath;
+            makeAbsolutePath(item.queryProp("@filename"), dir.str(), filepath);
+            makePathUniversal(filepath.str(), respath);
+
+            item.setProp("@originalFilename", filepath.str());
+            item.setProp("@resourcePath", respath.str());
+
             if (!item.hasProp("@type"))
                 item.setProp("@type", "UNKNOWN");
-            const char *filename = item.queryProp("@filename");
             int id;
-            if (getDuplicateResourceId(item.queryProp("@type"), filename, id))
+            if (getDuplicateResourceId(item.queryProp("@type"), respath.str(), NULL, id))
             {
                 item.setPropInt("@id", id);
                 manifest->addPropTree("Resource", LINK(&item));
             }
             else
             {
-                StringBuffer fullpath;
-                if (!isAbsolutePath(filename))
-                    fullpath.append(dir);
-                fullpath.append(filename);
-
                 MemoryBuffer content;
-                loadResource(fullpath.str(), content);
+                loadResource(filepath.str(), content);
                 addCompress(item.queryProp("@type"), content.length(), content.toByteArray(), &item);
             }
         }
@@ -171,6 +177,27 @@ void ResourceManager::addManifest(const char *filename)
     }
 }
 
+void ResourceManager::addManifest(const char *filename)
+{
+    StringBuffer path;
+    Owned<IPropertyTree> t = createPTree();
+    t->setProp("@originalFilename", makeAbsolutePath(filename, path).str());
+    manifest->addPropTree("Include", t.getClear());
+    addManifestFile(filename);
+}
+
+void ResourceManager::addManifestInclude(IPropertyTree &include, const char *dir)
+{
+    StringBuffer includePath;
+    makeAbsolutePath(include.queryProp("@filename"), dir, includePath);
+    VStringBuffer xpath("Include[@originalFilename='%s']", includePath.str());
+    if (manifest->hasProp(xpath.str()))
+        return;
+    include.setProp("@originalFilename", includePath.str());
+    manifest->addPropTree("Include", LINK(&include));
+    addManifestFile(includePath.str());
+}
+
 void ResourceManager::addManifestFromArchive(IPropertyTree *archive)
 {
     if (!archive)
@@ -186,17 +213,25 @@ void ResourceManager::addManifestFromArchive(IPropertyTree *archive)
         Owned<IAttributeIterator> aiter = manifestSrc->getAttributes();
         ForEach (*aiter)
             manifest->setProp(aiter->queryName(), aiter->queryValue());
+        StringBuffer manifestDir;
+        if (manifestSrc->hasProp("@originalFilename"))
+            splitDirTail(manifestSrc->queryProp("@originalFilename"), manifestDir);
+
         Owned<IPropertyTreeIterator> iter = manifestSrc->getElements("*");
         ForEach(*iter)
         {
             IPropertyTree &item = iter->query();
-            if (streq(item.queryName(), "Resource")&& item.hasProp("@filename"))
+            if (streq(item.queryName(), "Resource") && item.hasProp("@filename"))
             {
                 if (!item.hasProp("@type"))
                     item.setProp("@type", "UNKNOWN");
-                const char *filename = item.queryProp("@filename");
+                const char *filename;
+                if (item.hasProp("@originalFilename"))
+                    filename = item.queryProp("@originalFilename");
+                else
+                    filename = item.queryProp("@filename");
                 int id;
-                if (getDuplicateResourceId(item.queryProp("@type"), filename, id))
+                if (getDuplicateResourceId(item.queryProp("@type"), NULL, filename, id))
                 {
                     item.setPropInt("@id", (int)id);
                     manifest->addPropTree("Resource", LINK(&item));

+ 3 - 1
ecl/hqlcpp/hqlres.hpp

@@ -36,7 +36,7 @@ public:
     void addManifestFromArchive(IPropertyTree *archive);
     void addWebServiceInfo(IPropertyTree *wsinfo);
     IPropertyTree *ensureManifestInfo(){if (!manifest) manifest.setown(createPTree("Manifest")); return manifest;}
-    bool getDuplicateResourceId(const char *srctype, const char *filename, int &id);
+    bool getDuplicateResourceId(const char *srctype, const char *respath, const char *filename, int &id);
     void finalize();
 
     unsigned count();
@@ -45,6 +45,8 @@ public:
     bool queryWriteText(StringBuffer & resTextName, const char * filename);
 private:
     void putbytes(int h, const void *b, unsigned len);
+    void addManifestFile(const char *filename);
+    void addManifestInclude(IPropertyTree &include, const char *dir);
 };
 
 #endif

+ 14 - 0
system/jlib/jfile.cpp

@@ -4842,6 +4842,20 @@ IFile * createIFile(const RemoteFilename & filename)
     return createIFile(getLocalOrRemoteName(name,filename).str());
 }
 
+StringBuffer &makePathUniversal(const char *path, StringBuffer &out)
+{
+    if (!path||!*path)
+        return out;
+    if (path[1]==':')
+    {
+        out.append('/').append(*path);
+        path+=2;
+    }
+    for (; *path; path++)
+        out.append(isPathSepChar(*path) ? '/' : *path);
+    return out;
+}
+
 StringBuffer &makeAbsolutePath(const char *relpath,StringBuffer &out)
 {
     if (isPathSepChar(relpath[0])&&(relpath[0]==relpath[1]))

+ 1 - 0
system/jlib/jfile.hpp

@@ -539,6 +539,7 @@ inline bool isAbsolutePath(const char *path)
 extern jlib_decl StringBuffer &makeAbsolutePath(const char *relpath,StringBuffer &out);
 extern jlib_decl StringBuffer &makeAbsolutePath(StringBuffer &relpath);
 extern jlib_decl StringBuffer &makeAbsolutePath(const char *relpath, const char *basedir, StringBuffer &out);
+extern jlib_decl StringBuffer &makePathUniversal(const char *path, StringBuffer &out);
 extern jlib_decl const char *splitRelativePath(const char *full,const char *basedir,StringBuffer &reldir); // removes basedir if matches, returns tail and relative dir
 extern jlib_decl const char *splitDirMultiTail(const char *multipath,StringBuffer &dir,StringBuffer &tail);
 extern jlib_decl StringBuffer &mergeDirMultiTail(const char *dir,const char *tail, StringBuffer &multipath);

+ 40 - 13
system/xmllib/libxslt_processor.cpp

@@ -30,6 +30,7 @@
 extern int xmlLoadExtDtdDefaultValue;
 xsltDocLoaderFunc originalLibXsltIncludeHandler = NULL;
 
+xmlDocPtr libXsltIncludeHandler(const xmlChar * URI, xmlDictPtr dict, int options, IIncludeHandler* handler, xsltLoadType type);
 xmlDocPtr globalLibXsltIncludeHandler(const xmlChar * URI, xmlDictPtr dict, int options, void *ctx, xsltLoadType type);
 
 void libxsltCustomMessageHandler(StringBuffer& out, const char* in, IXslTransform* transform);
@@ -44,10 +45,25 @@ public:
         srcType = IO_TYPE_FILE;
     }
 
+    CLibXsltSource(IIncludeHandler *handler, const char* rootpath, const char *_cacheId) : cacheId(_cacheId), compiledXslt(NULL), filename(rootpath)
+    {
+        srcType = IO_TYPE_BUFFER;
+        bool pathOnly=false;
+        MemoryBuffer mb;
+        if (!handler->getInclude(rootpath, mb, pathOnly) || pathOnly || !mb.length())
+            throw MakeStringException(XSLERR_InvalidSource, "Failed to load XSLT resource path %s\n", rootpath);
+        text.set(mb.toByteArray(), mb.length());
+        StringBuffer s("file://");
+        if (*rootpath!='/')
+            s.append('/');
+        filename.set(s.append(rootpath).str());
+    }
+
     CLibXsltSource(const char* s, int len, const char *_cacheId) : cacheId(_cacheId), compiledXslt(NULL)
     {
         srcType = IO_TYPE_BUFFER;
         text.set(s, len);
+        filename.set("buffer.xslt");
     }
 
     virtual ~CLibXsltSource()
@@ -105,7 +121,6 @@ xsltStylesheetPtr CLibXsltSource::parseXsltFile()
         return NULL;
 
     xmlDocPtr doc = xsltDocDefaultLoader((xmlChar *)filename.get(), NULL, XSLT_PARSE_OPTIONS, NULL, XSLT_LOAD_START);
-
     if (!doc)
         throw MakeStringException(XSLERR_InvalidSource, "Failed to parse XSLT source\n");
     doc->_private = static_cast<void *>(this);
@@ -139,7 +154,7 @@ void CLibXsltSource::compile()
                 compiledXslt = parseXsltFile();
             else if (srcType == IO_TYPE_BUFFER)
             {
-                xmlDocPtr xsldoc = xmlReadMemory(text.get(), text.length(), "inmemory.xslt", NULL, 0);
+                xmlDocPtr xsldoc = xmlReadMemory(text.get(), text.length(), filename.get(), NULL, 0);
                 if (!xsldoc)
                     throw MakeStringException(XSLERR_InvalidSource, "XSLT source contains invalid XML\n");
                 xsldoc->_private=(void*)this;
@@ -328,6 +343,7 @@ public:
     virtual int setXslSource(const char *pszBuffer, unsigned int nSize, const char *cacheId, const char *rootpath);
     virtual int setXslNoCache(const char *pszBuffer, unsigned int nSize, const char *rootpath=NULL);
     virtual int loadXslFromFile(const char *pszFileName, const char *altCacheId=NULL);
+    virtual int loadXslFromEmbedded(const char *path, const char *cacheId);
 
     virtual int setResultTarget(char *pszBuffer, unsigned int nSize);
     virtual int setResultTarget(const char *pszFileName);
@@ -597,6 +613,12 @@ int CLibXslTransform::setXslSource(const char *pszBuffer, unsigned int nSize, co
     return 0;
 }
 
+int CLibXslTransform::loadXslFromEmbedded(const char *path, const char *cacheId)
+{
+    xslSrc.setown(new CLibXsltSource(includeHandler.get(), path, cacheId));
+    return 0;
+}
+
 int CLibXslTransform::setXslNoCache(const char *pszBuffer, unsigned int nSize, const char *rootpath)
 {
     xslSrc.setown(new CLibXsltSource(pszBuffer, nSize, NULL));
@@ -728,6 +750,19 @@ CLibXsltSource *getXsltStylesheetSourceObject(xsltStylesheetPtr x)
     return static_cast<CLibXsltSource *>(x->_private);
 }
 
+xmlDocPtr libXsltIncludeHandler(const xmlChar * URI, xmlDictPtr dict, int options, IIncludeHandler* handler, xsltLoadType type)
+{
+    bool mbContainsPath=false;
+    MemoryBuffer mb;
+    if (!handler->getInclude((const char *)URI, mb, mbContainsPath))
+        return originalLibXsltIncludeHandler(URI, dict, options, NULL, type);
+    if (mbContainsPath)
+        return originalLibXsltIncludeHandler((const xmlChar *)mb.append((char)0).toByteArray(), dict, options, NULL, type);
+    if (mb.length())
+        return xmlReadMemory(mb.toByteArray(), mb.length(), (const char *)URI, NULL, 0);
+    return NULL;
+}
+
 IIncludeHandler* getXsltStylesheetIncludeHandler(xsltStylesheetPtr x, IIncludeHandler *def)
 {
     CLibXsltSource *src = getXsltStylesheetSourceObject(x);
@@ -741,17 +776,9 @@ xmlDocPtr globalLibXsltIncludeHandler(const xmlChar * URI, xmlDictPtr dict, int
         x = ((xsltTransformContextPtr)ctx)->style;
 
     IIncludeHandler* handler = getXsltStylesheetIncludeHandler(x, xslProcessor.queryDefIncludeHandler());
-    if (!handler)
-        return originalLibXsltIncludeHandler(URI, dict, options, ctx, type);
-    bool mbContainsPath=false;
-    MemoryBuffer mb;
-    if (!handler->getInclude((const char *)URI, mb, mbContainsPath))
-        return originalLibXsltIncludeHandler(URI, dict, options, ctx, type);
-    if (mbContainsPath)
-        return originalLibXsltIncludeHandler((const xmlChar *)mb.append((char)0).toByteArray(), dict, options, ctx, type);
-    if (mb.length())
-        return xmlReadMemory(mb.toByteArray(), mb.length(), (const char *)URI, NULL, 0);
-    return NULL;
+    if (handler)
+        return libXsltIncludeHandler(URI, dict, options, handler, type);
+    return originalLibXsltIncludeHandler(URI, dict, options, ctx, type);
 }
 
 void libxsltCustomMessageHandler(StringBuffer& out, const char* in, IXslTransform* trans)

+ 7 - 0
system/xmllib/xalan_processor.cpp

@@ -552,6 +552,13 @@ int CXslTransform::loadXslFromFile(const char *pszFileName, const char *cacheId)
     return 0;
 }
 
+int CXslTransform::loadXslFromEmbedded(const char *path, const char *cacheId)
+{
+    m_xslsource.setown(new CXslSource(m_sourceResolver?m_sourceResolver->getIncludeHandler():NULL, cacheId, path));
+
+    return 0;
+}
+
 int CXslTransform::setXslSource(const char *pszBuffer, unsigned int nSize, const char *cacheId, const char *rootpath)
 {
     assertex(cacheId && *cacheId);

+ 21 - 1
system/xmllib/xalan_processor.ipp

@@ -170,7 +170,7 @@ private:
 public:
     IMPLEMENT_IINTERFACE;
 
-    CXslSource(const char* fname, IIncludeHandler* handler, const char *cacheId=NULL) : m_XalanTransformer()
+    CXslSource(const char* fname, IIncludeHandler* handler, const char *cacheId) : m_XalanTransformer()
     {
         m_filename.set(fname);
         m_sourcetype = IO_TYPE_FILE;
@@ -181,6 +181,25 @@ public:
             setIncludeHandler(handler);
     }
 
+    CXslSource(IIncludeHandler* handler, const char *cacheId, const char *rootpath) : m_XalanTransformer()
+    {
+        if (!handler)
+            throw MakeStringException(-1, "xsl embedded include handler not set");
+        if (!rootpath || !*rootpath)
+            throw MakeStringException(-1, "xsl embedded resource path not set");
+        m_sourcetype = IO_TYPE_BUFFER;
+        m_CompiledStylesheet = NULL;
+        m_cacheId.set(cacheId);
+
+        MemoryBuffer mb;
+        bool pathOnly=false;
+        if (handler->getInclude(rootpath, mb, pathOnly) && mb.length())
+            m_xsltext.append(mb.length(), mb.toByteArray());
+
+        setIncludeHandler(handler);
+        m_rootpath.set(rootpath);
+    }
+
     CXslSource(const char* buf, int len, IIncludeHandler* handler, const char *cacheId, const char *rootpath = NULL) : m_XalanTransformer()
     {
         m_xsltext.append(len, buf);
@@ -510,6 +529,7 @@ public:
     virtual int setXmlSource(const char *pszFileName);
     virtual int setXmlSource(const char *pszBuffer, unsigned int nSize);
     virtual int loadXslFromFile(const char *pszFileName, const char *altCacheId=NULL);
+    virtual int loadXslFromEmbedded(const char *path, const char *cacheId);
     virtual int setXslSource(const char *pszBuffer, unsigned int nSize, const char *cacheId, const char *rootpath);
     virtual int setXslNoCache(const char *pszBuffer, unsigned int nSize, const char *rootpath);
     virtual int setResultTarget(char *pszBuffer, unsigned int nSize);

+ 1 - 0
system/xmllib/xslprocessor.hpp

@@ -56,6 +56,7 @@ public:
     virtual int setXslSource(const char *pszBuffer, unsigned int nSize, const char *cacheId, const char *rootpath) = 0;
     virtual int setXslNoCache(const char *pszBuffer, unsigned int nSize, const char *rootpath=NULL) = 0;
     virtual int loadXslFromFile(const char *pszFileName, const char *altCacheId=NULL) = 0;
+    virtual int loadXslFromEmbedded(const char *path, const char *cacheId) = 0;
 
     // setResultTarget - Specifies where the post transform data is to be placed
     // must be set before calling parameterless variety of transform