mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
SAIL-244: Added an XSLT Template Processor (hooked into the template service) along with an XSLT rendering engine that makes use of it.
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@19153 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -135,6 +135,23 @@
|
|||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<bean id="xsltRenderingEngine"
|
||||||
|
class="org.alfresco.repo.rendition.executer.XSLTRenderingEngine"
|
||||||
|
parent="baseRenderingAction">
|
||||||
|
<property name="templateService" ref="TemplateService" />
|
||||||
|
<property name="xsltFunctions" ref="xsltFunctions" />
|
||||||
|
<property name="namespacePrefixResolver" ref="NamespaceService" />
|
||||||
|
<property name="fileFolderService" ref="FileFolderService" />
|
||||||
|
<property name="searchService" ref="SearchService" />
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="xsltFunctions" class="org.alfresco.repo.rendition.executer.XSLTFunctions">
|
||||||
|
<property name="fileService" ref="FileFolderService" />
|
||||||
|
<property name="contentService" ref="ContentService" />
|
||||||
|
<property name="nodeService" ref="NodeService" />
|
||||||
|
<property name="namespaceService" ref="NamespaceService" />
|
||||||
|
<property name="dictionaryService" ref="DictionaryService" />
|
||||||
|
</bean>
|
||||||
|
|
||||||
<!-- Behaviours and policies for Renditions -->
|
<!-- Behaviours and policies for Renditions -->
|
||||||
|
|
||||||
|
@@ -31,6 +31,15 @@
|
|||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<bean id="xsltProcessor" parent="baseTemplateProcessor" class="org.alfresco.repo.template.XSLTProcessor">
|
||||||
|
<property name="name">
|
||||||
|
<value>xslt</value>
|
||||||
|
</property>
|
||||||
|
<property name="extension">
|
||||||
|
<value>xsl</value>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
<!-- base config implementation that template extension beans extend from - for auto registration
|
<!-- base config implementation that template extension beans extend from - for auto registration
|
||||||
as a global template helper with the TemplateService -->
|
as a global template helper with the TemplateService -->
|
||||||
<bean id="baseTemplateImplementation" abstract="true" init-method="register">
|
<bean id="baseTemplateImplementation" abstract="true" init-method="register">
|
||||||
|
@@ -235,6 +235,35 @@ public class RenditionServiceIntegrationTest extends BaseAlfrescoSpringTest
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testRenderFreeMarkerTemplateOneTransaction() throws Exception
|
||||||
|
{
|
||||||
|
this.setComplete();
|
||||||
|
this.endTransaction();
|
||||||
|
final QName renditionName = QName.createQName(NamespaceService.RENDITION_MODEL_1_0_URI,
|
||||||
|
TemplatingRenderingEngine.NAME);
|
||||||
|
|
||||||
|
this.renditionNode = transactionHelper
|
||||||
|
.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<NodeRef>()
|
||||||
|
{
|
||||||
|
public NodeRef execute() throws Throwable
|
||||||
|
{
|
||||||
|
// create test model
|
||||||
|
RenditionDefinition definition = renditionService.createRenditionDefinition(renditionName,
|
||||||
|
TemplatingRenderingEngine.NAME);
|
||||||
|
definition.setParameterValue(TemplatingRenderingEngine.PARAM_TEMPLATE_NODE,
|
||||||
|
nodeWithFreeMarkerContent);
|
||||||
|
ChildAssociationRef renditionAssoc = renditionService
|
||||||
|
.render(nodeWithDocContent, definition);
|
||||||
|
assertNotNull("The rendition association was null", renditionAssoc);
|
||||||
|
String output = readTextContent(renditionAssoc.getChildRef());
|
||||||
|
assertNotNull("The rendition content was null.", output);
|
||||||
|
// check the output contains root node Id as expected.
|
||||||
|
assertTrue(output.contains(nodeWithDocContent.getId()));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public void testRenderFreemarkerTemplatePath() throws Exception
|
public void testRenderFreemarkerTemplatePath() throws Exception
|
||||||
{
|
{
|
||||||
//TODO displayName paths.
|
//TODO displayName paths.
|
||||||
|
@@ -0,0 +1,278 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2010 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU 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 General Public License for more details.
|
||||||
|
|
||||||
|
* You should have received a copy of the GNU 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.
|
||||||
|
|
||||||
|
* As a special exception to the terms and conditions of version 2.0 of
|
||||||
|
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||||
|
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||||
|
* FLOSS exception. You should have received a copy of the text describing
|
||||||
|
* the FLOSS exception, and it is also available here:
|
||||||
|
* http://www.alfresco.com/legal/licensing"
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.alfresco.repo.rendition.executer;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
import org.alfresco.model.ContentModel;
|
||||||
|
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||||
|
import org.alfresco.service.cmr.model.FileFolderService;
|
||||||
|
import org.alfresco.service.cmr.model.FileInfo;
|
||||||
|
import org.alfresco.service.cmr.model.FileNotFoundException;
|
||||||
|
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||||
|
import org.alfresco.service.cmr.repository.ContentService;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
|
import org.alfresco.service.namespace.NamespaceService;
|
||||||
|
import org.alfresco.service.namespace.QName;
|
||||||
|
import org.alfresco.util.XMLUtil;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Brian Remmington
|
||||||
|
* @since 3.3
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class XSLTFunctions
|
||||||
|
{
|
||||||
|
private static final Log log = LogFactory.getLog(XSLTFunctions.class);
|
||||||
|
|
||||||
|
private FileFolderService fileService;
|
||||||
|
private ContentService contentService;
|
||||||
|
private NodeService nodeService;
|
||||||
|
private NamespaceService namespaceService;
|
||||||
|
private DictionaryService dictionaryService;
|
||||||
|
|
||||||
|
public XSLTFunctions()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public Document parseXMLDocument(final NodeRef root, String repoPath) throws IOException, SAXException,
|
||||||
|
FileNotFoundException
|
||||||
|
{
|
||||||
|
String[] pathElements = breakDownPath(repoPath);
|
||||||
|
FileInfo file = fileService.resolveNamePath(root, Arrays.asList(pathElements));
|
||||||
|
return XMLUtil.parse(file.getNodeRef(), contentService);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Document> parseXMLDocuments(final String typeName, NodeRef rootNode, String repoPath)
|
||||||
|
throws IOException, SAXException
|
||||||
|
{
|
||||||
|
final Map<String, Document> result = new TreeMap<String, Document>();
|
||||||
|
|
||||||
|
String[] pathElements = breakDownPath(repoPath);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
FileInfo file = fileService.resolveNamePath(rootNode, Arrays.asList(pathElements));
|
||||||
|
|
||||||
|
if (file.isFolder())
|
||||||
|
{
|
||||||
|
QName typeQName = QName.createQName(typeName, namespaceService);
|
||||||
|
Set<QName> types = new HashSet<QName>(dictionaryService.getSubTypes(typeQName, true));
|
||||||
|
types.add(typeQName);
|
||||||
|
List<ChildAssociationRef> children = nodeService.getChildAssocs(file.getNodeRef(), types);
|
||||||
|
for (ChildAssociationRef child : children)
|
||||||
|
{
|
||||||
|
String name = (String) nodeService.getProperty(child.getChildRef(), ContentModel.PROP_NAME);
|
||||||
|
Document doc = XMLUtil.parse(child.getChildRef(), contentService);
|
||||||
|
result.put(name, doc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
log.warn("Unexpected exception caught in call to parseXMLDocuments", ex);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param repoPath
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private String[] breakDownPath(String repoPath)
|
||||||
|
{
|
||||||
|
if (repoPath.startsWith("/"))
|
||||||
|
{
|
||||||
|
repoPath = repoPath.substring(1);
|
||||||
|
}
|
||||||
|
String[] pathElements = repoPath.split("/");
|
||||||
|
return pathElements;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes invalid HTML characters. (Fix for ETWOTWO-504 issue) This code was adopted from WebDAVHelper.encodeHTML()
|
||||||
|
* method with some restrictions.
|
||||||
|
*
|
||||||
|
* @see press-release.xsl for pattern.
|
||||||
|
*
|
||||||
|
* @param text
|
||||||
|
* to encode
|
||||||
|
* @return encoded text
|
||||||
|
* @throws IOException
|
||||||
|
* @throws SAXException
|
||||||
|
*/
|
||||||
|
public String encodeQuotes(String text) throws IOException, SAXException
|
||||||
|
{
|
||||||
|
if (text == null)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sb = null; // create on demand
|
||||||
|
String enc;
|
||||||
|
char c;
|
||||||
|
for (int i = 0; i < text.length(); i++)
|
||||||
|
{
|
||||||
|
enc = null;
|
||||||
|
c = text.charAt(i);
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case '"':
|
||||||
|
enc = """;
|
||||||
|
break; // "
|
||||||
|
// case '&': enc = "&"; break; //&
|
||||||
|
// case '<': enc = "<"; break; //<
|
||||||
|
// case '>': enc = ">"; break; //>
|
||||||
|
|
||||||
|
// german umlauts
|
||||||
|
case '\u00E4':
|
||||||
|
enc = "ä";
|
||||||
|
break;
|
||||||
|
case '\u00C4':
|
||||||
|
enc = "Ä";
|
||||||
|
break;
|
||||||
|
case '\u00F6':
|
||||||
|
enc = "ö";
|
||||||
|
break;
|
||||||
|
case '\u00D6':
|
||||||
|
enc = "Ö";
|
||||||
|
break;
|
||||||
|
case '\u00FC':
|
||||||
|
enc = "ü";
|
||||||
|
break;
|
||||||
|
case '\u00DC':
|
||||||
|
enc = "Ü";
|
||||||
|
break;
|
||||||
|
case '\u00DF':
|
||||||
|
enc = "ß";
|
||||||
|
break;
|
||||||
|
|
||||||
|
// misc
|
||||||
|
// case 0x80: enc = "€"; break; sometimes euro symbol is ascii 128, should we suport it?
|
||||||
|
case '\u20AC':
|
||||||
|
enc = "€";
|
||||||
|
break;
|
||||||
|
case '\u00AB':
|
||||||
|
enc = "«";
|
||||||
|
break;
|
||||||
|
case '\u00BB':
|
||||||
|
enc = "»";
|
||||||
|
break;
|
||||||
|
case '\u00A0':
|
||||||
|
enc = " ";
|
||||||
|
break;
|
||||||
|
|
||||||
|
// case '': enc = "&trade"; break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (((int) c) >= 0x80)
|
||||||
|
{
|
||||||
|
// encode all non basic latin characters
|
||||||
|
enc = "&#" + ((int) c) + ";";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enc != null)
|
||||||
|
{
|
||||||
|
if (sb == null)
|
||||||
|
{
|
||||||
|
String soFar = text.substring(0, i);
|
||||||
|
sb = new StringBuilder(i + 8);
|
||||||
|
sb.append(soFar);
|
||||||
|
}
|
||||||
|
sb.append(enc);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (sb != null)
|
||||||
|
{
|
||||||
|
sb.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sb == null)
|
||||||
|
{
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param fileService the fileService to set
|
||||||
|
*/
|
||||||
|
public void setFileService(FileFolderService fileService)
|
||||||
|
{
|
||||||
|
this.fileService = fileService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param contentService the contentService to set
|
||||||
|
*/
|
||||||
|
public void setContentService(ContentService contentService)
|
||||||
|
{
|
||||||
|
this.contentService = contentService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param nodeService the nodeService to set
|
||||||
|
*/
|
||||||
|
public void setNodeService(NodeService nodeService)
|
||||||
|
{
|
||||||
|
this.nodeService = nodeService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param namespaceService the namespaceService to set
|
||||||
|
*/
|
||||||
|
public void setNamespaceService(NamespaceService namespaceService)
|
||||||
|
{
|
||||||
|
this.namespaceService = namespaceService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param dictionaryService the dictionaryService to set
|
||||||
|
*/
|
||||||
|
public void setDictionaryService(DictionaryService dictionaryService)
|
||||||
|
{
|
||||||
|
this.dictionaryService = dictionaryService;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,397 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2010 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU 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 General Public License for more details.
|
||||||
|
|
||||||
|
* You should have received a copy of the GNU 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.
|
||||||
|
|
||||||
|
* As a special exception to the terms and conditions of version 2.0 of
|
||||||
|
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||||
|
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||||
|
* FLOSS exception. You should have received a copy of the text describing
|
||||||
|
* the FLOSS exception, and it is also available here:
|
||||||
|
* http://www.alfresco.com/legal/licensing"
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.alfresco.repo.rendition.executer;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
import org.alfresco.model.ContentModel;
|
||||||
|
import org.alfresco.repo.model.filefolder.FileFolderServiceImpl;
|
||||||
|
import org.alfresco.service.cmr.model.FileFolderService;
|
||||||
|
import org.alfresco.service.cmr.model.FileInfo;
|
||||||
|
import org.alfresco.service.cmr.repository.ContentService;
|
||||||
|
import org.alfresco.service.cmr.repository.ContentWriter;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
|
import org.alfresco.service.cmr.repository.StoreRef;
|
||||||
|
import org.alfresco.service.cmr.search.ResultSet;
|
||||||
|
import org.alfresco.service.cmr.search.SearchService;
|
||||||
|
import org.alfresco.util.BaseAlfrescoSpringTest;
|
||||||
|
import org.alfresco.util.GUID;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.NodeList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Brian
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class XSLTFunctionsTest extends BaseAlfrescoSpringTest
|
||||||
|
{
|
||||||
|
private final static Log log = LogFactory.getLog(XSLTFunctionsTest.class);
|
||||||
|
private XSLTFunctions xsltFunctions;
|
||||||
|
private SearchService searchService;
|
||||||
|
private NodeRef companyHome;
|
||||||
|
private FileFolderService fileFolderService;
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see org.alfresco.util.BaseAlfrescoSpringTest#onSetUpInTransaction()
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
@Override
|
||||||
|
protected void onSetUpInTransaction() throws Exception
|
||||||
|
{
|
||||||
|
super.onSetUpInTransaction();
|
||||||
|
this.searchService = (SearchService) this.applicationContext.getBean("SearchService");
|
||||||
|
this.xsltFunctions = (XSLTFunctions) this.applicationContext.getBean("xsltFunctions");
|
||||||
|
this.nodeService = (NodeService) this.applicationContext.getBean("NodeService");
|
||||||
|
this.contentService = (ContentService) this.applicationContext.getBean("ContentService");
|
||||||
|
this.fileFolderService = (FileFolderService) this.applicationContext.getBean("FileFolderService");
|
||||||
|
ResultSet rs = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, SearchService.LANGUAGE_XPATH, "/app:company_home");
|
||||||
|
this.companyHome = rs.getNodeRef(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSimplestParseXMLDocument()
|
||||||
|
{
|
||||||
|
FileInfo file = createXmlFile(companyHome);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Document doc = xsltFunctions.parseXMLDocument(companyHome, file.getName());
|
||||||
|
NodeList foodNodes = doc.getElementsByTagName("food");
|
||||||
|
assertEquals(10, foodNodes.getLength());
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
log.error("Error!", ex);
|
||||||
|
fail(ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testPathParseXMLDocument()
|
||||||
|
{
|
||||||
|
String path = "path/to/xml/files";
|
||||||
|
List<String> pathElements = Arrays.asList(path.split("/"));
|
||||||
|
FileInfo folder = FileFolderServiceImpl.makeFolders(fileFolderService, companyHome, pathElements, ContentModel.TYPE_FOLDER);
|
||||||
|
FileInfo file = createXmlFile(folder.getNodeRef());
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Document doc = xsltFunctions.parseXMLDocument(companyHome, path + "/" + file.getName());
|
||||||
|
NodeList foodNodes = doc.getElementsByTagName("food");
|
||||||
|
assertEquals(10, foodNodes.getLength());
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
log.error("Error!", ex);
|
||||||
|
fail(ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testParseXMLDocuments()
|
||||||
|
{
|
||||||
|
String path = "path/to/xml/files";
|
||||||
|
List<String> pathElements = Arrays.asList(path.split("/"));
|
||||||
|
FileInfo folder = FileFolderServiceImpl.makeFolders(fileFolderService, companyHome, pathElements, ContentModel.TYPE_FOLDER);
|
||||||
|
FileInfo file1 = createXmlFile(folder.getNodeRef());
|
||||||
|
FileInfo file2 = createXmlFile(folder.getNodeRef());
|
||||||
|
FileInfo file3 = createXmlFile(folder.getNodeRef());
|
||||||
|
FileInfo file4 = createXmlFile(folder.getNodeRef());
|
||||||
|
FileInfo file5 = createXmlFile(folder.getNodeRef());
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Map<String,Document> xmlFileMap = xsltFunctions.parseXMLDocuments("cm:content",
|
||||||
|
companyHome, "/" + path);
|
||||||
|
assertEquals(5, xmlFileMap.size());
|
||||||
|
Set<String> names = new TreeSet<String>(Arrays.asList(new String[] {file1.getName(), file2.getName(),
|
||||||
|
file3.getName(), file4.getName(), file5.getName()}));
|
||||||
|
names.removeAll(xmlFileMap.keySet());
|
||||||
|
assertEquals(0, names.size());
|
||||||
|
|
||||||
|
NodeList foodNodes = xmlFileMap.get(file3.getName()).getElementsByTagName("food");
|
||||||
|
assertEquals(10, foodNodes.getLength());
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
log.error("Error!", ex);
|
||||||
|
fail(ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private FileInfo createXmlFile(NodeRef folder)
|
||||||
|
{
|
||||||
|
String name = GUID.generate();
|
||||||
|
FileInfo testXmlFile = fileFolderService.create(folder, name + ".xml", ContentModel.TYPE_CONTENT);
|
||||||
|
ContentWriter writer = contentService.getWriter(testXmlFile.getNodeRef(), ContentModel.PROP_CONTENT, true);
|
||||||
|
writer.setMimetype("text/xml");
|
||||||
|
writer.setEncoding("UTF-8");
|
||||||
|
writer.putContent(sampleXML);
|
||||||
|
return testXmlFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String sampleXML = "<?xml version=\"1.0\"?>" +
|
||||||
|
"<nutrition>" +
|
||||||
|
|
||||||
|
"<daily-values>" +
|
||||||
|
"<total-fat units=\"g\">65</total-fat>" +
|
||||||
|
"<saturated-fat units=\"g\">20</saturated-fat>" +
|
||||||
|
"<cholesterol units=\"mg\">300</cholesterol>" +
|
||||||
|
"<sodium units=\"mg\">2400</sodium>" +
|
||||||
|
"<carb units=\"g\">300</carb>" +
|
||||||
|
"<fiber units=\"g\">25</fiber>" +
|
||||||
|
"<protein units=\"g\">50</protein>" +
|
||||||
|
"</daily-values>" +
|
||||||
|
|
||||||
|
"<food>" +
|
||||||
|
"<name>Avocado Dip</name>" +
|
||||||
|
"<mfr>Sunnydale</mfr>" +
|
||||||
|
"<serving units=\"g\">29</serving>" +
|
||||||
|
"<calories total=\"110\" fat=\"100\"/>" +
|
||||||
|
"<total-fat>11</total-fat>" +
|
||||||
|
"<saturated-fat>3</saturated-fat>" +
|
||||||
|
"<cholesterol>5</cholesterol>" +
|
||||||
|
"<sodium>210</sodium>" +
|
||||||
|
"<carb>2</carb>" +
|
||||||
|
"<fiber>0</fiber>" +
|
||||||
|
"<protein>1</protein>" +
|
||||||
|
"<vitamins>" +
|
||||||
|
"<a>0</a>" +
|
||||||
|
"<c>0</c>" +
|
||||||
|
"</vitamins>" +
|
||||||
|
"<minerals>" +
|
||||||
|
"<ca>0</ca>" +
|
||||||
|
"<fe>0</fe>" +
|
||||||
|
"</minerals>" +
|
||||||
|
"</food>" +
|
||||||
|
|
||||||
|
"<food>" +
|
||||||
|
"<name>Bagels, New York Style </name>" +
|
||||||
|
"<mfr>Thompson</mfr>" +
|
||||||
|
"<serving units=\"g\">104</serving>" +
|
||||||
|
"<calories total=\"300\" fat=\"35\"/>" +
|
||||||
|
"<total-fat>4</total-fat>" +
|
||||||
|
"<saturated-fat>1</saturated-fat>" +
|
||||||
|
"<cholesterol>0</cholesterol>" +
|
||||||
|
"<sodium>510</sodium>" +
|
||||||
|
"<carb>54</carb>" +
|
||||||
|
"<fiber>3</fiber>" +
|
||||||
|
"<protein>11</protein>" +
|
||||||
|
"<vitamins>" +
|
||||||
|
"<a>0</a>" +
|
||||||
|
"<c>0</c>" +
|
||||||
|
"</vitamins>" +
|
||||||
|
"<minerals>" +
|
||||||
|
"<ca>8</ca>" +
|
||||||
|
"<fe>20</fe>" +
|
||||||
|
"</minerals>" +
|
||||||
|
"</food>" +
|
||||||
|
|
||||||
|
"<food>" +
|
||||||
|
"<name>Beef Frankfurter, Quarter Pound </name>" +
|
||||||
|
"<mfr>Armitage</mfr>" +
|
||||||
|
"<serving units=\"g\">115</serving>" +
|
||||||
|
"<calories total=\"370\" fat=\"290\"/>" +
|
||||||
|
"<total-fat>32</total-fat>" +
|
||||||
|
"<saturated-fat>15</saturated-fat>" +
|
||||||
|
"<cholesterol>65</cholesterol>" +
|
||||||
|
"<sodium>1100</sodium>" +
|
||||||
|
"<carb>8</carb>" +
|
||||||
|
"<fiber>0</fiber>" +
|
||||||
|
"<protein>13</protein>" +
|
||||||
|
"<vitamins>" +
|
||||||
|
"<a>0</a>" +
|
||||||
|
"<c>2</c>" +
|
||||||
|
"</vitamins>" +
|
||||||
|
"<minerals>" +
|
||||||
|
"<ca>1</ca>" +
|
||||||
|
"<fe>6</fe>" +
|
||||||
|
"</minerals>" +
|
||||||
|
"</food>" +
|
||||||
|
|
||||||
|
"<food>" +
|
||||||
|
"<name>Chicken Pot Pie</name>" +
|
||||||
|
"<mfr>Lakeson</mfr>" +
|
||||||
|
"<serving units=\"g\">198</serving>" +
|
||||||
|
"<calories total=\"410\" fat=\"200\"/>" +
|
||||||
|
"<total-fat>22</total-fat>" +
|
||||||
|
"<saturated-fat>9</saturated-fat>" +
|
||||||
|
"<cholesterol>25</cholesterol>" +
|
||||||
|
"<sodium>810</sodium>" +
|
||||||
|
"<carb>42</carb>" +
|
||||||
|
"<fiber>2</fiber>" +
|
||||||
|
"<protein>10</protein>" +
|
||||||
|
"<vitamins>" +
|
||||||
|
"<a>20</a>" +
|
||||||
|
"<c>2</c>" +
|
||||||
|
"</vitamins>" +
|
||||||
|
"<minerals>" +
|
||||||
|
"<ca>2</ca>" +
|
||||||
|
"<fe>10</fe>" +
|
||||||
|
"</minerals>" +
|
||||||
|
"</food>" +
|
||||||
|
|
||||||
|
"<food>" +
|
||||||
|
"<name>Cole Slaw</name>" +
|
||||||
|
"<mfr>Fresh Quick</mfr>" +
|
||||||
|
"<serving units=\" cup\">1.5</serving>" +
|
||||||
|
"<calories total=\"20\" fat=\"0\"/>" +
|
||||||
|
"<total-fat>0</total-fat>" +
|
||||||
|
"<saturated-fat>0</saturated-fat>" +
|
||||||
|
"<cholesterol>0</cholesterol>" +
|
||||||
|
"<sodium>15</sodium>" +
|
||||||
|
"<carb>5</carb>" +
|
||||||
|
"<fiber>2</fiber>" +
|
||||||
|
"<protein>1</protein>" +
|
||||||
|
"<vitamins>" +
|
||||||
|
"<a>30</a>" +
|
||||||
|
"<c>45</c>" +
|
||||||
|
"</vitamins>" +
|
||||||
|
"<minerals>" +
|
||||||
|
"<ca>4</ca>" +
|
||||||
|
"<fe>2</fe>" +
|
||||||
|
"</minerals>" +
|
||||||
|
"</food>" +
|
||||||
|
|
||||||
|
"<food>" +
|
||||||
|
"<name>Eggs</name>" +
|
||||||
|
"<mfr>Goodpath</mfr>" +
|
||||||
|
"<serving units=\"g\">50</serving>" +
|
||||||
|
"<calories total=\"70\" fat=\"40\"/>" +
|
||||||
|
"<total-fat>4.5</total-fat>" +
|
||||||
|
"<saturated-fat>1.5</saturated-fat>" +
|
||||||
|
"<cholesterol>215</cholesterol>" +
|
||||||
|
"<sodium>65</sodium>" +
|
||||||
|
"<carb>1</carb>" +
|
||||||
|
"<fiber>0</fiber>" +
|
||||||
|
"<protein>6</protein>" +
|
||||||
|
"<vitamins>" +
|
||||||
|
"<a>6</a>" +
|
||||||
|
"<c>0</c>" +
|
||||||
|
"</vitamins>" +
|
||||||
|
"<minerals>" +
|
||||||
|
"<ca>2</ca>" +
|
||||||
|
"<fe>4</fe>" +
|
||||||
|
"</minerals>" +
|
||||||
|
"</food>" +
|
||||||
|
|
||||||
|
"<food>" +
|
||||||
|
"<name>Hazelnut Spread</name>" +
|
||||||
|
"<mfr>Ferreira</mfr>" +
|
||||||
|
"<serving units=\"tbsp\">2</serving>" +
|
||||||
|
"<calories total=\"200\" fat=\"90\"/>" +
|
||||||
|
"<total-fat>10</total-fat>" +
|
||||||
|
"<saturated-fat>2</saturated-fat>" +
|
||||||
|
"<cholesterol>0</cholesterol>" +
|
||||||
|
"<sodium>20</sodium>" +
|
||||||
|
"<carb>23</carb>" +
|
||||||
|
"<fiber>2</fiber>" +
|
||||||
|
"<protein>3</protein>" +
|
||||||
|
"<vitamins>" +
|
||||||
|
"<a>0</a>" +
|
||||||
|
"<c>0</c>" +
|
||||||
|
"</vitamins>" +
|
||||||
|
"<minerals>" +
|
||||||
|
"<ca>6</ca>" +
|
||||||
|
"<fe>4</fe>" +
|
||||||
|
"</minerals>" +
|
||||||
|
"</food>" +
|
||||||
|
|
||||||
|
"<food>" +
|
||||||
|
"<name>Potato Chips</name>" +
|
||||||
|
"<mfr>Lees</mfr>" +
|
||||||
|
"<serving units=\"g\">28</serving>" +
|
||||||
|
"<calories total=\"150\" fat=\"90\"/>" +
|
||||||
|
"<total-fat>10</total-fat>" +
|
||||||
|
"<saturated-fat>3</saturated-fat>" +
|
||||||
|
"<cholesterol>0</cholesterol>" +
|
||||||
|
"<sodium>180</sodium>" +
|
||||||
|
"<carb>15</carb>" +
|
||||||
|
"<fiber>1</fiber>" +
|
||||||
|
"<protein>2</protein>" +
|
||||||
|
"<vitamins>" +
|
||||||
|
"<a>0</a>" +
|
||||||
|
"<c>10</c>" +
|
||||||
|
"</vitamins>" +
|
||||||
|
"<minerals>" +
|
||||||
|
"<ca>0</ca>" +
|
||||||
|
"<fe>0</fe>" +
|
||||||
|
"</minerals>" +
|
||||||
|
"</food>" +
|
||||||
|
|
||||||
|
"<food>" +
|
||||||
|
"<name>Soy Patties, Grilled</name>" +
|
||||||
|
"<mfr>Gardenproducts</mfr>" +
|
||||||
|
"<serving units=\"g\">96</serving>" +
|
||||||
|
"<calories total=\"160\" fat=\"45\"/>" +
|
||||||
|
"<total-fat>5</total-fat>" +
|
||||||
|
"<saturated-fat>0</saturated-fat>" +
|
||||||
|
"<cholesterol>0</cholesterol>" +
|
||||||
|
"<sodium>420</sodium>" +
|
||||||
|
"<carb>10</carb>" +
|
||||||
|
"<fiber>4</fiber>" +
|
||||||
|
"<protein>9</protein>" +
|
||||||
|
"<vitamins>" +
|
||||||
|
"<a>0</a>" +
|
||||||
|
"<c>0</c>" +
|
||||||
|
"</vitamins>" +
|
||||||
|
"<minerals>" +
|
||||||
|
"<ca>0</ca>" +
|
||||||
|
"<fe>0</fe>" +
|
||||||
|
"</minerals>" +
|
||||||
|
"</food>" +
|
||||||
|
|
||||||
|
"<food>" +
|
||||||
|
"<name>Truffles, Dark Chocolate</name>" +
|
||||||
|
"<mfr>Lyndon's</mfr>" +
|
||||||
|
"<serving units=\"g\">39</serving>" +
|
||||||
|
"<calories total=\"220\" fat=\"170\"/>" +
|
||||||
|
"<total-fat>19</total-fat>" +
|
||||||
|
"<saturated-fat>14</saturated-fat>" +
|
||||||
|
"<cholesterol>25</cholesterol>" +
|
||||||
|
"<sodium>10</sodium>" +
|
||||||
|
"<carb>16</carb>" +
|
||||||
|
"<fiber>1</fiber>" +
|
||||||
|
"<protein>1</protein>" +
|
||||||
|
"<vitamins>" +
|
||||||
|
"<a>0</a>" +
|
||||||
|
"<c>0</c>" +
|
||||||
|
"</vitamins>" +
|
||||||
|
"<minerals>" +
|
||||||
|
"<ca>0</ca>" +
|
||||||
|
"<fe>0</fe>" +
|
||||||
|
"</minerals>" +
|
||||||
|
"</food>" +
|
||||||
|
|
||||||
|
"</nutrition>";
|
||||||
|
}
|
@@ -0,0 +1,414 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2010 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU 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 General Public License for more details.
|
||||||
|
|
||||||
|
* You should have received a copy of the GNU 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.
|
||||||
|
|
||||||
|
* As a special exception to the terms and conditions of version 2.0 of
|
||||||
|
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||||
|
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||||
|
* FLOSS exception. You should have recieved a copy of the text describing
|
||||||
|
* the FLOSS exception, and it is also available here:
|
||||||
|
* http://www.alfresco.com/legal/licensing"
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.alfresco.repo.rendition.executer;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.alfresco.model.ContentModel;
|
||||||
|
import org.alfresco.repo.action.ParameterDefinitionImpl;
|
||||||
|
import org.alfresco.repo.template.TemplateProcessorMethod;
|
||||||
|
import org.alfresco.repo.template.XSLTProcessor;
|
||||||
|
import org.alfresco.repo.template.XSLTemplateModel;
|
||||||
|
import org.alfresco.service.cmr.action.ParameterDefinition;
|
||||||
|
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
||||||
|
import org.alfresco.service.cmr.model.FileFolderService;
|
||||||
|
import org.alfresco.service.cmr.model.FileInfo;
|
||||||
|
import org.alfresco.service.cmr.model.FileNotFoundException;
|
||||||
|
import org.alfresco.service.cmr.rendition.RenditionServiceException;
|
||||||
|
import org.alfresco.service.cmr.repository.ContentWriter;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
|
import org.alfresco.service.cmr.repository.StoreRef;
|
||||||
|
import org.alfresco.service.cmr.repository.TemplateService;
|
||||||
|
import org.alfresco.service.cmr.search.ResultSet;
|
||||||
|
import org.alfresco.service.cmr.search.SearchService;
|
||||||
|
import org.alfresco.service.namespace.NamespacePrefixResolver;
|
||||||
|
import org.alfresco.service.namespace.NamespaceService;
|
||||||
|
import org.alfresco.service.namespace.QName;
|
||||||
|
import org.alfresco.util.XMLUtil;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
import org.w3c.dom.Node;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Brian Remmington
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
public class XSLTRenderingEngine extends AbstractRenderingEngine
|
||||||
|
{
|
||||||
|
private static final Log log = LogFactory.getLog(XSLTRenderingEngine.class);
|
||||||
|
|
||||||
|
public static final String NAME = "xsltRenderingEngine";
|
||||||
|
public static final String PARAM_MODEL = "model";
|
||||||
|
public static final String PARAM_TEMPLATE = "template_string";
|
||||||
|
public static final String PARAM_TEMPLATE_NODE = "template_node";
|
||||||
|
public static final String PARAM_TEMPLATE_PATH = "template_path";
|
||||||
|
|
||||||
|
private TemplateService templateService;
|
||||||
|
private XSLTFunctions xsltFunctions;
|
||||||
|
private NamespacePrefixResolver namespacePrefixResolver;
|
||||||
|
private FileFolderService fileFolderService;
|
||||||
|
private SearchService searchService;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @see org.alfresco.repo.rendition.executer.AbstractRenderingEngine#render(org
|
||||||
|
* .alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.rendition.RenditionDefinition,
|
||||||
|
* org.alfresco.service.cmr.repository.ContentReader, org.alfresco.service.cmr.repository.ChildAssociationRef)
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
protected void render(RenderingContext context)
|
||||||
|
{
|
||||||
|
NodeRef templateNode = getTemplateNode(context);
|
||||||
|
Map<String, Serializable> paramMap = context.getCheckedParam(PARAM_MODEL, Map.class);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
XSLTemplateModel model = buildModel(context, paramMap);
|
||||||
|
ContentWriter contentWriter = context.makeContentWriter();
|
||||||
|
Writer writer = new OutputStreamWriter(contentWriter.getContentOutputStream());
|
||||||
|
processTemplate(context, templateNode, model, writer);
|
||||||
|
writer.flush();
|
||||||
|
writer.close();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
log.warn("Unexpected error while rendering through XSLT rendering engine.", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processTemplate(RenderingContext context, NodeRef templateNode, XSLTemplateModel model, Writer out)
|
||||||
|
{
|
||||||
|
String template = context.getCheckedParam(PARAM_TEMPLATE, String.class);
|
||||||
|
if (template != null)
|
||||||
|
{
|
||||||
|
templateService.processTemplateString("xslt", (String) template, model, out);
|
||||||
|
}
|
||||||
|
else if (templateNode != null)
|
||||||
|
{
|
||||||
|
templateService.processTemplate("xslt", templateNode.toString(), model, out);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throwTemplateParamsNotFoundException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void throwTemplateParamsNotFoundException()
|
||||||
|
{
|
||||||
|
StringBuilder msg = new StringBuilder("This action requires that either the ");
|
||||||
|
msg.append(PARAM_TEMPLATE);
|
||||||
|
msg.append(" parameter or the ");
|
||||||
|
msg.append(PARAM_TEMPLATE_NODE);
|
||||||
|
msg.append(" parameter be specified. ");
|
||||||
|
throw new RenditionServiceException(msg.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private NodeRef getTemplateNode(RenderingContext context)
|
||||||
|
{
|
||||||
|
NodeRef node = context.getCheckedParam(PARAM_TEMPLATE_NODE, NodeRef.class);
|
||||||
|
if (node == null)
|
||||||
|
{
|
||||||
|
String path = context.getCheckedParam(PARAM_TEMPLATE_PATH, String.class);
|
||||||
|
if (path != null && path.length() > 0)
|
||||||
|
{
|
||||||
|
StoreRef storeRef = context.getDestinationNode().getStoreRef();
|
||||||
|
ResultSet result = searchService.query(storeRef, SearchService.LANGUAGE_XPATH, path);
|
||||||
|
if (result.length() != 1)
|
||||||
|
{
|
||||||
|
throw new RenditionServiceException("Could not find template node for path: " + path);
|
||||||
|
}
|
||||||
|
node = result.getNodeRef(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
protected XSLTemplateModel buildModel(RenderingContext context, Map<String, Serializable> suppliedParams)
|
||||||
|
throws IOException, SAXException
|
||||||
|
{
|
||||||
|
final NodeRef sourceNode = context.getSourceNode();
|
||||||
|
final NodeRef parentNode = nodeService.getPrimaryParent(context.getSourceNode()).getParentRef();
|
||||||
|
final String sourcePath = getPath(sourceNode);
|
||||||
|
final String parentPath = getPath(parentNode);
|
||||||
|
|
||||||
|
XSLTemplateModel model = new XSLTemplateModel();
|
||||||
|
|
||||||
|
// add simple scalar parameters
|
||||||
|
model.put(QName.createQName(NamespaceService.ALFRESCO_URI, "date"), new Date());
|
||||||
|
|
||||||
|
model.put(QName.createQName(NamespaceService.ALFRESCO_PREFIX, "source_file_name", namespacePrefixResolver),
|
||||||
|
nodeService.getProperty(context.getSourceNode(), ContentModel.PROP_NAME));
|
||||||
|
model.put(QName.createQName(NamespaceService.ALFRESCO_PREFIX, "source_path", namespacePrefixResolver),
|
||||||
|
sourcePath);
|
||||||
|
model.put(QName.createQName(NamespaceService.ALFRESCO_PREFIX, "parent_path", namespacePrefixResolver),
|
||||||
|
parentPath);
|
||||||
|
|
||||||
|
// add methods
|
||||||
|
model.put(QName.createQName(NamespaceService.ALFRESCO_PREFIX, "encodeQuotes", namespacePrefixResolver),
|
||||||
|
new TemplateProcessorMethod()
|
||||||
|
{
|
||||||
|
public Object exec(final Object[] arguments) throws IOException, SAXException
|
||||||
|
{
|
||||||
|
if (arguments.length != 1)
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("expected 1 argument to encodeQuotes. got "
|
||||||
|
+ arguments.length);
|
||||||
|
|
||||||
|
}
|
||||||
|
if (!(arguments[0] instanceof String))
|
||||||
|
{
|
||||||
|
throw new ClassCastException("expected arguments[0] to be a " + String.class.getName()
|
||||||
|
+ ". got a " + arguments[0].getClass().getName() + ".");
|
||||||
|
}
|
||||||
|
String text = (String) arguments[0];
|
||||||
|
|
||||||
|
if (log.isDebugEnabled())
|
||||||
|
{
|
||||||
|
log.debug("tpm_encodeQuotes('" + text + "'), parentPath = " + parentPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
final String result = xsltFunctions.encodeQuotes(text);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
model.put(QName.createQName(NamespaceService.ALFRESCO_PREFIX, "parseXMLDocument", namespacePrefixResolver),
|
||||||
|
new TemplateProcessorMethod()
|
||||||
|
{
|
||||||
|
public Object exec(final Object[] arguments) throws IOException, SAXException
|
||||||
|
{
|
||||||
|
if (arguments.length != 1)
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("expected 1 argument to parseXMLDocument. got "
|
||||||
|
+ arguments.length);
|
||||||
|
|
||||||
|
}
|
||||||
|
if (!(arguments[0] instanceof String))
|
||||||
|
{
|
||||||
|
throw new ClassCastException("expected arguments[0] to be a " + String.class.getName()
|
||||||
|
+ ". got a " + arguments[0].getClass().getName() + ".");
|
||||||
|
}
|
||||||
|
String path = (String) arguments[0];
|
||||||
|
|
||||||
|
if (log.isDebugEnabled())
|
||||||
|
{
|
||||||
|
log.debug("parseXMLDocument('" + path + "'), parentPath = " + parentPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
Document d = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
d = xsltFunctions.parseXMLDocument(parentNode, path);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
return d == null ? null : d.getDocumentElement();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
model.put(QName.createQName(NamespaceService.ALFRESCO_PREFIX, "parseXMLDocuments", namespacePrefixResolver),
|
||||||
|
new TemplateProcessorMethod()
|
||||||
|
{
|
||||||
|
public Object exec(final Object[] arguments) throws IOException, SAXException
|
||||||
|
{
|
||||||
|
if (arguments.length > 2)
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("expected one or two arguments to "
|
||||||
|
+ "parseXMLDocuments. got " + arguments.length);
|
||||||
|
}
|
||||||
|
if (!(arguments[0] instanceof String))
|
||||||
|
{
|
||||||
|
throw new ClassCastException("expected arguments[0] to be a " + String.class.getName()
|
||||||
|
+ ". got a " + arguments[0].getClass().getName() + ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments.length == 2 && !(arguments[1] instanceof String))
|
||||||
|
{
|
||||||
|
throw new ClassCastException("expected arguments[1] to be a " + String.class.getName()
|
||||||
|
+ ". got a " + arguments[1].getClass().getName() + ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
String path = arguments.length == 2 ? (String) arguments[1] : "";
|
||||||
|
final String typeName = (String) arguments[0];
|
||||||
|
|
||||||
|
if (log.isDebugEnabled())
|
||||||
|
{
|
||||||
|
log.debug("tpm_parseXMLDocuments('" + typeName + "','" + path + "'), parentPath = "
|
||||||
|
+ parentPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<String, Document> resultMap = xsltFunctions.parseXMLDocuments(typeName, parentNode,
|
||||||
|
path);
|
||||||
|
|
||||||
|
if (log.isDebugEnabled())
|
||||||
|
{
|
||||||
|
log.debug("received " + resultMap.size() + " documents in " + path + " with form name "
|
||||||
|
+ typeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a root document for rooting all the results. we do this
|
||||||
|
// so that each document root element has a common parent node
|
||||||
|
// and so that xpath axes work properly
|
||||||
|
final Document rootNodeDocument = XMLUtil.newDocument();
|
||||||
|
final Element rootNodeDocumentEl = rootNodeDocument.createElementNS(
|
||||||
|
NamespaceService.ALFRESCO_URI, NamespaceService.ALFRESCO_PREFIX + ":file_list");
|
||||||
|
rootNodeDocumentEl.setAttribute("xmlns:" + NamespaceService.ALFRESCO_PREFIX,
|
||||||
|
NamespaceService.ALFRESCO_URI);
|
||||||
|
rootNodeDocument.appendChild(rootNodeDocumentEl);
|
||||||
|
|
||||||
|
final List<Node> result = new ArrayList<Node>(resultMap.size());
|
||||||
|
for (Map.Entry<String, Document> e : resultMap.entrySet())
|
||||||
|
{
|
||||||
|
final Element documentEl = e.getValue().getDocumentElement();
|
||||||
|
documentEl.setAttribute("xmlns:" + NamespaceService.ALFRESCO_PREFIX,
|
||||||
|
NamespaceService.ALFRESCO_URI);
|
||||||
|
documentEl.setAttributeNS(NamespaceService.ALFRESCO_URI, NamespaceService.ALFRESCO_PREFIX
|
||||||
|
+ ":file_name", e.getKey());
|
||||||
|
final Node n = rootNodeDocument.importNode(documentEl, true);
|
||||||
|
rootNodeDocumentEl.appendChild(n);
|
||||||
|
result.add(n);
|
||||||
|
}
|
||||||
|
return result.toArray(new Node[result.size()]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (suppliedParams != null)
|
||||||
|
{
|
||||||
|
for (Map.Entry<String, Serializable> suppliedParam : suppliedParams.entrySet())
|
||||||
|
{
|
||||||
|
model.put(QName.createQName(suppliedParam.getKey()), suppliedParam.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the xml document
|
||||||
|
model.put(XSLTProcessor.ROOT_NAMESPACE, XMLUtil.parse(sourceNode, contentService));
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param nodeRef
|
||||||
|
* @return
|
||||||
|
* @throws FileNotFoundException
|
||||||
|
*/
|
||||||
|
private String getPath(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
List<FileInfo> parentFileInfoList = fileFolderService.getNamePath(null, nodeRef);
|
||||||
|
for (FileInfo fileInfo : parentFileInfoList)
|
||||||
|
{
|
||||||
|
sb.append('/');
|
||||||
|
sb.append(fileInfo.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (FileNotFoundException ex)
|
||||||
|
{
|
||||||
|
log.info("Unexpected problem: error while calculating path to node " + nodeRef, ex);
|
||||||
|
}
|
||||||
|
String path = sb.toString();
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @seeorg.alfresco.repo.rendition.executer.AbstractRenderingEngine# getParameterDefinitions()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected Collection<ParameterDefinition> getParameterDefinitions()
|
||||||
|
{
|
||||||
|
Collection<ParameterDefinition> paramList = super.getParameterDefinitions();
|
||||||
|
ParameterDefinitionImpl modelParamDef = new ParameterDefinitionImpl(PARAM_MODEL, DataTypeDefinition.ANY, false,
|
||||||
|
getParamDisplayLabel(PARAM_MODEL));
|
||||||
|
ParameterDefinitionImpl templateParamDef = new ParameterDefinitionImpl(//
|
||||||
|
PARAM_TEMPLATE, DataTypeDefinition.TEXT, false, getParamDisplayLabel(PARAM_TEMPLATE));
|
||||||
|
ParameterDefinitionImpl templateNodeParamDef = new ParameterDefinitionImpl(PARAM_TEMPLATE_NODE,
|
||||||
|
DataTypeDefinition.NODE_REF, false, getParamDisplayLabel(PARAM_TEMPLATE_NODE));
|
||||||
|
ParameterDefinitionImpl templatePathParamDef = new ParameterDefinitionImpl(PARAM_TEMPLATE_PATH,
|
||||||
|
DataTypeDefinition.TEXT, false, getParamDisplayLabel(PARAM_TEMPLATE_PATH));
|
||||||
|
paramList.add(modelParamDef);
|
||||||
|
paramList.add(templateParamDef);
|
||||||
|
paramList.add(templateNodeParamDef);
|
||||||
|
paramList.add(templatePathParamDef);
|
||||||
|
return paramList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param templateService
|
||||||
|
* the templateService to set
|
||||||
|
*/
|
||||||
|
public void setTemplateService(TemplateService templateService)
|
||||||
|
{
|
||||||
|
this.templateService = templateService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param xsltFunctions
|
||||||
|
* the xsltFunctions to set
|
||||||
|
*/
|
||||||
|
public void setXsltFunctions(XSLTFunctions xsltFunctions)
|
||||||
|
{
|
||||||
|
this.xsltFunctions = xsltFunctions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param namespacePrefixResolver
|
||||||
|
* the namespacePrefixResolver to set
|
||||||
|
*/
|
||||||
|
public void setNamespacePrefixResolver(NamespacePrefixResolver namespacePrefixResolver)
|
||||||
|
{
|
||||||
|
this.namespacePrefixResolver = namespacePrefixResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param fileFolderService
|
||||||
|
* the fileFolderService to set
|
||||||
|
*/
|
||||||
|
public void setFileFolderService(FileFolderService fileFolderService)
|
||||||
|
{
|
||||||
|
this.fileFolderService = fileFolderService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param searchService
|
||||||
|
* the searchService to set
|
||||||
|
*/
|
||||||
|
public void setSearchService(SearchService searchService)
|
||||||
|
{
|
||||||
|
this.searchService = searchService;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,317 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2010 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU 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 General Public License for more details.
|
||||||
|
|
||||||
|
* You should have received a copy of the GNU 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.
|
||||||
|
|
||||||
|
* As a special exception to the terms and conditions of version 2.0 of
|
||||||
|
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||||
|
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||||
|
* FLOSS exception. You should have received a copy of the text describing
|
||||||
|
* the FLOSS exception, and it is also available here:
|
||||||
|
* http://www.alfresco.com/legal/licensing"
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.alfresco.repo.rendition.executer;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.alfresco.model.ContentModel;
|
||||||
|
import org.alfresco.repo.model.filefolder.FileFolderServiceImpl;
|
||||||
|
import org.alfresco.service.cmr.model.FileFolderService;
|
||||||
|
import org.alfresco.service.cmr.model.FileInfo;
|
||||||
|
import org.alfresco.service.cmr.rendition.RenditionDefinition;
|
||||||
|
import org.alfresco.service.cmr.rendition.RenditionService;
|
||||||
|
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||||
|
import org.alfresco.service.cmr.repository.ContentReader;
|
||||||
|
import org.alfresco.service.cmr.repository.ContentService;
|
||||||
|
import org.alfresco.service.cmr.repository.ContentWriter;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
|
import org.alfresco.service.cmr.repository.StoreRef;
|
||||||
|
import org.alfresco.service.cmr.repository.TemplateProcessor;
|
||||||
|
import org.alfresco.service.cmr.repository.TemplateService;
|
||||||
|
import org.alfresco.service.cmr.search.ResultSet;
|
||||||
|
import org.alfresco.service.cmr.search.SearchService;
|
||||||
|
import org.alfresco.service.namespace.QName;
|
||||||
|
import org.alfresco.util.BaseAlfrescoSpringTest;
|
||||||
|
import org.alfresco.util.GUID;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Brian
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class XSLTRenderingEngineTest extends BaseAlfrescoSpringTest
|
||||||
|
{
|
||||||
|
private final static Log log = LogFactory.getLog(XSLTRenderingEngineTest.class);
|
||||||
|
private XSLTFunctions xsltFunctions;
|
||||||
|
private SearchService searchService;
|
||||||
|
private NodeRef companyHome;
|
||||||
|
private FileFolderService fileFolderService;
|
||||||
|
private TemplateProcessor xsltProcessor;
|
||||||
|
private TemplateService templateService;
|
||||||
|
private RenditionService renditionService;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
*
|
||||||
|
* @see org.alfresco.util.BaseAlfrescoSpringTest#onSetUpInTransaction()
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
@Override
|
||||||
|
protected void onSetUpInTransaction() throws Exception
|
||||||
|
{
|
||||||
|
super.onSetUpInTransaction();
|
||||||
|
this.searchService = (SearchService) this.applicationContext.getBean("SearchService");
|
||||||
|
this.xsltFunctions = (XSLTFunctions) this.applicationContext.getBean("xsltFunctions");
|
||||||
|
this.nodeService = (NodeService) this.applicationContext.getBean("NodeService");
|
||||||
|
this.contentService = (ContentService) this.applicationContext.getBean("ContentService");
|
||||||
|
this.fileFolderService = (FileFolderService) this.applicationContext.getBean("FileFolderService");
|
||||||
|
this.xsltProcessor = (TemplateProcessor) this.applicationContext.getBean("xsltProcessor");
|
||||||
|
this.templateService = (TemplateService) this.applicationContext.getBean("TemplateService");
|
||||||
|
this.renditionService = (RenditionService) this.applicationContext.getBean("RenditionService");
|
||||||
|
ResultSet rs = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, SearchService.LANGUAGE_XPATH,
|
||||||
|
"/app:company_home");
|
||||||
|
this.companyHome = rs.getNodeRef(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSimplestStringTemplate() throws Exception
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
FileInfo file = createXmlFile(companyHome);
|
||||||
|
FileInfo xslFile = createXmlFile(companyHome, verySimpleXSLT);
|
||||||
|
|
||||||
|
RenditionDefinition def = renditionService.createRenditionDefinition(QName.createQName("Test"), XSLTRenderingEngine.NAME);
|
||||||
|
def.setParameterValue(XSLTRenderingEngine.PARAM_TEMPLATE_NODE, xslFile.getNodeRef());
|
||||||
|
|
||||||
|
ChildAssociationRef rendition = renditionService.render(file.getNodeRef(), def);
|
||||||
|
|
||||||
|
assertNotNull(rendition);
|
||||||
|
|
||||||
|
ContentReader reader = contentService.getReader(rendition.getChildRef(), ContentModel.PROP_CONTENT);
|
||||||
|
assertNotNull(reader);
|
||||||
|
String output = reader.getContentString();
|
||||||
|
|
||||||
|
log.debug("XSLT Processor output: " + output);
|
||||||
|
assertEquals("Avocado DipBagels, New York StyleBeef Frankfurter, Quarter PoundChicken Pot PieCole SlawEggsHazelnut SpreadPotato ChipsSoy Patties, GrilledTruffles, Dark Chocolate", output);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
log.error("Error!", ex);
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void testParseXMLDocument() throws Exception
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
FileInfo file = createXmlFile(companyHome);
|
||||||
|
createXmlFile(companyHome, "TestXML.xml", sampleXML);
|
||||||
|
FileInfo xslFile = createXmlFile(companyHome, callParseXmlDocument);
|
||||||
|
|
||||||
|
RenditionDefinition def = renditionService.createRenditionDefinition(QName.createQName("Test"), XSLTRenderingEngine.NAME);
|
||||||
|
def.setParameterValue(XSLTRenderingEngine.PARAM_TEMPLATE_NODE, xslFile.getNodeRef());
|
||||||
|
|
||||||
|
ChildAssociationRef rendition = renditionService.render(file.getNodeRef(), def);
|
||||||
|
|
||||||
|
assertNotNull(rendition);
|
||||||
|
|
||||||
|
ContentReader reader = contentService.getReader(rendition.getChildRef(), ContentModel.PROP_CONTENT);
|
||||||
|
assertNotNull(reader);
|
||||||
|
String output = reader.getContentString();
|
||||||
|
|
||||||
|
log.debug("XSLT Processor output: " + output);
|
||||||
|
assertEquals("Avocado DipBagels, New York StyleBeef Frankfurter, Quarter PoundChicken Pot PieCole SlawEggsHazelnut SpreadPotato ChipsSoy Patties, GrilledTruffles, Dark Chocolate", output);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
log.error("Error!", ex);
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testParseXMLDocuments() throws Exception
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
FileInfo file = createXmlFile(companyHome);
|
||||||
|
String path = "path/to/xml/files";
|
||||||
|
List<String> pathElements = Arrays.asList(path.split("/"));
|
||||||
|
FileInfo folder = FileFolderServiceImpl.makeFolders(fileFolderService, companyHome, pathElements, ContentModel.TYPE_FOLDER);
|
||||||
|
createXmlFile(folder.getNodeRef());
|
||||||
|
createXmlFile(folder.getNodeRef());
|
||||||
|
createXmlFile(folder.getNodeRef());
|
||||||
|
createXmlFile(folder.getNodeRef());
|
||||||
|
createXmlFile(folder.getNodeRef());
|
||||||
|
FileInfo xslFile = createXmlFile(companyHome, callParseXmlDocuments);
|
||||||
|
|
||||||
|
RenditionDefinition def = renditionService.createRenditionDefinition(QName.createQName("Test"), XSLTRenderingEngine.NAME);
|
||||||
|
def.setParameterValue(XSLTRenderingEngine.PARAM_TEMPLATE_NODE, xslFile.getNodeRef());
|
||||||
|
|
||||||
|
ChildAssociationRef rendition = renditionService.render(file.getNodeRef(), def);
|
||||||
|
|
||||||
|
assertNotNull(rendition);
|
||||||
|
|
||||||
|
ContentReader reader = contentService.getReader(rendition.getChildRef(), ContentModel.PROP_CONTENT);
|
||||||
|
assertNotNull(reader);
|
||||||
|
String output = reader.getContentString();
|
||||||
|
|
||||||
|
log.debug("XSLT Processor output: " + output);
|
||||||
|
assertEquals(
|
||||||
|
"Avocado DipBagels, New York StyleBeef Frankfurter, Quarter PoundChicken Pot PieCole SlawEggsHazelnut SpreadPotato ChipsSoy Patties, GrilledTruffles, Dark Chocolate"+
|
||||||
|
"Avocado DipBagels, New York StyleBeef Frankfurter, Quarter PoundChicken Pot PieCole SlawEggsHazelnut SpreadPotato ChipsSoy Patties, GrilledTruffles, Dark Chocolate"+
|
||||||
|
"Avocado DipBagels, New York StyleBeef Frankfurter, Quarter PoundChicken Pot PieCole SlawEggsHazelnut SpreadPotato ChipsSoy Patties, GrilledTruffles, Dark Chocolate"+
|
||||||
|
"Avocado DipBagels, New York StyleBeef Frankfurter, Quarter PoundChicken Pot PieCole SlawEggsHazelnut SpreadPotato ChipsSoy Patties, GrilledTruffles, Dark Chocolate"+
|
||||||
|
"Avocado DipBagels, New York StyleBeef Frankfurter, Quarter PoundChicken Pot PieCole SlawEggsHazelnut SpreadPotato ChipsSoy Patties, GrilledTruffles, Dark Chocolate"
|
||||||
|
, output);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
log.error("Error!", ex);
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private FileInfo createXmlFile(NodeRef folder)
|
||||||
|
{
|
||||||
|
return createXmlFile(folder, sampleXML);
|
||||||
|
}
|
||||||
|
|
||||||
|
private FileInfo createXmlFile(NodeRef folder, String content)
|
||||||
|
{
|
||||||
|
String name = GUID.generate() + ".xml";
|
||||||
|
return createXmlFile(folder, name, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
private FileInfo createXmlFile(NodeRef folder, String filename, String content)
|
||||||
|
{
|
||||||
|
FileInfo testXmlFile = fileFolderService.create(folder, filename, ContentModel.TYPE_CONTENT);
|
||||||
|
ContentWriter writer = contentService.getWriter(testXmlFile.getNodeRef(), ContentModel.PROP_CONTENT, true);
|
||||||
|
writer.setMimetype("text/xml");
|
||||||
|
writer.setEncoding("UTF-8");
|
||||||
|
writer.putContent(content);
|
||||||
|
return testXmlFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String sampleXML = "<?xml version=\"1.0\"?>" + "<nutrition>" +
|
||||||
|
|
||||||
|
"<daily-values>" + "<total-fat units=\"g\">65</total-fat>" + "<saturated-fat units=\"g\">20</saturated-fat>"
|
||||||
|
+ "<cholesterol units=\"mg\">300</cholesterol>" + "<sodium units=\"mg\">2400</sodium>"
|
||||||
|
+ "<carb units=\"g\">300</carb>" + "<fiber units=\"g\">25</fiber>" + "<protein units=\"g\">50</protein>"
|
||||||
|
+ "</daily-values>" +
|
||||||
|
|
||||||
|
"<food>" + "<name>Avocado Dip</name>" + "<mfr>Sunnydale</mfr>" + "<serving units=\"g\">29</serving>"
|
||||||
|
+ "<calories total=\"110\" fat=\"100\"/>" + "<total-fat>11</total-fat>"
|
||||||
|
+ "<saturated-fat>3</saturated-fat>" + "<cholesterol>5</cholesterol>" + "<sodium>210</sodium>"
|
||||||
|
+ "<carb>2</carb>" + "<fiber>0</fiber>" + "<protein>1</protein>" + "<vitamins>" + "<a>0</a>" + "<c>0</c>"
|
||||||
|
+ "</vitamins>" + "<minerals>" + "<ca>0</ca>" + "<fe>0</fe>" + "</minerals>" + "</food>" +
|
||||||
|
|
||||||
|
"<food>" + "<name>Bagels, New York Style</name>" + "<mfr>Thompson</mfr>"
|
||||||
|
+ "<serving units=\"g\">104</serving>" + "<calories total=\"300\" fat=\"35\"/>"
|
||||||
|
+ "<total-fat>4</total-fat>" + "<saturated-fat>1</saturated-fat>" + "<cholesterol>0</cholesterol>"
|
||||||
|
+ "<sodium>510</sodium>" + "<carb>54</carb>" + "<fiber>3</fiber>" + "<protein>11</protein>" + "<vitamins>"
|
||||||
|
+ "<a>0</a>" + "<c>0</c>" + "</vitamins>" + "<minerals>" + "<ca>8</ca>" + "<fe>20</fe>" + "</minerals>"
|
||||||
|
+ "</food>" +
|
||||||
|
|
||||||
|
"<food>" + "<name>Beef Frankfurter, Quarter Pound</name>" + "<mfr>Armitage</mfr>"
|
||||||
|
+ "<serving units=\"g\">115</serving>" + "<calories total=\"370\" fat=\"290\"/>"
|
||||||
|
+ "<total-fat>32</total-fat>" + "<saturated-fat>15</saturated-fat>" + "<cholesterol>65</cholesterol>"
|
||||||
|
+ "<sodium>1100</sodium>" + "<carb>8</carb>" + "<fiber>0</fiber>" + "<protein>13</protein>" + "<vitamins>"
|
||||||
|
+ "<a>0</a>" + "<c>2</c>" + "</vitamins>" + "<minerals>" + "<ca>1</ca>" + "<fe>6</fe>" + "</minerals>"
|
||||||
|
+ "</food>" +
|
||||||
|
|
||||||
|
"<food>" + "<name>Chicken Pot Pie</name>" + "<mfr>Lakeson</mfr>" + "<serving units=\"g\">198</serving>"
|
||||||
|
+ "<calories total=\"410\" fat=\"200\"/>" + "<total-fat>22</total-fat>"
|
||||||
|
+ "<saturated-fat>9</saturated-fat>" + "<cholesterol>25</cholesterol>" + "<sodium>810</sodium>"
|
||||||
|
+ "<carb>42</carb>" + "<fiber>2</fiber>" + "<protein>10</protein>" + "<vitamins>" + "<a>20</a>"
|
||||||
|
+ "<c>2</c>" + "</vitamins>" + "<minerals>" + "<ca>2</ca>" + "<fe>10</fe>" + "</minerals>" + "</food>" +
|
||||||
|
|
||||||
|
"<food>" + "<name>Cole Slaw</name>" + "<mfr>Fresh Quick</mfr>" + "<serving units=\" cup\">1.5</serving>"
|
||||||
|
+ "<calories total=\"20\" fat=\"0\"/>" + "<total-fat>0</total-fat>" + "<saturated-fat>0</saturated-fat>"
|
||||||
|
+ "<cholesterol>0</cholesterol>" + "<sodium>15</sodium>" + "<carb>5</carb>" + "<fiber>2</fiber>"
|
||||||
|
+ "<protein>1</protein>" + "<vitamins>" + "<a>30</a>" + "<c>45</c>" + "</vitamins>" + "<minerals>"
|
||||||
|
+ "<ca>4</ca>" + "<fe>2</fe>" + "</minerals>" + "</food>" +
|
||||||
|
|
||||||
|
"<food>" + "<name>Eggs</name>" + "<mfr>Goodpath</mfr>" + "<serving units=\"g\">50</serving>"
|
||||||
|
+ "<calories total=\"70\" fat=\"40\"/>" + "<total-fat>4.5</total-fat>"
|
||||||
|
+ "<saturated-fat>1.5</saturated-fat>" + "<cholesterol>215</cholesterol>" + "<sodium>65</sodium>"
|
||||||
|
+ "<carb>1</carb>" + "<fiber>0</fiber>" + "<protein>6</protein>" + "<vitamins>" + "<a>6</a>" + "<c>0</c>"
|
||||||
|
+ "</vitamins>" + "<minerals>" + "<ca>2</ca>" + "<fe>4</fe>" + "</minerals>" + "</food>" +
|
||||||
|
|
||||||
|
"<food>" + "<name>Hazelnut Spread</name>" + "<mfr>Ferreira</mfr>" + "<serving units=\"tbsp\">2</serving>"
|
||||||
|
+ "<calories total=\"200\" fat=\"90\"/>" + "<total-fat>10</total-fat>" + "<saturated-fat>2</saturated-fat>"
|
||||||
|
+ "<cholesterol>0</cholesterol>" + "<sodium>20</sodium>" + "<carb>23</carb>" + "<fiber>2</fiber>"
|
||||||
|
+ "<protein>3</protein>" + "<vitamins>" + "<a>0</a>" + "<c>0</c>" + "</vitamins>" + "<minerals>"
|
||||||
|
+ "<ca>6</ca>" + "<fe>4</fe>" + "</minerals>" + "</food>" +
|
||||||
|
|
||||||
|
"<food>" + "<name>Potato Chips</name>" + "<mfr>Lees</mfr>" + "<serving units=\"g\">28</serving>"
|
||||||
|
+ "<calories total=\"150\" fat=\"90\"/>" + "<total-fat>10</total-fat>" + "<saturated-fat>3</saturated-fat>"
|
||||||
|
+ "<cholesterol>0</cholesterol>" + "<sodium>180</sodium>" + "<carb>15</carb>" + "<fiber>1</fiber>"
|
||||||
|
+ "<protein>2</protein>" + "<vitamins>" + "<a>0</a>" + "<c>10</c>" + "</vitamins>" + "<minerals>"
|
||||||
|
+ "<ca>0</ca>" + "<fe>0</fe>" + "</minerals>" + "</food>" +
|
||||||
|
|
||||||
|
"<food>" + "<name>Soy Patties, Grilled</name>" + "<mfr>Gardenproducts</mfr>"
|
||||||
|
+ "<serving units=\"g\">96</serving>" + "<calories total=\"160\" fat=\"45\"/>" + "<total-fat>5</total-fat>"
|
||||||
|
+ "<saturated-fat>0</saturated-fat>" + "<cholesterol>0</cholesterol>" + "<sodium>420</sodium>"
|
||||||
|
+ "<carb>10</carb>" + "<fiber>4</fiber>" + "<protein>9</protein>" + "<vitamins>" + "<a>0</a>" + "<c>0</c>"
|
||||||
|
+ "</vitamins>" + "<minerals>" + "<ca>0</ca>" + "<fe>0</fe>" + "</minerals>" + "</food>" +
|
||||||
|
|
||||||
|
"<food>" + "<name>Truffles, Dark Chocolate</name>" + "<mfr>Lyndon's</mfr>"
|
||||||
|
+ "<serving units=\"g\">39</serving>" + "<calories total=\"220\" fat=\"170\"/>"
|
||||||
|
+ "<total-fat>19</total-fat>" + "<saturated-fat>14</saturated-fat>" + "<cholesterol>25</cholesterol>"
|
||||||
|
+ "<sodium>10</sodium>" + "<carb>16</carb>" + "<fiber>1</fiber>" + "<protein>1</protein>" + "<vitamins>"
|
||||||
|
+ "<a>0</a>" + "<c>0</c>" + "</vitamins>" + "<minerals>" + "<ca>0</ca>" + "<fe>0</fe>" + "</minerals>"
|
||||||
|
+ "</food>" +
|
||||||
|
|
||||||
|
"</nutrition>";
|
||||||
|
|
||||||
|
|
||||||
|
private String verySimpleXSLT = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<xsl:stylesheet version=\"1.0\" "
|
||||||
|
+ "xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" "
|
||||||
|
+ "xmlns:fn=\"http://www.w3.org/2005/02/xpath-functions\"> " + "<xsl:output method=\"text\" />" +
|
||||||
|
|
||||||
|
"<xsl:preserve-space elements=\"*\"/>" +
|
||||||
|
|
||||||
|
"<xsl:template match=\"/\">" + "<xsl:for-each select=\"/nutrition/food\">"
|
||||||
|
+ "<xsl:value-of select=\"name\"/>" + "</xsl:for-each>" + "</xsl:template>" + "</xsl:stylesheet>";
|
||||||
|
|
||||||
|
private String callParseXmlDocument = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<xsl:stylesheet version=\"1.0\" "
|
||||||
|
+ "xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" "
|
||||||
|
+ "xmlns:fn=\"http://www.w3.org/2005/02/xpath-functions\"> " + "<xsl:output method=\"text\" />" +
|
||||||
|
|
||||||
|
"<xsl:preserve-space elements=\"*\"/>" +
|
||||||
|
|
||||||
|
"<xsl:variable name=\"cf\" select=\"alf:parseXMLDocument('TestXML.xml')\"/>" +
|
||||||
|
"<xsl:template match=\"/\">" + "<xsl:for-each select=\"$cf/food\">"
|
||||||
|
+ "<xsl:value-of select=\"name\"/>" + "</xsl:for-each>" + "</xsl:template>" + "</xsl:stylesheet>";
|
||||||
|
|
||||||
|
private String callParseXmlDocuments = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<xsl:stylesheet version=\"1.0\" "
|
||||||
|
+ "xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" "
|
||||||
|
+ "xmlns:fn=\"http://www.w3.org/2005/02/xpath-functions\"> " + "<xsl:output method=\"text\" />" +
|
||||||
|
|
||||||
|
"<xsl:preserve-space elements=\"*\"/>" +
|
||||||
|
|
||||||
|
"<xsl:variable name=\"all_docs\" select=\"alf:parseXMLDocuments('cm:content', 'path/to/xml/files')\"/>" +
|
||||||
|
"<xsl:template match=\"/\">" + "<xsl:for-each select=\"$all_docs\">" + "<xsl:for-each select=\"food\">"
|
||||||
|
+ "<xsl:value-of select=\"name\"/>" + "</xsl:for-each>" + "</xsl:for-each>" + "</xsl:template>" + "</xsl:stylesheet>";
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@@ -26,13 +26,11 @@ import java.net.URL;
|
|||||||
import java.net.URLConnection;
|
import java.net.URLConnection;
|
||||||
|
|
||||||
import org.alfresco.model.ContentModel;
|
import org.alfresco.model.ContentModel;
|
||||||
import org.alfresco.service.ServiceRegistry;
|
|
||||||
import org.alfresco.service.cmr.repository.ContentReader;
|
import org.alfresco.service.cmr.repository.ContentReader;
|
||||||
import org.alfresco.service.cmr.repository.ContentService;
|
import org.alfresco.service.cmr.repository.ContentService;
|
||||||
import org.alfresco.service.cmr.repository.NodeRef;
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
import org.alfresco.service.cmr.repository.NodeService;
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
import org.alfresco.service.cmr.repository.StoreRef;
|
import org.alfresco.service.cmr.repository.StoreRef;
|
||||||
import org.alfresco.util.ApplicationContextHelper;
|
|
||||||
|
|
||||||
import freemarker.cache.TemplateLoader;
|
import freemarker.cache.TemplateLoader;
|
||||||
|
|
||||||
@@ -85,8 +83,14 @@ public class ClassPathRepoTemplateLoader implements TemplateLoader
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
URL url = this.getClass().getClassLoader().getResource(name);
|
//Fix a common problem: classpath resource paths should not start with "/"
|
||||||
return url == null ? null : new ClassPathTemplateSource(url, encoding);
|
if (name.startsWith("/"))
|
||||||
|
{
|
||||||
|
name = name.substring(1);
|
||||||
|
}
|
||||||
|
ClassLoader classLoader = getClass().getClassLoader();
|
||||||
|
URL url = classLoader.getResource(name);
|
||||||
|
return url == null ? null : new ClassPathTemplateSource(classLoader, name, encoding);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,13 +120,20 @@ public class ClassPathRepoTemplateLoader implements TemplateLoader
|
|||||||
/**
|
/**
|
||||||
* Class used as a base for custom Template Source objects
|
* Class used as a base for custom Template Source objects
|
||||||
*/
|
*/
|
||||||
abstract class BaseTemplateSource
|
abstract class BaseTemplateSource implements TemplateSource
|
||||||
{
|
{
|
||||||
public abstract Reader getReader(String encoding) throws IOException;
|
public abstract Reader getReader(String encoding) throws IOException;
|
||||||
|
|
||||||
public abstract void close() throws IOException;
|
public abstract void close() throws IOException;
|
||||||
|
|
||||||
public abstract long lastModified();
|
public abstract long lastModified();
|
||||||
|
|
||||||
|
public InputStream getResource(String name)
|
||||||
|
{
|
||||||
|
return getRelativeResource(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract InputStream getRelativeResource(String name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -135,10 +146,14 @@ public class ClassPathRepoTemplateLoader implements TemplateLoader
|
|||||||
private URLConnection conn;
|
private URLConnection conn;
|
||||||
private InputStream inputStream;
|
private InputStream inputStream;
|
||||||
private String encoding;
|
private String encoding;
|
||||||
|
private ClassLoader classLoader;
|
||||||
|
private String resourceName;
|
||||||
|
|
||||||
ClassPathTemplateSource(URL url, String encoding) throws IOException
|
ClassPathTemplateSource(ClassLoader classLoader, String name, String encoding) throws IOException
|
||||||
{
|
{
|
||||||
this.url = url;
|
this.classLoader = classLoader;
|
||||||
|
this.resourceName = name;
|
||||||
|
this.url = classLoader.getResource(name);
|
||||||
this.conn = url.openConnection();
|
this.conn = url.openConnection();
|
||||||
this.encoding = encoding;
|
this.encoding = encoding;
|
||||||
}
|
}
|
||||||
@@ -198,6 +213,32 @@ public class ClassPathRepoTemplateLoader implements TemplateLoader
|
|||||||
conn = null;
|
conn = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see org.alfresco.repo.template.ClassPathRepoTemplateLoader.BaseTemplateSource#getRelativeResource(java.lang.String)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected InputStream getRelativeResource(String name)
|
||||||
|
{
|
||||||
|
String newResourceName = name;
|
||||||
|
if (!name.startsWith("/"))
|
||||||
|
{
|
||||||
|
int lastSlash = resourceName.lastIndexOf('/');
|
||||||
|
if (lastSlash != -1)
|
||||||
|
{
|
||||||
|
newResourceName = name.substring(0, lastSlash) + "/" + name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
URL url = classLoader.getResource(newResourceName);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return (url == null) ? null : url.openConnection().getInputStream();
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -263,5 +304,25 @@ public class ClassPathRepoTemplateLoader implements TemplateLoader
|
|||||||
conn = null;
|
conn = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see org.alfresco.repo.template.ClassPathRepoTemplateLoader.BaseTemplateSource#getRelativeResource(java.lang.String)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected InputStream getRelativeResource(String name)
|
||||||
|
{
|
||||||
|
InputStream stream = null;
|
||||||
|
NodeRef parentRef = nodeService.getPrimaryParent(nodeRef).getParentRef();
|
||||||
|
NodeRef child = nodeService.getChildByName(parentRef, ContentModel.ASSOC_CONTAINS, name);
|
||||||
|
if (child != null)
|
||||||
|
{
|
||||||
|
ContentReader contentReader = contentService.getReader(child, ContentModel.PROP_CONTENT);
|
||||||
|
if (contentReader.exists())
|
||||||
|
{
|
||||||
|
stream = contentReader.getContentInputStream();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2010 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* Alfresco 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 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco 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 Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.alfresco.repo.template;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Brian
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface TemplateProcessorMethod extends Serializable
|
||||||
|
{
|
||||||
|
Object exec(final Object[] arguments) throws Exception;
|
||||||
|
}
|
35
source/java/org/alfresco/repo/template/TemplateSource.java
Normal file
35
source/java/org/alfresco/repo/template/TemplateSource.java
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2010 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* Alfresco 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 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco 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 Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.alfresco.repo.template;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.Reader;
|
||||||
|
|
||||||
|
public interface TemplateSource
|
||||||
|
{
|
||||||
|
Reader getReader(String encoding) throws IOException;
|
||||||
|
|
||||||
|
void close() throws IOException;
|
||||||
|
|
||||||
|
long lastModified();
|
||||||
|
|
||||||
|
InputStream getResource(String name);
|
||||||
|
}
|
417
source/java/org/alfresco/repo/template/XSLTProcessor.java
Normal file
417
source/java/org/alfresco/repo/template/XSLTProcessor.java
Normal file
@@ -0,0 +1,417 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2010 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* Alfresco 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 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco 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 Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.template;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.xml.transform.ErrorListener;
|
||||||
|
import javax.xml.transform.Source;
|
||||||
|
import javax.xml.transform.Transformer;
|
||||||
|
import javax.xml.transform.TransformerConfigurationException;
|
||||||
|
import javax.xml.transform.TransformerException;
|
||||||
|
import javax.xml.transform.TransformerFactory;
|
||||||
|
import javax.xml.transform.URIResolver;
|
||||||
|
import javax.xml.transform.dom.DOMSource;
|
||||||
|
import javax.xml.transform.stream.StreamResult;
|
||||||
|
|
||||||
|
import org.alfresco.repo.processor.BaseProcessor;
|
||||||
|
import org.alfresco.service.cmr.repository.TemplateException;
|
||||||
|
import org.alfresco.service.cmr.repository.TemplateProcessor;
|
||||||
|
import org.alfresco.service.namespace.QName;
|
||||||
|
import org.alfresco.util.XMLUtil;
|
||||||
|
import org.apache.bsf.BSFManager;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.apache.xml.utils.Constants;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
|
import freemarker.cache.TemplateLoader;
|
||||||
|
|
||||||
|
public class XSLTProcessor extends BaseProcessor implements TemplateProcessor
|
||||||
|
{
|
||||||
|
private static final Log log = LogFactory.getLog(XSLTProcessor.class);
|
||||||
|
|
||||||
|
private static final String MSG_UNABLE_TO_READ_TEMPLATE = "template.xslt.read_error";
|
||||||
|
private static final String MSG_UNABLE_TO_PARSE_TEMPLATE = "template.xslt.parse_error";
|
||||||
|
|
||||||
|
public final static QName ROOT_NAMESPACE = QName.createQName(null, "root_namespace");
|
||||||
|
|
||||||
|
private String defaultEncoding = "UTF-8";
|
||||||
|
private TemplateLoader templateLoader;
|
||||||
|
|
||||||
|
public void register()
|
||||||
|
{
|
||||||
|
super.register();
|
||||||
|
templateLoader = new ClassPathRepoTemplateLoader(this.services.getNodeService(), this.services
|
||||||
|
.getContentService(), defaultEncoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void process(String template, Object model, Writer out)
|
||||||
|
{
|
||||||
|
TemplateSource templateSource;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
templateSource = (TemplateSource) templateLoader.findTemplateSource(template);
|
||||||
|
}
|
||||||
|
catch (IOException ex)
|
||||||
|
{
|
||||||
|
throw new TemplateException(MSG_UNABLE_TO_READ_TEMPLATE, new Object[] { ex.getMessage() }, ex);
|
||||||
|
}
|
||||||
|
process(templateSource, model, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void processString(final String template, Object model, Writer out)
|
||||||
|
{
|
||||||
|
TemplateSource stringTemplateSource = new TemplateSource()
|
||||||
|
{
|
||||||
|
public long lastModified()
|
||||||
|
{
|
||||||
|
return System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputStream getResource(String name)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Reader getReader(String encoding) throws IOException
|
||||||
|
{
|
||||||
|
return new StringReader(template);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() throws IOException
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
process(stringTemplateSource, model, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param templateSource
|
||||||
|
* @param xsltModel
|
||||||
|
* @param out
|
||||||
|
*/
|
||||||
|
private void process(TemplateSource templateSource, Object model, Writer out)
|
||||||
|
{
|
||||||
|
if ((model == null) || !XSLTemplateModel.class.isAssignableFrom(model.getClass()))
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("\"model\" must be an XSLTemplateModel object: " + model);
|
||||||
|
}
|
||||||
|
|
||||||
|
XSLTemplateModel xsltModel = (XSLTemplateModel) model;
|
||||||
|
System.setProperty("org.apache.xalan.extensions.bsf.BSFManager", BSFManager.class.getName());
|
||||||
|
|
||||||
|
Document xslTemplate;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
xslTemplate = XMLUtil.parse(templateSource.getReader(defaultEncoding));
|
||||||
|
}
|
||||||
|
catch (IOException ex)
|
||||||
|
{
|
||||||
|
throw new TemplateException(MSG_UNABLE_TO_READ_TEMPLATE, new Object[] { ex.getMessage() }, ex);
|
||||||
|
}
|
||||||
|
catch (SAXException sax)
|
||||||
|
{
|
||||||
|
throw new TemplateException(MSG_UNABLE_TO_PARSE_TEMPLATE, new Object[] { sax.getMessage() }, sax);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
templateSource.close();
|
||||||
|
}
|
||||||
|
catch (IOException ex)
|
||||||
|
{
|
||||||
|
// There's little to be done here. Log it and carry on
|
||||||
|
log.warn("Error while trying to close template stream", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> scriptIds = addScripts(xsltModel, xslTemplate);
|
||||||
|
addParameters(xsltModel, xslTemplate);
|
||||||
|
|
||||||
|
final LinkedList<TransformerException> errors = new LinkedList<TransformerException>();
|
||||||
|
final ErrorListener errorListener = new ErrorListener()
|
||||||
|
{
|
||||||
|
public void error(final TransformerException te) throws TransformerException
|
||||||
|
{
|
||||||
|
log.debug("error " + te.getMessageAndLocation());
|
||||||
|
errors.add(te);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fatalError(final TransformerException te) throws TransformerException
|
||||||
|
{
|
||||||
|
log.debug("fatalError " + te.getMessageAndLocation());
|
||||||
|
throw te;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void warning(final TransformerException te) throws TransformerException
|
||||||
|
{
|
||||||
|
log.debug("warning " + te.getMessageAndLocation());
|
||||||
|
errors.add(te);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
final TemplateSource resourceSource = templateSource;
|
||||||
|
final URIResolver uriResolver = new URIResolver()
|
||||||
|
{
|
||||||
|
public Source resolve(final String href, String base) throws TransformerException
|
||||||
|
{
|
||||||
|
if (log.isDebugEnabled())
|
||||||
|
{
|
||||||
|
log.debug("request to resolve href " + href + " using base " + base);
|
||||||
|
}
|
||||||
|
InputStream in = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
in = resourceSource.getResource(href);
|
||||||
|
if (in == null)
|
||||||
|
{
|
||||||
|
throw new TransformerException("unable to resolve href " + href);
|
||||||
|
}
|
||||||
|
|
||||||
|
Document d = XMLUtil.parse(in);
|
||||||
|
if (log.isDebugEnabled())
|
||||||
|
{
|
||||||
|
log.debug("loaded " + XMLUtil.toString(d));
|
||||||
|
}
|
||||||
|
return new DOMSource(d);
|
||||||
|
}
|
||||||
|
catch (TransformerException ex)
|
||||||
|
{
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new TransformerException("unable to load " + href, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Source xmlSource = this.getXMLSource(xsltModel);
|
||||||
|
|
||||||
|
Transformer t = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
final TransformerFactory tf = TransformerFactory.newInstance();
|
||||||
|
tf.setErrorListener(errorListener);
|
||||||
|
tf.setURIResolver(uriResolver);
|
||||||
|
|
||||||
|
if (log.isDebugEnabled())
|
||||||
|
{
|
||||||
|
log.debug("xslTemplate: \n" + XMLUtil.toString(xslTemplate));
|
||||||
|
}
|
||||||
|
|
||||||
|
t = tf.newTransformer(new DOMSource(xslTemplate));
|
||||||
|
|
||||||
|
if (errors.size() != 0)
|
||||||
|
{
|
||||||
|
final StringBuilder msg = new StringBuilder("errors encountered creating tranformer ... \n");
|
||||||
|
for (TransformerException te : errors)
|
||||||
|
{
|
||||||
|
msg.append(te.getMessageAndLocation()).append("\n");
|
||||||
|
}
|
||||||
|
throw new TemplateException(msg.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
t.setErrorListener(errorListener);
|
||||||
|
t.setURIResolver(uriResolver);
|
||||||
|
t.setParameter("versionParam", "2.0");
|
||||||
|
}
|
||||||
|
catch (TransformerConfigurationException tce)
|
||||||
|
{
|
||||||
|
log.error(tce);
|
||||||
|
throw new TemplateException(tce.getMessage(), tce);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
t.transform(xmlSource, new StreamResult(out));
|
||||||
|
}
|
||||||
|
catch (TransformerException te)
|
||||||
|
{
|
||||||
|
log.error(te.getMessageAndLocation());
|
||||||
|
throw new TemplateException(te.getMessageAndLocation(), te);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
log.error("unexpected error " + e);
|
||||||
|
throw new TemplateException(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
//Clear out any scripts that were created for this transform
|
||||||
|
if (!scriptIds.isEmpty())
|
||||||
|
{
|
||||||
|
XSLTProcessorMethodInvoker.removeMethods(scriptIds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errors.size() != 0)
|
||||||
|
{
|
||||||
|
final StringBuilder msg = new StringBuilder("errors encountered during transformation ... \n");
|
||||||
|
for (TransformerException te : errors)
|
||||||
|
{
|
||||||
|
msg.append(te.getMessageAndLocation()).append("\n");
|
||||||
|
}
|
||||||
|
throw new TemplateException(msg.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a script element to the xsl which makes static methods on this object available to the xsl tempalte.
|
||||||
|
*
|
||||||
|
* @param xslTemplate
|
||||||
|
* the xsl template
|
||||||
|
*/
|
||||||
|
protected List<String> addScripts(final XSLTemplateModel xsltModel, final Document xslTemplate)
|
||||||
|
{
|
||||||
|
final Map<QName, List<Map.Entry<QName, Object>>> methods = new HashMap<QName, List<Map.Entry<QName, Object>>>();
|
||||||
|
for (final Map.Entry<QName, Object> entry : xsltModel.entrySet())
|
||||||
|
{
|
||||||
|
if (entry.getValue() instanceof TemplateProcessorMethod)
|
||||||
|
{
|
||||||
|
final String prefix = QName.splitPrefixedQName(entry.getKey().toPrefixString())[0];
|
||||||
|
final QName qn = QName.createQName(entry.getKey().getNamespaceURI(), prefix);
|
||||||
|
if (!methods.containsKey(qn))
|
||||||
|
{
|
||||||
|
methods.put(qn, new LinkedList<Map.Entry<QName, Object>>());
|
||||||
|
}
|
||||||
|
methods.get(qn).add(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final Element docEl = xslTemplate.getDocumentElement();
|
||||||
|
final String XALAN_NS = Constants.S_BUILTIN_EXTENSIONS_URL;
|
||||||
|
final String XALAN_NS_PREFIX = "xalan";
|
||||||
|
docEl.setAttribute("xmlns:" + XALAN_NS_PREFIX, XALAN_NS);
|
||||||
|
|
||||||
|
final Set<String> excludePrefixes = new HashSet<String>();
|
||||||
|
if (docEl.hasAttribute("exclude-result-prefixes"))
|
||||||
|
{
|
||||||
|
excludePrefixes.addAll(Arrays.asList(docEl.getAttribute("exclude-result-prefixes").split(" ")));
|
||||||
|
}
|
||||||
|
excludePrefixes.add(XALAN_NS_PREFIX);
|
||||||
|
|
||||||
|
final List<String> result = new LinkedList<String>();
|
||||||
|
for (QName ns : methods.keySet())
|
||||||
|
{
|
||||||
|
final String prefix = ns.getLocalName();
|
||||||
|
docEl.setAttribute("xmlns:" + prefix, ns.getNamespaceURI());
|
||||||
|
excludePrefixes.add(prefix);
|
||||||
|
|
||||||
|
final Element compEl = xslTemplate.createElementNS(XALAN_NS, XALAN_NS_PREFIX + ":component");
|
||||||
|
compEl.setAttribute("prefix", prefix);
|
||||||
|
docEl.appendChild(compEl);
|
||||||
|
String functionNames = null;
|
||||||
|
final Element scriptEl = xslTemplate.createElementNS(XALAN_NS, XALAN_NS_PREFIX + ":script");
|
||||||
|
scriptEl.setAttribute("lang", "javascript");
|
||||||
|
final StringBuilder js = new StringBuilder("var _xsltp_invoke = java.lang.Class.forName('"
|
||||||
|
+ XSLTProcessorMethodInvoker.class.getName() + "').newInstance();\n"
|
||||||
|
+ "function _xsltp_to_java_array(js_array) {\n"
|
||||||
|
+ "var java_array = java.lang.reflect.Array.newInstance(java.lang.Object, js_array.length);\n"
|
||||||
|
+ "for (var i = 0; i < js_array.length; i++) { java_array[i] = js_array[i]; }\n"
|
||||||
|
+ "return java_array; }\n");
|
||||||
|
for (final Map.Entry<QName, Object> entry : methods.get(ns))
|
||||||
|
{
|
||||||
|
if (functionNames == null)
|
||||||
|
{
|
||||||
|
functionNames = entry.getKey().getLocalName();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
functionNames += " " + entry.getKey().getLocalName();
|
||||||
|
}
|
||||||
|
final String id = entry.getKey().getLocalName() + entry.getValue().hashCode();
|
||||||
|
js.append("function " + entry.getKey().getLocalName() + "() { return _xsltp_invoke.invokeMethod('" + id
|
||||||
|
+ "', _xsltp_to_java_array(arguments)); }\n");
|
||||||
|
XSLTProcessorMethodInvoker.addMethod(id, (TemplateProcessorMethod) entry.getValue());
|
||||||
|
result.add(id);
|
||||||
|
}
|
||||||
|
log.debug("generated JavaScript bindings:\n" + js);
|
||||||
|
scriptEl.appendChild(xslTemplate.createTextNode(js.toString()));
|
||||||
|
compEl.setAttribute("functions", functionNames);
|
||||||
|
compEl.appendChild(scriptEl);
|
||||||
|
}
|
||||||
|
docEl.setAttribute("exclude-result-prefixes", StringUtils.join(excludePrefixes
|
||||||
|
.toArray(new String[excludePrefixes.size()]), " "));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the specified parameters to the xsl template as variables within the alfresco namespace.
|
||||||
|
*
|
||||||
|
* @param xsltModel
|
||||||
|
* the variables to place within the xsl template
|
||||||
|
* @param xslTemplate
|
||||||
|
* the xsl template
|
||||||
|
*/
|
||||||
|
protected void addParameters(final XSLTemplateModel xsltModel, final Document xslTemplate)
|
||||||
|
{
|
||||||
|
final Element docEl = xslTemplate.getDocumentElement();
|
||||||
|
final String XSL_NS = docEl.getNamespaceURI();
|
||||||
|
final String XSL_NS_PREFIX = docEl.getPrefix();
|
||||||
|
|
||||||
|
for (Map.Entry<QName, Object> e : xsltModel.entrySet())
|
||||||
|
{
|
||||||
|
if (ROOT_NAMESPACE.equals(e.getKey()))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final Element el = xslTemplate.createElementNS(XSL_NS, XSL_NS_PREFIX + ":variable");
|
||||||
|
el.setAttribute("name", e.getKey().toPrefixString());
|
||||||
|
final Object o = e.getValue();
|
||||||
|
if (o instanceof String || o instanceof Number || o instanceof Boolean)
|
||||||
|
{
|
||||||
|
el.appendChild(xslTemplate.createTextNode(o.toString()));
|
||||||
|
docEl.insertBefore(el, docEl.getFirstChild());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Source getXMLSource(final Map<QName, Object> model)
|
||||||
|
{
|
||||||
|
if (!model.containsKey(ROOT_NAMESPACE))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final Object o = model.get(ROOT_NAMESPACE);
|
||||||
|
if (!(o instanceof Document))
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("expected root namespace object to be a " + Document.class.getName()
|
||||||
|
+ ". found a " + o.getClass().getName());
|
||||||
|
}
|
||||||
|
return new DOMSource((Document) o);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,200 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2010 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* Alfresco 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 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco 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 Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.alfresco.repo.template;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.apache.xml.dtm.ref.DTMNodeProxy;
|
||||||
|
import org.w3c.dom.DOMException;
|
||||||
|
import org.w3c.dom.Node;
|
||||||
|
import org.w3c.dom.traversal.NodeFilter;
|
||||||
|
import org.w3c.dom.traversal.NodeIterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Brian
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class XSLTProcessorMethodInvoker
|
||||||
|
{
|
||||||
|
private final static Log log = LogFactory.getLog(XSLTProcessorMethodInvoker.class);
|
||||||
|
|
||||||
|
private final static Map<String, TemplateProcessorMethod> PROCESSOR_METHODS = new TreeMap<String, TemplateProcessorMethod>();
|
||||||
|
|
||||||
|
public XSLTProcessorMethodInvoker()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addMethod(String name, TemplateProcessorMethod method)
|
||||||
|
{
|
||||||
|
PROCESSOR_METHODS.put(name, method);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removeMethods(Collection<String> methodNames)
|
||||||
|
{
|
||||||
|
for (String methodName : methodNames)
|
||||||
|
{
|
||||||
|
PROCESSOR_METHODS.remove(methodName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object[] convertArguments(final Object[] arguments)
|
||||||
|
{
|
||||||
|
final List<Object> result = new LinkedList<Object>();
|
||||||
|
for (int i = 0; i < arguments.length; i++)
|
||||||
|
{
|
||||||
|
log.debug("args[" + i + "] = " + arguments[i] + "("
|
||||||
|
+ (arguments[i] != null ? arguments[i].getClass().getName() : "null") + ")");
|
||||||
|
if (arguments[i] == null || arguments[i] instanceof String || arguments[i] instanceof Number)
|
||||||
|
{
|
||||||
|
result.add(arguments[i]);
|
||||||
|
}
|
||||||
|
else if (arguments[i] instanceof DTMNodeProxy)
|
||||||
|
{
|
||||||
|
result.add(((DTMNodeProxy) arguments[i]).getStringValue());
|
||||||
|
}
|
||||||
|
else if (arguments[i] instanceof Node)
|
||||||
|
{
|
||||||
|
log.debug("node type is " + ((Node) arguments[i]).getNodeType() + " content "
|
||||||
|
+ ((Node) arguments[i]).getTextContent());
|
||||||
|
result.add(((Node) arguments[i]).getNodeValue());
|
||||||
|
}
|
||||||
|
else if (arguments[i] instanceof NodeIterator)
|
||||||
|
{
|
||||||
|
Node n = ((NodeIterator) arguments[i]).nextNode();
|
||||||
|
while (n != null)
|
||||||
|
{
|
||||||
|
log.debug("iterated to node " + n + " type " + n.getNodeType() + " value " + n.getNodeValue()
|
||||||
|
+ " tc " + n.getTextContent() + " nn " + n.getNodeName() + " sv "
|
||||||
|
+ ((org.apache.xml.dtm.ref.DTMNodeProxy) n).getStringValue());
|
||||||
|
if (n instanceof DTMNodeProxy)
|
||||||
|
{
|
||||||
|
result.add(((DTMNodeProxy) n).getStringValue());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result.add(n);
|
||||||
|
}
|
||||||
|
n = ((NodeIterator) arguments[i]).nextNode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("unable to convert argument " + arguments[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.toArray(new Object[result.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object invokeMethod(final String id, Object[] arguments) throws Exception
|
||||||
|
{
|
||||||
|
if (!PROCESSOR_METHODS.containsKey(id))
|
||||||
|
{
|
||||||
|
throw new NullPointerException("unable to find method " + id);
|
||||||
|
}
|
||||||
|
|
||||||
|
final TemplateProcessorMethod method = PROCESSOR_METHODS.get(id);
|
||||||
|
arguments = this.convertArguments(arguments);
|
||||||
|
log.debug("invoking " + id + " with " + arguments.length);
|
||||||
|
|
||||||
|
Object result = method.exec(arguments);
|
||||||
|
log.debug(id + " returned a " + result);
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else if (result.getClass().isArray() && Node.class.isAssignableFrom(result.getClass().getComponentType()))
|
||||||
|
{
|
||||||
|
log.debug("converting " + result + " to a node iterator");
|
||||||
|
final Node[] array = (Node[]) result;
|
||||||
|
return new NodeIterator()
|
||||||
|
{
|
||||||
|
private int index = 0;
|
||||||
|
private boolean detached = false;
|
||||||
|
|
||||||
|
public void detach()
|
||||||
|
{
|
||||||
|
if (log.isDebugEnabled())
|
||||||
|
log.debug("detaching NodeIterator");
|
||||||
|
this.detached = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getExpandEntityReferences()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getWhatToShow()
|
||||||
|
{
|
||||||
|
return NodeFilter.SHOW_ALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node getRoot()
|
||||||
|
{
|
||||||
|
return (array.length == 0 ? null : array[0].getOwnerDocument().getDocumentElement());
|
||||||
|
}
|
||||||
|
|
||||||
|
public NodeFilter getFilter()
|
||||||
|
{
|
||||||
|
return new NodeFilter()
|
||||||
|
{
|
||||||
|
public short acceptNode(final Node n)
|
||||||
|
{
|
||||||
|
return NodeFilter.FILTER_ACCEPT;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node nextNode() throws DOMException
|
||||||
|
{
|
||||||
|
if (log.isDebugEnabled())
|
||||||
|
log.debug("NodeIterator.nextNode(" + index + ")");
|
||||||
|
if (this.detached)
|
||||||
|
throw new DOMException(DOMException.INVALID_STATE_ERR, null);
|
||||||
|
return index == array.length ? null : array[index++];
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node previousNode() throws DOMException
|
||||||
|
{
|
||||||
|
if (log.isDebugEnabled())
|
||||||
|
log.debug("NodeIterator.previousNode(" + index + ")");
|
||||||
|
if (this.detached)
|
||||||
|
throw new DOMException(DOMException.INVALID_STATE_ERR, null);
|
||||||
|
return index == -1 ? null : array[index--];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (result instanceof String || result instanceof Number || result instanceof Node)
|
||||||
|
{
|
||||||
|
log.debug("returning " + result + " as is");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("unable to convert " + result.getClass().getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
270
source/java/org/alfresco/repo/template/XSLTProcessorTest.java
Normal file
270
source/java/org/alfresco/repo/template/XSLTProcessorTest.java
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2010 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* Alfresco 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 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco 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 Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.alfresco.repo.template;
|
||||||
|
|
||||||
|
import java.io.StringWriter;
|
||||||
|
|
||||||
|
import org.alfresco.model.ContentModel;
|
||||||
|
import org.alfresco.repo.rendition.executer.XSLTFunctions;
|
||||||
|
import org.alfresco.service.cmr.model.FileFolderService;
|
||||||
|
import org.alfresco.service.cmr.model.FileInfo;
|
||||||
|
import org.alfresco.service.cmr.repository.ContentService;
|
||||||
|
import org.alfresco.service.cmr.repository.ContentWriter;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
|
import org.alfresco.service.cmr.repository.StoreRef;
|
||||||
|
import org.alfresco.service.cmr.repository.TemplateProcessor;
|
||||||
|
import org.alfresco.service.cmr.repository.TemplateService;
|
||||||
|
import org.alfresco.service.cmr.search.ResultSet;
|
||||||
|
import org.alfresco.service.cmr.search.SearchService;
|
||||||
|
import org.alfresco.util.BaseAlfrescoSpringTest;
|
||||||
|
import org.alfresco.util.GUID;
|
||||||
|
import org.alfresco.util.XMLUtil;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Brian
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class XSLTProcessorTest extends BaseAlfrescoSpringTest
|
||||||
|
{
|
||||||
|
private final static Log log = LogFactory.getLog(XSLTProcessorTest.class);
|
||||||
|
private XSLTFunctions xsltFunctions;
|
||||||
|
private SearchService searchService;
|
||||||
|
private NodeRef companyHome;
|
||||||
|
private FileFolderService fileFolderService;
|
||||||
|
private TemplateProcessor xsltProcessor;
|
||||||
|
private TemplateService templateService;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
*
|
||||||
|
* @see org.alfresco.util.BaseAlfrescoSpringTest#onSetUpInTransaction()
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
@Override
|
||||||
|
protected void onSetUpInTransaction() throws Exception
|
||||||
|
{
|
||||||
|
super.onSetUpInTransaction();
|
||||||
|
this.searchService = (SearchService) this.applicationContext.getBean("SearchService");
|
||||||
|
this.xsltFunctions = (XSLTFunctions) this.applicationContext.getBean("xsltFunctions");
|
||||||
|
this.nodeService = (NodeService) this.applicationContext.getBean("NodeService");
|
||||||
|
this.contentService = (ContentService) this.applicationContext.getBean("ContentService");
|
||||||
|
this.fileFolderService = (FileFolderService) this.applicationContext.getBean("FileFolderService");
|
||||||
|
this.xsltProcessor = (TemplateProcessor) this.applicationContext.getBean("xsltProcessor");
|
||||||
|
this.templateService = (TemplateService) this.applicationContext.getBean("TemplateService");
|
||||||
|
ResultSet rs = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, SearchService.LANGUAGE_XPATH,
|
||||||
|
"/app:company_home");
|
||||||
|
this.companyHome = rs.getNodeRef(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSimplestStringTemplate() throws Exception
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
FileInfo file = createXmlFile(companyHome);
|
||||||
|
XSLTemplateModel model = new XSLTemplateModel();
|
||||||
|
model.put(XSLTProcessor.ROOT_NAMESPACE, XMLUtil.parse(file.getNodeRef(), contentService));
|
||||||
|
|
||||||
|
StringWriter writer = new StringWriter();
|
||||||
|
xsltProcessor.processString(verySimpleXSLT, model, writer);
|
||||||
|
String output = writer.toString();
|
||||||
|
|
||||||
|
log.debug("XSLT Processor output: " + output);
|
||||||
|
assertEquals("Avocado DipBagels, New York StyleBeef Frankfurter, Quarter PoundChicken Pot PieCole SlawEggsHazelnut SpreadPotato ChipsSoy Patties, GrilledTruffles, Dark Chocolate", output);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
log.error("Error!", ex);
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSimplestNodeTemplate() throws Exception
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
FileInfo xmlFile = createXmlFile(companyHome);
|
||||||
|
FileInfo xslFile = createXmlFile(companyHome, verySimpleXSLT);
|
||||||
|
XSLTemplateModel model = new XSLTemplateModel();
|
||||||
|
model.put(XSLTProcessor.ROOT_NAMESPACE, XMLUtil.parse(xmlFile.getNodeRef(), contentService));
|
||||||
|
|
||||||
|
StringWriter writer = new StringWriter();
|
||||||
|
xsltProcessor.process(xslFile.getNodeRef().toString(), model, writer);
|
||||||
|
|
||||||
|
String output = writer.toString();
|
||||||
|
|
||||||
|
log.debug("XSLT Processor output: " + output);
|
||||||
|
assertEquals("Avocado DipBagels, New York StyleBeef Frankfurter, Quarter PoundChicken Pot PieCole SlawEggsHazelnut SpreadPotato ChipsSoy Patties, GrilledTruffles, Dark Chocolate", output);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
log.error("Error!", ex);
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSimplestClasspathTemplate() throws Exception
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
FileInfo xmlFile = createXmlFile(companyHome);
|
||||||
|
XSLTemplateModel model = new XSLTemplateModel();
|
||||||
|
model.put(XSLTProcessor.ROOT_NAMESPACE, XMLUtil.parse(xmlFile.getNodeRef(), contentService));
|
||||||
|
|
||||||
|
StringWriter writer = new StringWriter();
|
||||||
|
xsltProcessor.process("org/alfresco/repo/template/test_template1.xsl", model, writer);
|
||||||
|
|
||||||
|
String output = writer.toString();
|
||||||
|
|
||||||
|
log.debug("XSLT Processor output: " + output);
|
||||||
|
assertEquals("Avocado DipBagels, New York StyleBeef Frankfurter, Quarter PoundChicken Pot PieCole SlawEggsHazelnut SpreadPotato ChipsSoy Patties, GrilledTruffles, Dark Chocolate", output);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
log.error("Error!", ex);
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testTemplateServiceBinding() throws Exception
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
FileInfo xmlFile = createXmlFile(companyHome);
|
||||||
|
XSLTemplateModel model = new XSLTemplateModel();
|
||||||
|
model.put(XSLTProcessor.ROOT_NAMESPACE, XMLUtil.parse(xmlFile.getNodeRef(), contentService));
|
||||||
|
|
||||||
|
StringWriter writer = new StringWriter();
|
||||||
|
templateService.processTemplate("xslt", "org/alfresco/repo/template/test_template1.xsl", model, writer);
|
||||||
|
|
||||||
|
String output = writer.toString();
|
||||||
|
|
||||||
|
log.debug("XSLT Processor output: " + output);
|
||||||
|
assertEquals("Avocado DipBagels, New York StyleBeef Frankfurter, Quarter PoundChicken Pot PieCole SlawEggsHazelnut SpreadPotato ChipsSoy Patties, GrilledTruffles, Dark Chocolate", output);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
log.error("Error!", ex);
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private FileInfo createXmlFile(NodeRef folder)
|
||||||
|
{
|
||||||
|
return createXmlFile(folder, sampleXML);
|
||||||
|
}
|
||||||
|
|
||||||
|
private FileInfo createXmlFile(NodeRef folder, String content)
|
||||||
|
{
|
||||||
|
String name = GUID.generate();
|
||||||
|
FileInfo testXmlFile = fileFolderService.create(folder, name + ".xml", ContentModel.TYPE_CONTENT);
|
||||||
|
ContentWriter writer = contentService.getWriter(testXmlFile.getNodeRef(), ContentModel.PROP_CONTENT, true);
|
||||||
|
writer.setMimetype("text/xml");
|
||||||
|
writer.setEncoding("UTF-8");
|
||||||
|
writer.putContent(content);
|
||||||
|
return testXmlFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String sampleXML = "<?xml version=\"1.0\"?>" + "<nutrition>" +
|
||||||
|
|
||||||
|
"<daily-values>" + "<total-fat units=\"g\">65</total-fat>" + "<saturated-fat units=\"g\">20</saturated-fat>"
|
||||||
|
+ "<cholesterol units=\"mg\">300</cholesterol>" + "<sodium units=\"mg\">2400</sodium>"
|
||||||
|
+ "<carb units=\"g\">300</carb>" + "<fiber units=\"g\">25</fiber>" + "<protein units=\"g\">50</protein>"
|
||||||
|
+ "</daily-values>" +
|
||||||
|
|
||||||
|
"<food>" + "<name>Avocado Dip</name>" + "<mfr>Sunnydale</mfr>" + "<serving units=\"g\">29</serving>"
|
||||||
|
+ "<calories total=\"110\" fat=\"100\"/>" + "<total-fat>11</total-fat>"
|
||||||
|
+ "<saturated-fat>3</saturated-fat>" + "<cholesterol>5</cholesterol>" + "<sodium>210</sodium>"
|
||||||
|
+ "<carb>2</carb>" + "<fiber>0</fiber>" + "<protein>1</protein>" + "<vitamins>" + "<a>0</a>" + "<c>0</c>"
|
||||||
|
+ "</vitamins>" + "<minerals>" + "<ca>0</ca>" + "<fe>0</fe>" + "</minerals>" + "</food>" +
|
||||||
|
|
||||||
|
"<food>" + "<name>Bagels, New York Style</name>" + "<mfr>Thompson</mfr>"
|
||||||
|
+ "<serving units=\"g\">104</serving>" + "<calories total=\"300\" fat=\"35\"/>"
|
||||||
|
+ "<total-fat>4</total-fat>" + "<saturated-fat>1</saturated-fat>" + "<cholesterol>0</cholesterol>"
|
||||||
|
+ "<sodium>510</sodium>" + "<carb>54</carb>" + "<fiber>3</fiber>" + "<protein>11</protein>" + "<vitamins>"
|
||||||
|
+ "<a>0</a>" + "<c>0</c>" + "</vitamins>" + "<minerals>" + "<ca>8</ca>" + "<fe>20</fe>" + "</minerals>"
|
||||||
|
+ "</food>" +
|
||||||
|
|
||||||
|
"<food>" + "<name>Beef Frankfurter, Quarter Pound</name>" + "<mfr>Armitage</mfr>"
|
||||||
|
+ "<serving units=\"g\">115</serving>" + "<calories total=\"370\" fat=\"290\"/>"
|
||||||
|
+ "<total-fat>32</total-fat>" + "<saturated-fat>15</saturated-fat>" + "<cholesterol>65</cholesterol>"
|
||||||
|
+ "<sodium>1100</sodium>" + "<carb>8</carb>" + "<fiber>0</fiber>" + "<protein>13</protein>" + "<vitamins>"
|
||||||
|
+ "<a>0</a>" + "<c>2</c>" + "</vitamins>" + "<minerals>" + "<ca>1</ca>" + "<fe>6</fe>" + "</minerals>"
|
||||||
|
+ "</food>" +
|
||||||
|
|
||||||
|
"<food>" + "<name>Chicken Pot Pie</name>" + "<mfr>Lakeson</mfr>" + "<serving units=\"g\">198</serving>"
|
||||||
|
+ "<calories total=\"410\" fat=\"200\"/>" + "<total-fat>22</total-fat>"
|
||||||
|
+ "<saturated-fat>9</saturated-fat>" + "<cholesterol>25</cholesterol>" + "<sodium>810</sodium>"
|
||||||
|
+ "<carb>42</carb>" + "<fiber>2</fiber>" + "<protein>10</protein>" + "<vitamins>" + "<a>20</a>"
|
||||||
|
+ "<c>2</c>" + "</vitamins>" + "<minerals>" + "<ca>2</ca>" + "<fe>10</fe>" + "</minerals>" + "</food>" +
|
||||||
|
|
||||||
|
"<food>" + "<name>Cole Slaw</name>" + "<mfr>Fresh Quick</mfr>" + "<serving units=\" cup\">1.5</serving>"
|
||||||
|
+ "<calories total=\"20\" fat=\"0\"/>" + "<total-fat>0</total-fat>" + "<saturated-fat>0</saturated-fat>"
|
||||||
|
+ "<cholesterol>0</cholesterol>" + "<sodium>15</sodium>" + "<carb>5</carb>" + "<fiber>2</fiber>"
|
||||||
|
+ "<protein>1</protein>" + "<vitamins>" + "<a>30</a>" + "<c>45</c>" + "</vitamins>" + "<minerals>"
|
||||||
|
+ "<ca>4</ca>" + "<fe>2</fe>" + "</minerals>" + "</food>" +
|
||||||
|
|
||||||
|
"<food>" + "<name>Eggs</name>" + "<mfr>Goodpath</mfr>" + "<serving units=\"g\">50</serving>"
|
||||||
|
+ "<calories total=\"70\" fat=\"40\"/>" + "<total-fat>4.5</total-fat>"
|
||||||
|
+ "<saturated-fat>1.5</saturated-fat>" + "<cholesterol>215</cholesterol>" + "<sodium>65</sodium>"
|
||||||
|
+ "<carb>1</carb>" + "<fiber>0</fiber>" + "<protein>6</protein>" + "<vitamins>" + "<a>6</a>" + "<c>0</c>"
|
||||||
|
+ "</vitamins>" + "<minerals>" + "<ca>2</ca>" + "<fe>4</fe>" + "</minerals>" + "</food>" +
|
||||||
|
|
||||||
|
"<food>" + "<name>Hazelnut Spread</name>" + "<mfr>Ferreira</mfr>" + "<serving units=\"tbsp\">2</serving>"
|
||||||
|
+ "<calories total=\"200\" fat=\"90\"/>" + "<total-fat>10</total-fat>" + "<saturated-fat>2</saturated-fat>"
|
||||||
|
+ "<cholesterol>0</cholesterol>" + "<sodium>20</sodium>" + "<carb>23</carb>" + "<fiber>2</fiber>"
|
||||||
|
+ "<protein>3</protein>" + "<vitamins>" + "<a>0</a>" + "<c>0</c>" + "</vitamins>" + "<minerals>"
|
||||||
|
+ "<ca>6</ca>" + "<fe>4</fe>" + "</minerals>" + "</food>" +
|
||||||
|
|
||||||
|
"<food>" + "<name>Potato Chips</name>" + "<mfr>Lees</mfr>" + "<serving units=\"g\">28</serving>"
|
||||||
|
+ "<calories total=\"150\" fat=\"90\"/>" + "<total-fat>10</total-fat>" + "<saturated-fat>3</saturated-fat>"
|
||||||
|
+ "<cholesterol>0</cholesterol>" + "<sodium>180</sodium>" + "<carb>15</carb>" + "<fiber>1</fiber>"
|
||||||
|
+ "<protein>2</protein>" + "<vitamins>" + "<a>0</a>" + "<c>10</c>" + "</vitamins>" + "<minerals>"
|
||||||
|
+ "<ca>0</ca>" + "<fe>0</fe>" + "</minerals>" + "</food>" +
|
||||||
|
|
||||||
|
"<food>" + "<name>Soy Patties, Grilled</name>" + "<mfr>Gardenproducts</mfr>"
|
||||||
|
+ "<serving units=\"g\">96</serving>" + "<calories total=\"160\" fat=\"45\"/>" + "<total-fat>5</total-fat>"
|
||||||
|
+ "<saturated-fat>0</saturated-fat>" + "<cholesterol>0</cholesterol>" + "<sodium>420</sodium>"
|
||||||
|
+ "<carb>10</carb>" + "<fiber>4</fiber>" + "<protein>9</protein>" + "<vitamins>" + "<a>0</a>" + "<c>0</c>"
|
||||||
|
+ "</vitamins>" + "<minerals>" + "<ca>0</ca>" + "<fe>0</fe>" + "</minerals>" + "</food>" +
|
||||||
|
|
||||||
|
"<food>" + "<name>Truffles, Dark Chocolate</name>" + "<mfr>Lyndon's</mfr>"
|
||||||
|
+ "<serving units=\"g\">39</serving>" + "<calories total=\"220\" fat=\"170\"/>"
|
||||||
|
+ "<total-fat>19</total-fat>" + "<saturated-fat>14</saturated-fat>" + "<cholesterol>25</cholesterol>"
|
||||||
|
+ "<sodium>10</sodium>" + "<carb>16</carb>" + "<fiber>1</fiber>" + "<protein>1</protein>" + "<vitamins>"
|
||||||
|
+ "<a>0</a>" + "<c>0</c>" + "</vitamins>" + "<minerals>" + "<ca>0</ca>" + "<fe>0</fe>" + "</minerals>"
|
||||||
|
+ "</food>" +
|
||||||
|
|
||||||
|
"</nutrition>";
|
||||||
|
|
||||||
|
|
||||||
|
private String verySimpleXSLT = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<xsl:stylesheet version=\"1.0\" "
|
||||||
|
+ "xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" "
|
||||||
|
+ "xmlns:fn=\"http://www.w3.org/2005/02/xpath-functions\"> " + "<xsl:output method=\"text\" />" +
|
||||||
|
|
||||||
|
"<xsl:preserve-space elements=\"*\"/>" +
|
||||||
|
|
||||||
|
"<xsl:template match=\"/\">" + "<xsl:for-each select=\"/nutrition/food\">"
|
||||||
|
+ "<xsl:value-of select=\"name\"/>" + "</xsl:for-each>" + "</xsl:template>" + "</xsl:stylesheet>";
|
||||||
|
}
|
166
source/java/org/alfresco/repo/template/XSLTemplateModel.java
Normal file
166
source/java/org/alfresco/repo/template/XSLTemplateModel.java
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2010 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* Alfresco 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 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco 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 Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.template;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.alfresco.service.namespace.QName;
|
||||||
|
|
||||||
|
public class XSLTemplateModel implements Map<QName, Object>
|
||||||
|
{
|
||||||
|
private Map<QName, Object> wrappedMap = new HashMap<QName, Object>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @see java.util.Map#clear()
|
||||||
|
*/
|
||||||
|
public void clear()
|
||||||
|
{
|
||||||
|
wrappedMap.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param key
|
||||||
|
* @return
|
||||||
|
* @see java.util.Map#containsKey(java.lang.Object)
|
||||||
|
*/
|
||||||
|
public boolean containsKey(Object key)
|
||||||
|
{
|
||||||
|
return wrappedMap.containsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param value
|
||||||
|
* @return
|
||||||
|
* @see java.util.Map#containsValue(java.lang.Object)
|
||||||
|
*/
|
||||||
|
public boolean containsValue(Object value)
|
||||||
|
{
|
||||||
|
return wrappedMap.containsValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return
|
||||||
|
* @see java.util.Map#entrySet()
|
||||||
|
*/
|
||||||
|
public Set<Entry<QName, Object>> entrySet()
|
||||||
|
{
|
||||||
|
return wrappedMap.entrySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param o
|
||||||
|
* @return
|
||||||
|
* @see java.util.Map#equals(java.lang.Object)
|
||||||
|
*/
|
||||||
|
public boolean equals(Object o)
|
||||||
|
{
|
||||||
|
return wrappedMap.equals(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param key
|
||||||
|
* @return
|
||||||
|
* @see java.util.Map#get(java.lang.Object)
|
||||||
|
*/
|
||||||
|
public Object get(Object key)
|
||||||
|
{
|
||||||
|
return wrappedMap.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return
|
||||||
|
* @see java.util.Map#hashCode()
|
||||||
|
*/
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
return wrappedMap.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return
|
||||||
|
* @see java.util.Map#isEmpty()
|
||||||
|
*/
|
||||||
|
public boolean isEmpty()
|
||||||
|
{
|
||||||
|
return wrappedMap.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return
|
||||||
|
* @see java.util.Map#keySet()
|
||||||
|
*/
|
||||||
|
public Set<QName> keySet()
|
||||||
|
{
|
||||||
|
return wrappedMap.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param key
|
||||||
|
* @param value
|
||||||
|
* @return
|
||||||
|
* @see java.util.Map#put(java.lang.Object, java.lang.Object)
|
||||||
|
*/
|
||||||
|
public Object put(QName key, Object value)
|
||||||
|
{
|
||||||
|
return wrappedMap.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param m
|
||||||
|
* @see java.util.Map#putAll(java.util.Map)
|
||||||
|
*/
|
||||||
|
public void putAll(Map<? extends QName, ? extends Object> m)
|
||||||
|
{
|
||||||
|
wrappedMap.putAll(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param key
|
||||||
|
* @return
|
||||||
|
* @see java.util.Map#remove(java.lang.Object)
|
||||||
|
*/
|
||||||
|
public Object remove(Object key)
|
||||||
|
{
|
||||||
|
return wrappedMap.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return
|
||||||
|
* @see java.util.Map#size()
|
||||||
|
*/
|
||||||
|
public int size()
|
||||||
|
{
|
||||||
|
return wrappedMap.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return
|
||||||
|
* @see java.util.Map#values()
|
||||||
|
*/
|
||||||
|
public Collection<Object> values()
|
||||||
|
{
|
||||||
|
return wrappedMap.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
11
source/java/org/alfresco/repo/template/test_template1.xsl
Normal file
11
source/java/org/alfresco/repo/template/test_template1.xsl
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<xsl:stylesheet version="1.0"
|
||||||
|
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://www.w3.org/2005/02/xpath-functions">
|
||||||
|
<xsl:output method="text" />
|
||||||
|
<xsl:preserve-space elements="*" />
|
||||||
|
<xsl:template match="/">
|
||||||
|
<xsl:for-each select="/nutrition/food">
|
||||||
|
<xsl:value-of select="name" />
|
||||||
|
</xsl:for-each>
|
||||||
|
</xsl:template>
|
||||||
|
</xsl:stylesheet>
|
@@ -1,28 +1,26 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2005-2008 Alfresco Software Limited.
|
* Copyright (C) 2005-2010 Alfresco Software Limited.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This file is part of Alfresco
|
||||||
* modify it under the terms of the GNU General Public License
|
*
|
||||||
* as published by the Free Software Foundation; either version 2
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
* of the License, or (at your option) any later version.
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* This program is distributed in the hope that it will be useful,
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
* You should have received a copy of the GNU 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.
|
|
||||||
* As a special exception to the terms and conditions of version 2.0 of
|
|
||||||
* the GPL, you may redistribute this Program in connection with Free/Libre
|
|
||||||
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
|
||||||
* FLOSS exception. You should have recieved a copy of the text describing
|
|
||||||
* the FLOSS exception, and it is also available here:
|
|
||||||
* http://www.alfresco.com/legal/licensing" */
|
|
||||||
package org.alfresco.util;
|
package org.alfresco.util;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
import javax.xml.parsers.DocumentBuilder;
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
import javax.xml.parsers.DocumentBuilderFactory;
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
@@ -40,6 +38,7 @@ import org.alfresco.service.cmr.repository.NodeRef;
|
|||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.w3c.dom.*;
|
import org.w3c.dom.*;
|
||||||
|
import org.xml.sax.InputSource;
|
||||||
import org.xml.sax.SAXException;
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -168,6 +167,22 @@ public class XMLUtil
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** utility function for parsing xml */
|
||||||
|
public static Document parse(final Reader source)
|
||||||
|
throws SAXException,
|
||||||
|
IOException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
final DocumentBuilder db = XMLUtil.getDocumentBuilder();
|
||||||
|
return db.parse(new InputSource(source));
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
source.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** provides a document builder that is namespace aware but not validating by default */
|
/** provides a document builder that is namespace aware but not validating by default */
|
||||||
public static DocumentBuilder getDocumentBuilder()
|
public static DocumentBuilder getDocumentBuilder()
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user