View Javadoc

1   /*
2    * Copyright Emmanouil Batsis
3    * 
4    * Licensed under the GNU General Public 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.gnu.org/licenses/gpl.txt
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   */
17  package com.geekologue.md4j.generators;
18  
19  import java.io.BufferedInputStream;
20  import java.io.BufferedOutputStream;
21  import java.io.File;
22  import java.io.FileInputStream;
23  import java.io.FileOutputStream;
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.io.OutputStream;
27  import java.util.HashMap;
28  import java.util.Iterator;
29  import java.util.Map;
30  import javax.xml.parsers.ParserConfigurationException;
31  import javax.xml.parsers.SAXParserFactory;
32  import javax.xml.transform.Source;
33  import javax.xml.transform.Transformer;
34  import javax.xml.transform.TransformerException;
35  import javax.xml.transform.TransformerFactory;
36  import javax.xml.transform.sax.SAXSource;
37  import javax.xml.transform.stream.StreamResult;
38  import org.apache.tools.ant.BuildException;
39  import org.apache.tools.ant.Project;
40  import org.apache.tools.ant.util.FileNameMapper;
41  import org.apache.tools.ant.util.JAXPUtils;
42  import org.xml.sax.InputSource;
43  import org.xml.sax.SAXException;
44  import org.xml.sax.XMLReader;
45  import com.geekologue.md4j.tools.ant.HibernateMappingProcessorLiason;
46  import com.geekologue.md4j.tools.ant.HibernateMappingProcessorTask;
47  
48  public abstract class AbstractXsltBasedGenerator implements
49          HibernateMappingProcessorLiason {
50      HibernateMappingProcessorTask task;
51  
52      private Transformer           transformer;
53  
54      private ResourceResolver      internalResolver;
55  
56      protected File                destDir         = null;
57  
58      private FileNameMapper        runtimeMapper;
59  
60      protected Map                 styleParameters = new HashMap();
61  
62      private String                xsl;
63  
64      private AbstractXsltBasedGenerator() {
65          super();
66      }
67  
68      /***
69       * @param stylesheet
70       *            the XSLT file path
71       */
72      protected AbstractXsltBasedGenerator(String stylesheet) {
73          this();
74          this.xsl = stylesheet;
75      }
76  
77      /***
78       * Implementations must return a simple structure mapper
79       * 
80       * @return the simple mapper
81       */
82      public abstract FileNameMapper getSimpleMapper();
83  
84      /***
85       * Implementations must return a nested structure mapper
86       * 
87       * @return the nested mapper
88       */
89      public abstract FileNameMapper getNestedMapper();
90  
91      /***
92       * @see com.geekologue.md4j.tools.ant.generators.HibernateMappingProcessorLiason#init(com.geekologue.md4j.tools.ant.HibernateMappingProcessorTask)
93       */
94      public void init(Map context) {
95          this.task = (HibernateMappingProcessorTask) context.get("task");
96          // maybe some subclass wants to override destination dir in it's own
97          // init()
98          if (this.destDir == null) {
99              this.destDir = task.getDestDir();
100         }
101         try {
102             if (this.task.getMapper().equalsIgnoreCase("simple")) {
103                 this.runtimeMapper = this.getSimpleMapper();
104             } else {
105                 this.runtimeMapper = this.getNestedMapper();
106             }
107             // initialize transformer etc
108             this.internalResolver = new ResourceResolver(this.task);
109             // Get a TransformerFactory object
110             TransformerFactory xformFactory = TransformerFactory.newInstance();
111             xformFactory.setURIResolver(this.internalResolver);
112             Source xslSource = this.internalResolver.resolve(this.xsl,
113                     HibernateMappingProcessorTask.MD4J_BASE_URI);
114             this.transformer = xformFactory.newTransformer(xslSource);
115             // is this needed since we configured the factory?
116             this.transformer.setURIResolver(this.internalResolver);
117             // set stylesheet parameters
118             for (Iterator iter = this.styleParameters.keySet().iterator(); iter
119                     .hasNext();) {
120                 String paramName = (String) iter.next();
121                 this.transformer.setParameter(paramName, this.styleParameters
122                         .get(paramName));
123             }
124             // add package param by default
125             this.styleParameters.put("package", context.get("package"));
126         } catch (TransformerException e1) {
127             this.task.logOrThrowError("Looks like tranformer trouble: ", e1);
128         }
129     }
130 
131     /***
132      * @see com.geekologue.md4j.tools.ant.generators.HibernateMappingProcessorLiason#process(java.io.File,
133      *      java.lang.String, java.io.File)
134      */
135     public void process(File contextDir, String xmlFile) throws BuildException {
136         File outFile = null;
137         File inFile = null;
138         try {
139             inFile = new File(contextDir, xmlFile);
140             // skip directories
141             if (inFile.isDirectory()) {
142                 this.task.log("Skipping " + inFile + " it is a directory.",
143                         Project.MSG_VERBOSE);
144                 return;
145             }
146             String[] outFileName = this.runtimeMapper.mapFileName(xmlFile);
147             // skip files that cannot be mapped
148             if (outFileName == null || outFileName.length == 0) {
149                 this.task.log("Skipping " + inFile
150                         + " it cannot get mapped to output.",
151                         Project.MSG_VERBOSE);
152                 return;
153             } else if (outFileName == null || outFileName.length > 1) {
154                 this.task.log("Skipping " + inFile
155                         + " its mapping is ambiguous.", Project.MSG_VERBOSE);
156                 return;
157             }
158             outFile = new File(this.destDir, outFileName[0]);
159             if (this.task.isForce()
160                     || inFile.lastModified() >= outFile.lastModified()) {
161                 ensureDirectoryFor(outFile);
162                 this.task.log("Processing " + inFile + " to " + outFile,
163                         Project.MSG_VERBOSE);
164                 process(inFile, outFile);
165             } else {
166                 this.task.log("Skipping input file " + inFile
167                         + " because it is older than output file " + outFile,
168                         Project.MSG_VERBOSE);
169             }
170         } catch (Exception ex) {
171             ex.printStackTrace();
172             this.task.log("Failed to process " + inFile, Project.MSG_ERR);
173             if (outFile != null) {
174                 outFile.delete();
175             }
176             throw new BuildException(ex);
177         }
178     }
179 
180     /***
181      * 
182      * @param infile
183      * @param outfile
184      * @throws Exception
185      */
186     private void process(File infile, File outfile) throws Exception {
187         InputStream fis = null;
188         OutputStream fos = null;
189         try {
190             fis = new BufferedInputStream(new FileInputStream(infile));
191             fos = new BufferedOutputStream(new FileOutputStream(outfile));
192             StreamResult res = new StreamResult(fos);
193             res.setSystemId(JAXPUtils.getSystemId(outfile));
194             Source src = getSource(fis, JAXPUtils.getSystemId(infile));
195             this.transformer.transform(src, res);
196         } finally {
197             // cleanup
198             try {
199                 if (fis != null) {
200                     fis.close();
201                 }
202             } catch (IOException ignored) {
203                 // ignore
204             }
205             try {
206                 if (fos != null) {
207                     fos.close();
208                 }
209             } catch (IOException ignored) {
210                 // ignore
211             }
212         }
213     }
214 
215     /***
216      * Get the source instance from the stream and id of the file.
217      * 
218      * @param is
219      *            the input file stream
220      * @param systemId
221      *            the systemid
222      * @return a properly configured source
223      */
224     private Source getSource(InputStream is, String systemId)
225             throws ParserConfigurationException, SAXException {
226         SAXParserFactory spFactory = SAXParserFactory.newInstance();
227         spFactory.setNamespaceAware(true);
228         XMLReader reader = spFactory.newSAXParser().getXMLReader();
229         reader.setEntityResolver(this.task.getXmlCatalog());
230         Source src = new SAXSource(reader, new InputSource(is));
231         src.setSystemId(systemId);
232         return src;
233     }
234 
235     /***
236      * Ensure the directory exists for the given file
237      * 
238      * @param targetFile
239      *            the file for which the directories are required.
240      * @exception BuildException
241      *                if the directories cannot be created.
242      */
243     private void ensureDirectoryFor(File targetFile) throws BuildException {
244         File directory = targetFile.getParentFile();
245         if (!directory.exists()) {
246             if (!directory.mkdirs()) {
247                 this.task.logOrThrowError("Unable to create directory: "
248                         + directory.getAbsolutePath());
249             }
250         }
251     }
252 }