View Javadoc
1   /*
2    * Copyright 2009 the original author or authors.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *     
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.openehealth.ipf.commons.xml;
17  
18  import lombok.Getter;
19  import lombok.Setter;
20  import org.openehealth.ipf.commons.core.modules.api.Transmogrifier;
21  import org.slf4j.Logger;
22  import org.slf4j.LoggerFactory;
23  
24  import javax.xml.transform.*;
25  import java.util.Map;
26  import java.util.Map.Entry;
27  import java.util.concurrent.ConcurrentHashMap;
28  import java.util.concurrent.ConcurrentMap;
29  
30  /**
31   * Xslt Processor transforming a {@link Source} into an object of type T. The stylesheet to be used is passed with the
32   * {@link #zap(Source, Object...)} call, however, the Xslt Template object is cached for subsequent transformations
33   * using this stylesheet.
34   * <p>
35   * Xslt parameters can be passed in as Map parameter.
36   * <p>
37   * Note: This class is thread-safe and reentrant.
38   * 
39   * @author Christian Ohr
40   */
41  public class XsltTransmogrifier<T> extends AbstractCachingXmlProcessor<Templates> implements Transmogrifier<Source, T> {
42      private static final Logger LOG = LoggerFactory.getLogger(XsltTransmogrifier.class);
43  
44      private static final ConcurrentMap<String, Loader<Templates>> XSLT_CACHE = new ConcurrentHashMap<>();
45  
46      @Getter @Setter private Map<String, Object> staticParams;
47      private final TransformerFactory factory;
48      private final URIResolver resolver;
49      private final Class<T> outputFormat;
50  
51      @SuppressWarnings("unchecked")
52      public XsltTransmogrifier() {
53          this((Class<T>) String.class, null, null);
54      }
55  
56      public XsltTransmogrifier(Class<T> outputFormat) {
57          this(outputFormat, null, null);
58      }
59  
60      public XsltTransmogrifier(Class<T> outputFormat, ClassLoader classLoader) {
61          this(outputFormat, classLoader, null);
62      }
63  
64      public XsltTransmogrifier(Class<T> outputFormat, Map<String, Object> staticParams) {
65          this(outputFormat, null, staticParams);
66      }
67  
68      public TransformerFactory getFactory() {
69          return factory;
70      }
71  
72      @Override
73      protected ConcurrentMap<String, Loader<Templates>> getCache() {
74          return XSLT_CACHE;
75      }
76  
77      /**
78       * @param outputFormat
79       *            currently supported: String, Writer, OutputStream
80       * @param classLoader
81       *            class loader for resource retrieval, may be <code>null</code>
82       * @param staticParams
83       *            static Xslt parameters
84       */
85      public XsltTransmogrifier(
86              Class<T> outputFormat,
87              ClassLoader classLoader,
88              Map<String, Object> staticParams)
89      {
90          super(classLoader);
91          factory = TransformerFactory.newInstance();
92          // Wrap the default resolver
93          resolver = new ClasspathUriResolver(factory.getURIResolver());
94          factory.setURIResolver(resolver);
95          this.outputFormat = outputFormat;
96          this.staticParams = staticParams;
97      }
98  
99      /**
100      * Converts a Source into a Result using a XSL processor.
101      * <p>
102      * The XSL stylesheet resource location is mandatory in the first extra parameter.
103      * XSL Parameters may be passed as a Map in the second parameter.
104      * 
105      * @see Transmogrifier#zap(java.lang.Object, java.lang.Object[])
106      */
107     @Override
108     public T zap(Source source, Object... params) {
109         ResultHolder<T> accessor = ResultHolderFactory.create(outputFormat);
110         if (accessor == null) throw new IllegalArgumentException("Format " + outputFormat.getClass() + " is not supported");
111         Result result = accessor.createResult();
112         doZap(source, result, params);
113         return accessor.getResult();
114     }
115 
116     private void doZap(Source source, Result result, Object... params) {
117         if (params.length == 0) {
118             throw new IllegalArgumentException("Expected XSL location in first parameter");
119         }
120         try {
121             Templates template = resource(params);
122             Transformer transformer = template.newTransformer();
123             transformer.setURIResolver(resolver);
124             setXsltParameters(transformer, staticParams);
125             setXsltParameters(transformer, resourceParameters(params));
126             transformer.transform(source, result);
127         } catch (Exception e) {
128             throw new RuntimeException("XSLT processing failed", e);
129         }
130 
131     }
132 
133     /**
134      * Sets the parameter to the {@link Transformer} object
135      */
136     protected void setXsltParameters(Transformer transformer, Map<String, Object> param) {
137         if (param == null) {
138             return;
139         }
140         for (Entry<String, Object> entry : param.entrySet()) {
141             LOG.debug("Add new parameter for transformer: {}", entry.getKey());
142             transformer.setParameter(entry.getKey(), entry.getValue());
143         }
144     }
145 
146     @Override
147     protected Templates createResource(Object... params) {
148         try {
149             return factory.newTemplates(resourceContent(params));
150         } catch (TransformerConfigurationException e) {
151             throw new IllegalArgumentException("Could not initialize XSLT template", e);
152         }
153     }
154 
155 }