View Javadoc
1   package org.andromda.maven.plugin.site;
2   
3   import java.io.ByteArrayInputStream;
4   import java.io.ByteArrayOutputStream;
5   import java.io.File;
6   import java.io.FileWriter;
7   import java.io.IOException;
8   import java.io.InputStream;
9   import java.net.URL;
10  import java.util.List;
11  import javax.xml.parsers.DocumentBuilder;
12  import javax.xml.parsers.DocumentBuilderFactory;
13  import javax.xml.parsers.ParserConfigurationException;
14  import javax.xml.transform.Result;
15  import javax.xml.transform.Source;
16  import javax.xml.transform.TransformerFactory;
17  import javax.xml.transform.dom.DOMSource;
18  import javax.xml.transform.stream.StreamResult;
19  import javax.xml.transform.stream.StreamSource;
20  import org.apache.commons.lang.StringUtils;
21  import org.dom4j.Element;
22  import org.dom4j.io.SAXReader;
23  import org.dom4j.io.XMLWriter;
24  import org.w3c.dom.Document;
25  import org.xml.sax.EntityResolver;
26  import org.xml.sax.InputSource;
27  import org.xml.sax.SAXException;
28  
29  /**
30   * Used to perform the transformation of XSL documents
31   * within the site plugin.
32   *
33   * @author Chad Brandon
34   * @author Vance Karimi
35   */
36  public class XslTransformer
37  {
38      private String projectName;
39  
40      /**
41       * Default constructor
42       *
43       */
44      public XslTransformer()
45      {
46          // Default constructor
47      }
48  
49      /**
50       * Constructor that sets the project name used to replace variable inside generated
51       * xdoc xml.
52       *
53       * @param projectNameIn
54       */
55      public XslTransformer(String projectNameIn)
56      {
57          this.projectName = projectNameIn;
58      }
59  
60      /**
61       * Applies the given XSLT files to the model in the order in which they are found.
62       *
63       * @param xmlDocument The full path of the original XML
64       * @param transformation The full path of the XSLT
65       * @param outputLocation The full path of the output xdoc XML
66       */
67      public void transform(
68          final String xmlDocument,
69          final String transformation,
70          final String outputLocation)
71      {
72          try
73          {
74              this.transform(xmlDocument, new File(transformation).toURI().toURL(), outputLocation);
75          }
76          catch (final Exception exception)
77          {
78              throw new RuntimeException(exception);
79          }
80      }
81  
82      /**
83       * Applies the given XSLT files to the model in the order in which they are found.
84       *
85       * @param xmlDocument The full path of the original XML
86       * @param xslt The URL of the XSLT
87       * @param outputLocation The full path of the output xdoc XML
88       */
89      public void transform(
90          final String xmlDocument,
91          final URL xslt,
92          final String outputLocation)
93      {
94          try
95          {
96              if (StringUtils.isNotBlank(xmlDocument))
97              {
98                  final Source xmlSource = new DOMSource(this.urlToDocument(xmlDocument));
99                  final TransformerFactory factory = TransformerFactory.newInstance();
100                 if (xslt != null)
101                 {
102                     final Source xsltSource = new StreamSource(xslt.openStream());
103                     final javax.xml.transform.Transformer transformer = factory.newTransformer(xsltSource);
104                     final ByteArrayOutputStream output = new ByteArrayOutputStream();
105                     final Result result = new StreamResult(output);
106                     transformer.transform(
107                         xmlSource,
108                         result);
109 
110                     final byte[] outputResult = output.toByteArray();
111                     final org.dom4j.Document document = replaceVariableProperties(outputResult);
112 
113                     if (StringUtils.isNotBlank(outputLocation))
114                     {
115                         final File fileOutput = new File(outputLocation);
116                         final File parent = fileOutput.getParentFile();
117                         if (parent != null)
118                         {
119                             parent.mkdirs();
120                         }
121 
122                         FileWriter fwriter = new FileWriter(fileOutput);
123                         XMLWriter writer = new XMLWriter(fwriter);
124                         writer.write(document);
125                         writer.flush();
126                         writer.close();
127                         fwriter.close();
128                     }
129                 }
130             }
131         }
132         catch (final Exception exception)
133         {
134             throw new RuntimeException(exception);
135         }
136     }
137 
138     /**
139      * Parses the XML retrieved from the String URL and returns a Document object.
140      * @param url the url of the XML to parse.
141      * @return Document newly created Document object.
142      * @throws ParserConfigurationException
143      * @throws IOException
144      * @throws SAXException
145      */
146     @SuppressWarnings("static-method")
147     private Document urlToDocument(String url)
148         throws Exception
149     {
150         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
151         DocumentBuilder builder = factory.newDocumentBuilder();
152         builder.setEntityResolver(new XslTransformerEntityResolver(url));
153         return builder.parse(new InputSource(url));
154     }
155 
156     /**
157      * The prefix that the systemId should start with when attempting
158      * to resolve it within a jar.
159      */
160     private static final String SYSTEM_ID_FILE = "file:";
161 
162     /**
163      * Provides the resolution of external entities.
164      */
165     private static final class XslTransformerEntityResolver
166         implements EntityResolver
167     {
168         private String xmlDocument;
169 
170         XslTransformerEntityResolver(final String xmlDocument)
171         {
172             this.xmlDocument = xmlDocument;
173         }
174 
175         /**
176          * @see org.xml.sax.EntityResolver#resolveEntity(String, String)
177          */
178         @SuppressWarnings("resource")
179         public InputSource resolveEntity(
180             final String publicId,
181             final String systemId)
182             throws SAXException, IOException
183         {
184             InputSource source = null;
185             String path = systemId;
186             if (path != null && path.startsWith(SYSTEM_ID_FILE))
187             {
188                 final String xmlResource = this.xmlDocument;
189                 path = path.replaceFirst(
190                         SYSTEM_ID_FILE,
191                         "");
192 
193                 // - remove any extra starting slashes
194                 path = path.replaceAll(
195                         "\\+",
196                         "/").replaceAll(
197                         "/+",
198                         "/");
199 
200                 // - if we still have one starting slash, remove it
201                 if (path.startsWith("/"))
202                 {
203                     path = path.substring(
204                             1,
205                             path.length());
206                 }
207                 final String xmlResourceName = xmlResource.replaceAll(
208                         ".*(\\+|/)",
209                         "");
210 
211                 InputStream inputStream = null;
212                 URL uri = new File(StringUtils.replace(
213                             xmlResource,
214                             xmlResourceName,
215                             path)).toURI().toURL();
216                 if (uri != null)
217                 {
218                     inputStream = uri.openStream();
219                 }
220                 if (inputStream != null)
221                 {
222                     source = new InputSource(inputStream);
223                     source.setPublicId(publicId);
224                     if (uri != null)
225                     {
226                         source.setSystemId(uri.toString());
227                     }
228                 }
229             }
230             return source;
231         }
232     }
233 
234     /**
235      * Replace the variable property defined by %module% in the output generated
236      * xdoc file.  Uses dom4j XPath to locate the variable.
237      *
238      * @param documentBuffer The byte array representing the xdoc XML
239      * @return the org.dom4j.Document object of the xdoc XML
240      * @throws Exception
241      */
242     private org.dom4j.Document replaceVariableProperties(byte[] documentBuffer)
243         throws Exception
244     {
245         SAXReader reader = new SAXReader();
246         org.dom4j.Document document = reader.read(new ByteArrayInputStream(documentBuffer));
247 
248         // List elements = document.selectNodes("//*[contains(text(),'%module%')]");
249         List<Element> elements = document.selectNodes("//*");
250         for (final Element element : elements)
251         {
252             if (StringUtils.contains(element.getText(), "%module%"))
253             {
254                 element.setText(
255                         StringUtils.replace(
256                                 element.getText(),
257                                 "%module%",
258                                 this.getProjectName()));
259             }
260         }
261         elements.clear();
262 
263         elements = document.selectNodes("//*[contains(@*,'%module%')]");
264         for (final Element element : elements)
265         {
266             element.addAttribute(
267                     "name",
268                     StringUtils.replace(
269                             element.attributeValue("name"),
270                             "%module%",
271                             this.getProjectName()));
272         }
273         return document;
274     }
275 
276     /**
277      * @return Returns the projectName.
278      */
279     public String getProjectName()
280     {
281         return projectName;
282     }
283 
284     /**
285      * @param projectName The projectName to set.
286      */
287     public void setProjectName(String projectName)
288     {
289         this.projectName = projectName;
290     }
291 }