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>
|
||||
</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 -->
|
||||
|
||||
|
@@ -31,6 +31,15 @@
|
||||
</property>
|
||||
</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
|
||||
as a global template helper with the TemplateService -->
|
||||
<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
|
||||
{
|
||||
//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 org.alfresco.model.ContentModel;
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.cmr.repository.ContentReader;
|
||||
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.cmr.repository.StoreRef;
|
||||
import org.alfresco.util.ApplicationContextHelper;
|
||||
|
||||
import freemarker.cache.TemplateLoader;
|
||||
|
||||
@@ -85,8 +83,14 @@ public class ClassPathRepoTemplateLoader implements TemplateLoader
|
||||
}
|
||||
else
|
||||
{
|
||||
URL url = this.getClass().getClassLoader().getResource(name);
|
||||
return url == null ? null : new ClassPathTemplateSource(url, encoding);
|
||||
//Fix a common problem: classpath resource paths should not start with "/"
|
||||
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
|
||||
*/
|
||||
abstract class BaseTemplateSource
|
||||
abstract class BaseTemplateSource implements TemplateSource
|
||||
{
|
||||
public abstract Reader getReader(String encoding) throws IOException;
|
||||
|
||||
public abstract void close() throws IOException;
|
||||
|
||||
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 InputStream inputStream;
|
||||
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.encoding = encoding;
|
||||
}
|
||||
@@ -198,6 +213,32 @@ public class ClassPathRepoTemplateLoader implements TemplateLoader
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/* (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
|
||||
* 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,
|
||||
* 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 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;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
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.LogFactory;
|
||||
import org.w3c.dom.*;
|
||||
import org.xml.sax.InputSource;
|
||||
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 */
|
||||
public static DocumentBuilder getDocumentBuilder()
|
||||
{
|
||||
|
Reference in New Issue
Block a user