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.CmsFile; 031import org.opencms.file.CmsObject; 032import org.opencms.file.CmsResource; 033import org.opencms.file.CmsResourceFilter; 034import org.opencms.main.CmsLog; 035import org.opencms.main.CmsRuntimeException; 036import org.opencms.xml.CmsXmlContentDefinition; 037import org.opencms.xml.CmsXmlUtils; 038 039import java.io.ByteArrayInputStream; 040import java.util.ArrayList; 041import java.util.List; 042 043import javax.xml.parsers.DocumentBuilder; 044import javax.xml.parsers.DocumentBuilderFactory; 045import javax.xml.transform.ErrorListener; 046import javax.xml.transform.OutputKeys; 047import javax.xml.transform.Source; 048import javax.xml.transform.Transformer; 049import javax.xml.transform.TransformerException; 050import javax.xml.transform.TransformerFactory; 051import javax.xml.transform.dom.DOMResult; 052import javax.xml.transform.dom.DOMSource; 053import javax.xml.transform.sax.SAXSource; 054 055import org.apache.commons.logging.Log; 056import org.apache.xerces.parsers.SAXParser; 057 058import org.dom4j.Document; 059import org.xml.sax.ErrorHandler; 060import org.xml.sax.InputSource; 061import org.xml.sax.SAXException; 062import org.xml.sax.SAXParseException; 063 064/** 065 * Provides static methods for XML content version transformations. 066 */ 067public class CmsVersionTransformer { 068 069 /** Logger for this class. */ 070 private static final Log LOG = CmsLog.getLog(CmsVersionTransformer.class); 071 072 /** XSL parameter for passing a context object to version transformations. */ 073 public static final String XSL_PARAM_TRANSFORMATION_CONTEXT = "context"; 074 075 /** 076 * Converts an XML content document to the current version using the version transformation XSLT file which is configured in the schema. 077 * 078 * @param cms the current CMS context 079 * @param document the document to transform 080 * @param contentDefinition the content definition for which we are doing the conversion 081 * 082 * @return the converted document 083 */ 084 @SuppressWarnings("synthetic-access") 085 public static Document transformDocumentToCurrentVersion( 086 CmsObject cms, 087 Document document, 088 CmsXmlContentDefinition contentDefinition) { 089 090 String transformation = contentDefinition.getContentHandler().getVersionTransformation(); 091 if (transformation == null) { 092 LOG.warn( 093 "Schema version detected, but no version transformation defined for " 094 + contentDefinition.getSchemaLocation()); 095 return document; 096 } 097 098 try { 099 CmsResource xsltResource = cms.readResource(transformation, CmsResourceFilter.IGNORE_EXPIRATION); 100 CmsFile xsltFile = cms.readFile(xsltResource); 101 // we explicitly want an Xalan transformer factory here, even if we add some other XSLT implementation later, 102 // because we rely on specific Xalan features (the way extension functions work). 103 TransformerFactory transformerFactory = new org.apache.xalan.processor.TransformerFactoryImpl(); 104 List<Exception> errors = new ArrayList<>(); 105 transformerFactory.setErrorListener(new ErrorListener() { 106 107 public void error(TransformerException e) throws TransformerException { 108 109 errors.add(e); 110 throw e; 111 112 } 113 114 public void fatalError(TransformerException e) throws TransformerException { 115 116 errors.add(e); 117 throw e; 118 } 119 120 public void warning(TransformerException e) { 121 122 LOG.warn(e.getLocalizedMessage(), e); 123 } 124 }); 125 SAXSource transformationSource = new SAXSource( 126 new InputSource(new ByteArrayInputStream(xsltFile.getContents()))); 127 SAXParser parser = new SAXParser(); 128 parser.setErrorHandler(new ErrorHandler() { 129 130 public void error(SAXParseException e) throws SAXException { 131 132 errors.add(e); 133 throw e; 134 135 } 136 137 public void fatalError(SAXParseException e) throws SAXException { 138 139 errors.add(e); 140 throw e; 141 142 } 143 144 public void warning(SAXParseException e) { 145 146 LOG.warn(e.getLocalizedMessage(), e); 147 148 } 149 }); 150 transformationSource.setXMLReader(parser); 151 Transformer transformer = transformerFactory.newTransformer(transformationSource); 152 if (errors.size() > 0) { 153 throw errors.get(0); 154 } 155 transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); 156 157 Source source = new DOMSource(CmsXmlUtils.convertDocumentFromDom4jToW3C(document)); 158 DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); 159 DocumentBuilder builder = documentBuilderFactory.newDocumentBuilder(); 160 org.w3c.dom.Document targetDoc = builder.newDocument(); 161 DOMResult target = new DOMResult(targetDoc); 162 transformer.setParameter(XSL_PARAM_TRANSFORMATION_CONTEXT, new CmsXsltContext(cms)); 163 transformer.transform(source, target); 164 if (errors.size() > 0) { 165 throw errors.get(0); 166 } 167 Document result = CmsXmlUtils.convertDocumentFromW3CToDom4j(targetDoc); 168 result.getRootElement().addAttribute(CmsXmlContent.A_VERSION, "" + contentDefinition.getVersion()); 169 if (LOG.isDebugEnabled()) { 170 try { 171 LOG.debug( 172 "Used XSL transformation " 173 + transformation 174 + "\n----------------------------" 175 + "\nOriginal XML:" 176 + "\n----------------------------\n" 177 + CmsXmlUtils.marshal(document, "UTF-8") 178 + "\n----------------------------\nTransformed XML:" 179 + "\n----------------------------\n" 180 + CmsXmlUtils.marshal(result, "UTF-8")); 181 } catch (Exception e) { 182 LOG.error(e.getLocalizedMessage(), e); 183 } 184 } 185 return result; 186 } catch (Exception e) { 187 LOG.error(e.getLocalizedMessage(), e); 188 throw new CmsRuntimeException( 189 Messages.get().container(Messages.ERR_XMLCONTENT_VERSION_TRANSFORMATION_ERROR_1, transformation), 190 e); 191 } 192 } 193 194}