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:
Brian Remmington
2010-03-09 14:02:55 +00:00
parent 7073ab6f88
commit 91ba631a3b
16 changed files with 2838 additions and 171 deletions

View File

@@ -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 -->

View File

@@ -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">

View File

@@ -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.

View File

@@ -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 = "&quot;";
break; // "
// case '&': enc = "&amp;"; break; //&
// case '<': enc = "&lt;"; break; //<
// case '>': enc = "&gt;"; break; //>
// german umlauts
case '\u00E4':
enc = "&auml;";
break;
case '\u00C4':
enc = "&Auml;";
break;
case '\u00F6':
enc = "&ouml;";
break;
case '\u00D6':
enc = "&Ouml;";
break;
case '\u00FC':
enc = "&uuml;";
break;
case '\u00DC':
enc = "&Uuml;";
break;
case '\u00DF':
enc = "&szlig;";
break;
// misc
// case 0x80: enc = "&euro;"; break; sometimes euro symbol is ascii 128, should we suport it?
case '\u20AC':
enc = "&euro;";
break;
case '\u00AB':
enc = "&laquo;";
break;
case '\u00BB':
enc = "&raquo;";
break;
case '\u00A0':
enc = "&nbsp;";
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;
}
}

View File

@@ -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>";
}

View File

@@ -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;
}
}

View File

@@ -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>";
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}

View 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);
}

View 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);
}
}

View File

@@ -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());
}
}
}

View 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>";
}

View 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();
}
}

View 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>

View File

@@ -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()
{