001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.xml.content; 029 030import org.opencms.file.CmsObject; 031import org.opencms.main.CmsLog; 032import org.opencms.main.OpenCms; 033import org.opencms.xml.I_CmsXmlDocument; 034import org.opencms.xml.types.I_CmsXmlContentValue; 035 036import java.util.ArrayList; 037import java.util.List; 038import java.util.Locale; 039 040import javax.xml.parsers.DocumentBuilder; 041import javax.xml.parsers.DocumentBuilderFactory; 042import javax.xml.parsers.ParserConfigurationException; 043 044import org.apache.commons.logging.Log; 045 046import org.dom4j.DocumentException; 047import org.dom4j.io.DOMReader; 048import org.dom4j.io.DOMWriter; 049import org.w3c.dom.Document; 050import org.w3c.dom.Element; 051import org.w3c.dom.Node; 052import org.w3c.dom.NodeList; 053 054/** 055 * Provides extension functions for use in XSLT version transformation files. 056 * 057 * <p>An instance of this class is meant to be used for only a single content conversion. 058 */ 059public class CmsXsltContext { 060 061 /** The logger instance for this class. */ 062 private static final Log LOG = CmsLog.getLog(CmsXsltContext.class); 063 064 /** The CMS context. */ 065 private CmsObject m_cms; 066 067 /** The document builder factory. */ 068 private DocumentBuilderFactory m_documentBuilderFactory; 069 070 /** The document builder. */ 071 private DocumentBuilder m_documentBuilder; 072 073 /** 074 * Creates a new instance. 075 * 076 * @param cms the CMS context 077 */ 078 public CmsXsltContext(CmsObject cms) { 079 080 m_cms = cms; 081 m_documentBuilderFactory = DocumentBuilderFactory.newInstance(); 082 try { 083 m_documentBuilder = m_documentBuilderFactory.newDocumentBuilder(); 084 } catch (ParserConfigurationException e) { 085 throw new RuntimeException(e); 086 } 087 } 088 089 /** 090 * XSLT extension function that converts the XML for a value between two different OpenCms content value types. 091 * 092 * @param value a node list that is expected to contain exactly one element, which should represent an XML content value 093 * @param sourceTypeName the original type of the value 094 * @param targetTypeName the type which the value should be converted to 095 * @param elementName the name that should be used 096 * 097 * @return a node list containing the converted XML value 098 */ 099 public NodeList convertType(NodeList value, String sourceTypeName, String targetTypeName, String elementName) { 100 101 if (value.getLength() != 1) { 102 throw new RuntimeException("convertType must be passed exactly one node."); 103 } 104 105 // this is somewhat convoluted - XSLT uses the org.w3c.dom classes, while OpenCms XML contents use dom4j DOM classes. 106 // We create a dom4j document containing only the relevant XML for the value, then interpret it as an OpenCms value 107 // on which we can call getStringValue(). Then we create a new value in the dom4j document for the new type, and set its 108 // string value to the previously read string value. In the end, we have to translate everything back to org.w3c.dom format again. 109 110 Document doc = m_documentBuilder.newDocument(); 111 Node copiedNode = doc.importNode(value.item(0), true); 112 Element w3cRoot = doc.createElement("root"); 113 doc.appendChild(w3cRoot); 114 w3cRoot.appendChild(copiedNode); 115 org.dom4j.io.DOMReader reader = new DOMReader(); 116 org.dom4j.Document dom4jDoc = reader.read(doc); 117 org.dom4j.Element dom4jRoot = dom4jDoc.getRootElement(); 118 org.dom4j.Element dom4jValue = dom4jRoot.elements().get(0); 119 final CmsDefaultXmlContentHandler handler = new CmsDefaultXmlContentHandler(); 120 121 // We need a dummy content with a content handler because creating a new value require a reference to a content, 122 // and the content handler is asked for a default value. This dummy content is probably unusable for anything else. 123 I_CmsXmlDocument dummyContent = new CmsXmlContent() { 124 125 public I_CmsXmlContentHandler getHandler() { 126 127 return handler; 128 } 129 130 }; 131 I_CmsXmlContentValue sourceType = (I_CmsXmlContentValue)OpenCms.getXmlContentTypeManager().getContentType( 132 sourceTypeName); 133 134 // We must use newInstance here so the element name is set 135 I_CmsXmlContentValue targetType = (I_CmsXmlContentValue)OpenCms.getXmlContentTypeManager().getContentType( 136 targetTypeName).newInstance(elementName, "0", "1"); 137 138 I_CmsXmlContentValue sourceValue = sourceType.createValue(dummyContent, dom4jValue, Locale.ENGLISH); 139 String valueString = sourceValue.getStringValue(m_cms); 140 141 // once we have the string value, we don't need the XML structure for the original value anymore - throw it away 142 dom4jValue.detach(); 143 144 org.dom4j.Element dom4jNewValue = targetType.generateXml(m_cms, dummyContent, dom4jRoot, Locale.ENGLISH); 145 I_CmsXmlContentValue newValue = targetType.createValue(dummyContent, dom4jNewValue, Locale.ENGLISH); 146 newValue.setStringValue(m_cms, valueString); 147 List<Node> result = new ArrayList<>(); 148 org.w3c.dom.Document newW3cDoc; 149 try { 150 newW3cDoc = new DOMWriter().write(dom4jDoc); 151 result.add(newW3cDoc.getDocumentElement().getFirstChild()); 152 } catch (DocumentException e) { 153 LOG.error(e.getLocalizedMessage(), e); 154 } 155 return new NodeList() { 156 157 public int getLength() { 158 159 return result.size(); 160 } 161 162 public Node item(int index) { 163 164 return result.get(index); 165 } 166 167 }; 168 } 169}