Project

General

Profile

1
package eu.dnetlib.miscutils.functional.xml;
2

    
3
import java.io.*;
4
import java.util.Map;
5
import java.util.function.Function;
6
import javax.xml.transform.*;
7
import javax.xml.transform.stream.StreamResult;
8
import javax.xml.transform.stream.StreamSource;
9

    
10
import org.apache.commons.logging.Log;
11
import org.apache.commons.logging.LogFactory;
12
import org.springframework.core.io.ClassPathResource;
13
import org.springframework.core.io.Resource;
14

    
15

    
16

    
17
/**
18
 * This function applies a stylesheet to something which can be transformed to a javax.xml.Source.
19
 * 
20
 * <p>Subclasses are specialized and know how to transform K into a Source</p>
21
 * 
22
 * @author marko
23
 *
24
 * @param <K>
25
 */
26
public abstract class AbstractApplyXslt<K> implements Function<K, String > {
27
	private static final String UNKNOWN_XSLT_NAME = "unknown xslt name";
28

    
29
	private static final Log log = LogFactory.getLog(AbstractApplyXslt.class); // NOPMD by marko on 11/24/08 5:02 PM
30

    
31
	private Transformer transformer;
32

    
33
	private TransformerFactory transformerFactory;
34
	
35
	/**
36
	 * optional, useful to keep track of xslt name for debugging purposes.
37
	 */
38
	private String xsltName;
39

    
40
	public AbstractApplyXslt(final Resource xslt, final TransformerFactory transformerFactory) {
41
		this(xslt, null, transformerFactory);
42
	}
43

    
44
	public AbstractApplyXslt(final Resource xslt, final Map<String, String> parameters, final TransformerFactory transformerFactory) {
45
		this(new StreamSource(getInputStream(xslt)), getFileName((ClassPathResource) xslt), parameters, transformerFactory);
46
	}
47
	
48
	public AbstractApplyXslt(final String xslt, final TransformerFactory transformerFactory) {
49
		this(xslt, UNKNOWN_XSLT_NAME, transformerFactory);
50
	}
51

    
52
	public AbstractApplyXslt(final String xslt, final String name, final TransformerFactory transformerFactory) {
53
		this(xslt, name, null, transformerFactory);
54
	}
55

    
56
	public AbstractApplyXslt(final String xslt, final String name, final Map<String, String> parameters, final TransformerFactory transformerFactory) {
57
		this(new StreamSource(new StringReader(xslt)), name, parameters, transformerFactory);
58
	}
59

    
60
	public AbstractApplyXslt(final Source xslt, final TransformerFactory transformerFactory) {
61
		this(xslt, UNKNOWN_XSLT_NAME, transformerFactory);
62
	}
63

    
64
	public AbstractApplyXslt(final Source xslt, final String name, final TransformerFactory transformerFactory) {
65
		this(xslt, name, null, transformerFactory);
66
	}
67

    
68
	/**
69
	 * Base method for all the others.
70
	 * @param xslt stylesheet as Source
71
	 * @param name name of the stylesheet
72
	 * @param parameters map of parameters to pass to the xslt
73
	 * @param transformerFactory factory to create Transformer instance. Must be a Saxon transformer Factory.
74
	 */
75
	public AbstractApplyXslt(final Source xslt, String name, Map<String, String> parameters, TransformerFactory transformerFactory) {
76
		this.transformerFactory = transformerFactory;
77
		try {
78
			this.xsltName = name;
79
			transformer = getTransformerFactory().newTransformer(xslt);
80
			if (! UNKNOWN_XSLT_NAME.equals(name))
81
				transformer.setURIResolver(new ExternalResourceURIResolver(name.replaceFirst("[^/]+$", "")));
82
			if(parameters != null)
83
				for(Map.Entry<String, String> parameter : parameters.entrySet())
84
					transformer.setParameter(parameter.getKey(), parameter.getValue());
85
			
86
		} catch (final Throwable e) {
87
			log.error("Problems with transformer!\n" + xslt + "--" + name, e);
88
			log.error(xsltDump(xslt));
89
			throw new IllegalStateException(e);
90
		}
91
	}
92
	
93
	/**
94
	 * Used only to convert checked to unchecked exception.
95
	 *
96
	 * @param xslt
97
	 * @return
98
	 */
99
	private static InputStream getInputStream(final Resource xslt) {
100
		try {
101
			return xslt.getInputStream();
102
		} catch (final IOException e) {
103
			throw new IllegalArgumentException(e);
104
		}
105
	}
106

    
107
	private static String getFileName(ClassPathResource xslt) {
108
		return xslt.getPath();
109
	}
110

    
111
	private String xsltDump(Source xslt) {
112
		Transformer transformer;
113
		try {
114
			transformer = getTransformerFactory().newTransformer();
115
			transformer.setOutputProperty(OutputKeys.INDENT, "yes");
116
			StringWriter outWriter = new StringWriter();
117
			Result result = new StreamResult(outWriter);
118
			transformer.transform(xslt, result);
119
			StringBuffer sb = outWriter.getBuffer();
120
			return sb.toString();
121
		} catch (TransformerConfigurationException e) {
122
			// TODO Auto-generated catch block
123
			e.printStackTrace();
124
		} catch (TransformerException e) {
125
			// TODO Auto-generated catch block
126
			e.printStackTrace();
127
		}
128
		return "error dumping the xslt";
129
	}
130

    
131
	@Override
132
	public String apply(final K input) {
133
		synchronized (transformer) {
134
			try {
135
				final StringWriter output = new StringWriter();
136
				transformer.transform(toStream(input), new StreamResult(output));
137
				return output.toString();
138
			} catch (final TransformerException e) {
139
				log.error("cannot transform record", e);
140
				log.error(input.toString());
141
				return "";
142
			}
143
		}
144
	}
145

    
146
	public abstract Source toStream(K input);
147

    
148
	public abstract String toString(K input);
149
	
150
	public TransformerFactory getTransformerFactory() {
151
		return transformerFactory;
152
	}
153

    
154
	public void setTransformerFactory(final TransformerFactory transformerFactory) {
155
		this.transformerFactory = transformerFactory;
156
	}
157

    
158
	public String getXsltName() {
159
		return xsltName;
160
	}
161

    
162
	public void setXsltName(String xsltName) {
163
		this.xsltName = xsltName;
164
	}
165

    
166
	/**
167
	 * This class contains the login enabling imports of peer external resources for the current xslt (<xsl:import>)
168
	 * The method resolve() first looks for the resource within the file system; if this fails, it looks in the classpath.
169
	 *
170
	 * @author Andrea Mannocci
171
	 */
172
	private class ExternalResourceURIResolver implements URIResolver {
173

    
174
		private final String xsltBasePath;
175

    
176
		public ExternalResourceURIResolver(String xsltBasePath) {
177
			this.xsltBasePath = xsltBasePath;
178
		}
179

    
180
		@Override
181
		public Source resolve(String href, String base) throws TransformerException {
182
			String externalResource = this.xsltBasePath + href;
183
			try {
184
				log.debug("trying to load external resource from file system: " + externalResource);
185
				return new StreamSource(new File(externalResource));
186
			} catch (final Throwable e) {
187
				log.debug("trying to load external resource from file system: " + externalResource);
188
				return new StreamSource(getInputStream(new ClassPathResource(externalResource)));
189
			}
190
		}
191
	}
192

    
193
}
(1-1/7)