%line | %branch | |||||||||
---|---|---|---|---|---|---|---|---|---|---|
org.apache.torque.task.TorqueDataModelTask$TorqueFileResourceLoader |
|
|
1 | package org.apache.torque.task; |
|
2 | ||
3 | /* |
|
4 | * Licensed to the Apache Software Foundation (ASF) under one |
|
5 | * or more contributor license agreements. See the NOTICE file |
|
6 | * distributed with this work for additional information |
|
7 | * regarding copyright ownership. The ASF licenses this file |
|
8 | * to you under the Apache License, Version 2.0 (the |
|
9 | * "License"); you may not use this file except in compliance |
|
10 | * with the License. You may obtain a copy of the License at |
|
11 | * |
|
12 | * http://www.apache.org/licenses/LICENSE-2.0 |
|
13 | * |
|
14 | * Unless required by applicable law or agreed to in writing, |
|
15 | * software distributed under the License is distributed on an |
|
16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
|
17 | * KIND, either express or implied. See the License for the |
|
18 | * specific language governing permissions and limitations |
|
19 | * under the License. |
|
20 | */ |
|
21 | ||
22 | import java.io.ByteArrayInputStream; |
|
23 | import java.io.ByteArrayOutputStream; |
|
24 | import java.io.File; |
|
25 | import java.io.IOException; |
|
26 | import java.io.InputStream; |
|
27 | import java.io.InputStreamReader; |
|
28 | import java.io.LineNumberReader; |
|
29 | import java.io.PrintStream; |
|
30 | import java.io.UnsupportedEncodingException; |
|
31 | import java.io.Writer; |
|
32 | import java.util.ArrayList; |
|
33 | import java.util.Date; |
|
34 | import java.util.Hashtable; |
|
35 | import java.util.Iterator; |
|
36 | import java.util.List; |
|
37 | import java.util.Map; |
|
38 | ||
39 | import org.apache.commons.lang.StringUtils; |
|
40 | import org.apache.texen.Generator; |
|
41 | import org.apache.texen.ant.TexenTask; |
|
42 | import org.apache.tools.ant.BuildException; |
|
43 | import org.apache.tools.ant.DirectoryScanner; |
|
44 | import org.apache.tools.ant.types.FileSet; |
|
45 | import org.apache.torque.engine.EngineException; |
|
46 | import org.apache.torque.engine.database.model.Database; |
|
47 | import org.apache.torque.engine.database.transform.XmlToAppData; |
|
48 | import org.apache.velocity.VelocityContext; |
|
49 | import org.apache.velocity.app.VelocityEngine; |
|
50 | import org.apache.velocity.context.Context; |
|
51 | import org.apache.velocity.exception.MethodInvocationException; |
|
52 | import org.apache.velocity.exception.ParseErrorException; |
|
53 | import org.apache.velocity.exception.ResourceNotFoundException; |
|
54 | import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader; |
|
55 | import org.apache.velocity.runtime.resource.loader.FileResourceLoader; |
|
56 | ||
57 | /** |
|
58 | * A base torque task that uses either a single XML schema |
|
59 | * representing a data model, or a <fileset> of XML schemas. |
|
60 | * We are making the assumption that an XML schema representing |
|
61 | * a data model contains tables for a <strong>single</strong> |
|
62 | * database. |
|
63 | * |
|
64 | * @author <a href="mailto:jvanzyl@zenplex.com">Jason van Zyl</a> |
|
65 | * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a> |
|
66 | */ |
|
67 | public class TorqueDataModelTask extends TexenTask |
|
68 | { |
|
69 | /** |
|
70 | * XML that describes the database model, this is transformed |
|
71 | * into the application model object. |
|
72 | */ |
|
73 | protected String xmlFile; |
|
74 | ||
75 | /** Fileset of XML schemas which represent our data models. */ |
|
76 | protected List filesets = new ArrayList(); |
|
77 | ||
78 | /** Data models that we collect. One from each XML schema file. */ |
|
79 | protected List dataModels = new ArrayList(); |
|
80 | ||
81 | /** Velocity context which exposes our objects in the templates. */ |
|
82 | protected Context context; |
|
83 | ||
84 | /** |
|
85 | * Map of data model name to database name. |
|
86 | * Should probably stick to the convention of them being the same but |
|
87 | * I know right now in a lot of cases they won't be. |
|
88 | */ |
|
89 | protected Hashtable dataModelDbMap; |
|
90 | ||
91 | /** |
|
92 | * Hashtable containing the names of all the databases |
|
93 | * in our collection of schemas. |
|
94 | */ |
|
95 | protected Hashtable databaseNames; |
|
96 | ||
97 | //!! This is probably a crappy idea having the sql file -> db map |
|
98 | // here. I can't remember why I put it here at the moment ... |
|
99 | // maybe I was going to map something else. It can probably |
|
100 | // move into the SQL task. |
|
101 | ||
102 | /** |
|
103 | * Name of the properties file that maps an SQL file |
|
104 | * to a particular database. |
|
105 | */ |
|
106 | protected String sqldbmap; |
|
107 | ||
108 | /** The target database(s) we are generating SQL for. */ |
|
109 | private String targetDatabase; |
|
110 | ||
111 | /** Target Java package to place the generated files in. */ |
|
112 | private String targetPackage; |
|
113 | ||
114 | ||
115 | /** |
|
116 | * Set the sqldbmap. |
|
117 | * |
|
118 | * @param sqldbmap th db map |
|
119 | */ |
|
120 | public void setSqlDbMap(String sqldbmap) |
|
121 | { |
|
122 | //!! Make all these references files not strings. |
|
123 | this.sqldbmap = getProject().resolveFile(sqldbmap).toString(); |
|
124 | } |
|
125 | ||
126 | /** |
|
127 | * Get the sqldbmap. |
|
128 | * |
|
129 | * @return String sqldbmap. |
|
130 | */ |
|
131 | public String getSqlDbMap() |
|
132 | { |
|
133 | return sqldbmap; |
|
134 | } |
|
135 | ||
136 | /** |
|
137 | * Return the data models that have been processed. |
|
138 | * |
|
139 | * @return List data models |
|
140 | */ |
|
141 | public List getDataModels() |
|
142 | { |
|
143 | return dataModels; |
|
144 | } |
|
145 | ||
146 | /** |
|
147 | * Return the data model to database name map. |
|
148 | * |
|
149 | * @return Hashtable data model name to database name map. |
|
150 | */ |
|
151 | public Hashtable getDataModelDbMap() |
|
152 | { |
|
153 | return dataModelDbMap; |
|
154 | } |
|
155 | ||
156 | /** |
|
157 | * Get the xml schema describing the application model. |
|
158 | * |
|
159 | * @return String xml schema file. |
|
160 | */ |
|
161 | public String getXmlFile() |
|
162 | { |
|
163 | return xmlFile; |
|
164 | } |
|
165 | ||
166 | /** |
|
167 | * Set the xml schema describing the application model. |
|
168 | * |
|
169 | * @param xmlFile The new XmlFile value |
|
170 | */ |
|
171 | public void setXmlFile(String xmlFile) |
|
172 | { |
|
173 | this.xmlFile = getProject().resolveFile(xmlFile).toString(); |
|
174 | } |
|
175 | ||
176 | /** |
|
177 | * Adds a set of xml schema files (nested fileset attribute). |
|
178 | * |
|
179 | * @param set a Set of xml schema files |
|
180 | */ |
|
181 | public void addFileset(FileSet set) |
|
182 | { |
|
183 | filesets.add(set); |
|
184 | } |
|
185 | ||
186 | /** |
|
187 | * Get the current target database. |
|
188 | * |
|
189 | * @return String target database(s) |
|
190 | */ |
|
191 | public String getTargetDatabase() |
|
192 | { |
|
193 | return targetDatabase; |
|
194 | } |
|
195 | ||
196 | /** |
|
197 | * Set the current target database. (e.g. mysql, oracle, ..) |
|
198 | * |
|
199 | * @param v target database(s) |
|
200 | */ |
|
201 | public void setTargetDatabase(String v) |
|
202 | { |
|
203 | targetDatabase = v; |
|
204 | } |
|
205 | ||
206 | /** |
|
207 | * Get the current target package. |
|
208 | * |
|
209 | * @return return target java package. |
|
210 | */ |
|
211 | public String getTargetPackage() |
|
212 | { |
|
213 | return targetPackage; |
|
214 | } |
|
215 | ||
216 | /** |
|
217 | * Set the current target package. This is where generated java classes will |
|
218 | * live. |
|
219 | * |
|
220 | * @param v target java package. |
|
221 | */ |
|
222 | public void setTargetPackage(String v) |
|
223 | { |
|
224 | targetPackage = v; |
|
225 | } |
|
226 | ||
227 | /** |
|
228 | * Set up the initial context for generating the SQL from the XML schema. |
|
229 | * |
|
230 | * @return the context |
|
231 | * @throws Exception |
|
232 | */ |
|
233 | public Context initControlContext() throws Exception |
|
234 | { |
|
235 | XmlToAppData xmlParser; |
|
236 | ||
237 | if (xmlFile == null && filesets.isEmpty()) |
|
238 | { |
|
239 | throw new BuildException("You must specify an XML schema or " |
|
240 | + "fileset of XML schemas!"); |
|
241 | } |
|
242 | ||
243 | try |
|
244 | { |
|
245 | if (xmlFile != null) |
|
246 | { |
|
247 | // Transform the XML database schema into |
|
248 | // data model object. |
|
249 | xmlParser = new XmlToAppData(getTargetDatabase(), |
|
250 | getTargetPackage()); |
|
251 | Database ad = xmlParser.parseFile(xmlFile); |
|
252 | ad.setFileName(grokName(xmlFile)); |
|
253 | dataModels.add(ad); |
|
254 | } |
|
255 | else |
|
256 | { |
|
257 | // Deal with the filesets. |
|
258 | for (int i = 0; i < filesets.size(); i++) |
|
259 | { |
|
260 | FileSet fs = (FileSet) filesets.get(i); |
|
261 | DirectoryScanner ds = fs.getDirectoryScanner(getProject()); |
|
262 | File srcDir = fs.getDir(getProject()); |
|
263 | ||
264 | String[] dataModelFiles = ds.getIncludedFiles(); |
|
265 | ||
266 | // Make a transaction for each file |
|
267 | for (int j = 0; j < dataModelFiles.length; j++) |
|
268 | { |
|
269 | File f = new File(srcDir, dataModelFiles[j]); |
|
270 | xmlParser = new XmlToAppData(getTargetDatabase(), |
|
271 | getTargetPackage()); |
|
272 | Database ad = xmlParser.parseFile(f.toString()); |
|
273 | ad.setFileName(grokName(f.toString())); |
|
274 | dataModels.add(ad); |
|
275 | } |
|
276 | } |
|
277 | } |
|
278 | ||
279 | Iterator i = dataModels.iterator(); |
|
280 | databaseNames = new Hashtable(); |
|
281 | dataModelDbMap = new Hashtable(); |
|
282 | ||
283 | // Different datamodels may state the same database |
|
284 | // names, we just want the unique names of databases. |
|
285 | while (i.hasNext()) |
|
286 | { |
|
287 | Database database = (Database) i.next(); |
|
288 | databaseNames.put(database.getName(), database.getName()); |
|
289 | dataModelDbMap.put(database.getFileName(), database.getName()); |
|
290 | } |
|
291 | } |
|
292 | catch (EngineException ee) |
|
293 | { |
|
294 | throw new BuildException(ee); |
|
295 | } |
|
296 | ||
297 | context = new VelocityContext(); |
|
298 | ||
299 | // Place our set of data models into the context along |
|
300 | // with the names of the databases as a convenience for now. |
|
301 | context.put("dataModels", dataModels); |
|
302 | context.put("databaseNames", databaseNames); |
|
303 | context.put("targetDatabase", targetDatabase); |
|
304 | context.put("targetPackage", targetPackage); |
|
305 | ||
306 | return context; |
|
307 | } |
|
308 | ||
309 | /** |
|
310 | * Change type of "now" to java.util.Date |
|
311 | * |
|
312 | * @see org.apache.texen.ant.TexenTask#populateInitialContext(org.apache.velocity.context.Context) |
|
313 | */ |
|
314 | protected void populateInitialContext(Context context) throws Exception |
|
315 | { |
|
316 | super.populateInitialContext(context); |
|
317 | context.put("now", new Date()); |
|
318 | } |
|
319 | ||
320 | /** |
|
321 | * Gets a name to use for the application's data model. |
|
322 | * |
|
323 | * @param xmlFile The path to the XML file housing the data model. |
|
324 | * @return The name to use for the <code>AppData</code>. |
|
325 | */ |
|
326 | private String grokName(String xmlFile) |
|
327 | { |
|
328 | // This can't be set from the file name as it is an unreliable |
|
329 | // method of naming the descriptor. Not everyone uses the same |
|
330 | // method as I do in the TDK. jvz. |
|
331 | ||
332 | String name = "data-model"; |
|
333 | int i = xmlFile.lastIndexOf(System.getProperty("file.separator")); |
|
334 | if (i != -1) |
|
335 | { |
|
336 | // Creep forward to the start of the file name. |
|
337 | i++; |
|
338 | ||
339 | int j = xmlFile.lastIndexOf('.'); |
|
340 | if (i < j) |
|
341 | { |
|
342 | name = xmlFile.substring(i, j); |
|
343 | } |
|
344 | else |
|
345 | { |
|
346 | // Weirdo |
|
347 | name = xmlFile.substring(i); |
|
348 | } |
|
349 | } |
|
350 | return name; |
|
351 | } |
|
352 | ||
353 | /** |
|
354 | * Override Texen's context properties to map the |
|
355 | * torque.xxx properties (including defaults set by the |
|
356 | * org/apache/torque/defaults.properties) to just xxx. |
|
357 | * |
|
358 | * <p> |
|
359 | * Also, move xxx.yyy properties to xxxYyy as Velocity |
|
360 | * doesn't like the xxx.yyy syntax. |
|
361 | * </p> |
|
362 | * |
|
363 | * @param file the file to read the properties from |
|
364 | */ |
|
365 | public void setContextProperties(String file) |
|
366 | { |
|
367 | super.setContextProperties(file); |
|
368 | ||
369 | // Map the torque.xxx elements from the env to the contextProperties |
|
370 | Hashtable env = super.getProject().getProperties(); |
|
371 | for (Iterator i = env.entrySet().iterator(); i.hasNext();) |
|
372 | { |
|
373 | Map.Entry entry = (Map.Entry) i.next(); |
|
374 | String key = (String) entry.getKey(); |
|
375 | if (key.startsWith("torque.")) |
|
376 | { |
|
377 | String newKey = key.substring("torque.".length()); |
|
378 | int j = newKey.indexOf("."); |
|
379 | while (j != -1) |
|
380 | { |
|
381 | newKey = |
|
382 | newKey.substring(0, j) |
|
383 | + StringUtils.capitalize(newKey.substring(j + 1)); |
|
384 | j = newKey.indexOf("."); |
|
385 | } |
|
386 | ||
387 | contextProperties.setProperty(newKey, entry.getValue()); |
|
388 | } |
|
389 | } |
|
390 | } |
|
391 | /** |
|
392 | * This message fragment (telling users to consult the log or |
|
393 | * invoke ant with the -debug flag) is appended to rethrown |
|
394 | * exception messages. |
|
395 | */ |
|
396 | private final static String ERR_MSG_FRAGMENT = |
|
397 | ". For more information consult the velocity log, or invoke ant " + |
|
398 | "with the -debug flag."; |
|
399 | ||
400 | /** |
|
401 | * This method creates an VelocityEngine instance, parses |
|
402 | * every template and creates the corresponding output. |
|
403 | * |
|
404 | * Unfortunately the TextenTask.execute() method makes |
|
405 | * everything for us but we just want to set our own |
|
406 | * VelocityTemplateLoader. |
|
407 | * TODO: change once TEXEN-14 is resolved and out. |
|
408 | * |
|
409 | * @see org.apache.texen.ant.TexenTask#execute() |
|
410 | */ |
|
411 | public void execute() throws BuildException |
|
412 | { |
|
413 | // Make sure the template path is set. |
|
414 | if (templatePath == null && useClasspath == false) |
|
415 | { |
|
416 | throw new BuildException( |
|
417 | "The template path needs to be defined if you are not using " |
|
418 | + "the classpath for locating templates!"); |
|
419 | } |
|
420 | ||
421 | // Make sure the control template is set. |
|
422 | if (controlTemplate == null) |
|
423 | { |
|
424 | throw new BuildException( |
|
425 | "The control template needs to be defined!"); |
|
426 | } |
|
427 | ||
428 | // Make sure the output directory is set. |
|
429 | if (outputDirectory == null) |
|
430 | { |
|
431 | throw new BuildException( |
|
432 | "The output directory needs to be defined!"); |
|
433 | } |
|
434 | ||
435 | // Make sure there is an output file. |
|
436 | if (outputFile == null) |
|
437 | { |
|
438 | throw new BuildException("The output file needs to be defined!"); |
|
439 | } |
|
440 | ||
441 | VelocityEngine ve = new VelocityEngine(); |
|
442 | ||
443 | try |
|
444 | { |
|
445 | // Setup the Velocity Runtime. |
|
446 | if (templatePath != null) |
|
447 | { |
|
448 | log("Using templatePath: " + templatePath, project.MSG_VERBOSE); |
|
449 | ve.setProperty("torque" + VelocityEngine.FILE_RESOURCE_LOADER_PATH, |
|
450 | templatePath); |
|
451 | ||
452 | // TR: We need our own FileResourceLoader |
|
453 | ve.addProperty(VelocityEngine.RESOURCE_LOADER, "torquefile"); |
|
454 | ve.setProperty("torquefile." + VelocityEngine.RESOURCE_LOADER |
|
455 | + ".instance", |
|
456 | new TorqueFileResourceLoader(this)); |
|
457 | } |
|
458 | ||
459 | if (useClasspath) |
|
460 | { |
|
461 | log("Using classpath"); |
|
462 | // TR: We need our own ClasspathResourceLoader |
|
463 | ve.addProperty(VelocityEngine.RESOURCE_LOADER, "classpath"); |
|
464 | ||
465 | ve.setProperty("classpath." + VelocityEngine.RESOURCE_LOADER |
|
466 | + ".instance", new TorqueClasspathResourceLoader(this)); |
|
467 | ||
468 | ve.setProperty("classpath." + VelocityEngine.RESOURCE_LOADER |
|
469 | + ".cache", "false"); |
|
470 | ||
471 | ve.setProperty("classpath." + VelocityEngine.RESOURCE_LOADER |
|
472 | + ".modificationCheckInterval", "2"); |
|
473 | } |
|
474 | ||
475 | ve.init(); |
|
476 | ||
477 | // Create the text generator. |
|
478 | Generator generator = Generator.getInstance(); |
|
479 | generator.setVelocityEngine(ve); |
|
480 | generator.setOutputPath(outputDirectory); |
|
481 | generator.setInputEncoding(inputEncoding); |
|
482 | generator.setOutputEncoding(outputEncoding); |
|
483 | ||
484 | if (templatePath != null) |
|
485 | { |
|
486 | generator.setTemplatePath(templatePath); |
|
487 | } |
|
488 | ||
489 | // Make sure the output directory exists, if it doesn't |
|
490 | // then create it. |
|
491 | File file = new File(outputDirectory); |
|
492 | if (!file.exists()) |
|
493 | { |
|
494 | file.mkdirs(); |
|
495 | } |
|
496 | ||
497 | String path = outputDirectory + File.separator + outputFile; |
|
498 | log("Generating to file " + path, project.MSG_INFO); |
|
499 | Writer writer = generator.getWriter(path, outputEncoding); |
|
500 | ||
501 | // The generator and the output path should |
|
502 | // be placed in the init context here and |
|
503 | // not in the generator class itself. |
|
504 | Context c = initControlContext(); |
|
505 | ||
506 | // Everything in the generator class should be |
|
507 | // pulled out and placed in here. What the generator |
|
508 | // class does can probably be added to the Velocity |
|
509 | // class and the generator class can probably |
|
510 | // be removed all together. |
|
511 | populateInitialContext(c); |
|
512 | ||
513 | // Feed all the options into the initial |
|
514 | // control context so they are available |
|
515 | // in the control/worker templates. |
|
516 | if (contextProperties != null) |
|
517 | { |
|
518 | Iterator i = contextProperties.getKeys(); |
|
519 | ||
520 | while (i.hasNext()) |
|
521 | { |
|
522 | String property = (String) i.next(); |
|
523 | String value = contextProperties.getString(property); |
|
524 | ||
525 | // Now lets quickly check to see if what |
|
526 | // we have is numeric and try to put it |
|
527 | // into the context as an Integer. |
|
528 | try |
|
529 | { |
|
530 | c.put(property, new Integer(value)); |
|
531 | } catch (NumberFormatException nfe) |
|
532 | { |
|
533 | // Now we will try to place the value into |
|
534 | // the context as a boolean value if it |
|
535 | // maps to a valid boolean value. |
|
536 | String booleanString = contextProperties |
|
537 | .testBoolean(value); |
|
538 | ||
539 | if (booleanString != null) |
|
540 | { |
|
541 | c.put(property, new Boolean(booleanString)); |
|
542 | } else |
|
543 | { |
|
544 | // We are going to do something special |
|
545 | // for properties that have a "file.contents" |
|
546 | // suffix: for these properties will pull |
|
547 | // in the contents of the file and make |
|
548 | // them available in the context. So for |
|
549 | // a line like the following in a properties file: |
|
550 | // |
|
551 | // license.file.contents = license.txt |
|
552 | // |
|
553 | // We will pull in the contents of license.txt |
|
554 | // and make it available in the context as |
|
555 | // $license. This should make texen a little |
|
556 | // more flexible. |
|
557 | if (property.endsWith("file.contents")) |
|
558 | { |
|
559 | // We need to turn the license file from |
|
560 | // relative to |
|
561 | // absolute, and let Ant help :) |
|
562 | value = org.apache.velocity.util.StringUtils |
|
563 | .fileContentsToString(project |
|
564 | .resolveFile(value) |
|
565 | .getCanonicalPath()); |
|
566 | ||
567 | property = property.substring(0, property |
|
568 | .indexOf("file.contents") - 1); |
|
569 | } |
|
570 | ||
571 | c.put(property, value); |
|
572 | } |
|
573 | } |
|
574 | } |
|
575 | } |
|
576 | ||
577 | writer.write(generator.parse(controlTemplate, c)); |
|
578 | writer.flush(); |
|
579 | writer.close(); |
|
580 | generator.shutdown(); |
|
581 | cleanup(); |
|
582 | } |
|
583 | catch (BuildException e) |
|
584 | { |
|
585 | throw e; |
|
586 | } |
|
587 | catch (MethodInvocationException e) |
|
588 | { |
|
589 | throw new BuildException("Exception thrown by '" |
|
590 | + e.getReferenceName() + "." + e.getMethodName() + "'" |
|
591 | + ERR_MSG_FRAGMENT, e.getWrappedThrowable()); |
|
592 | } |
|
593 | catch (ParseErrorException e) |
|
594 | { |
|
595 | throw new BuildException( |
|
596 | "Velocity syntax error" + ERR_MSG_FRAGMENT, e); |
|
597 | } |
|
598 | catch (ResourceNotFoundException e) |
|
599 | { |
|
600 | throw new BuildException( |
|
601 | "Resource not found" + ERR_MSG_FRAGMENT, |
|
602 | e); |
|
603 | } |
|
604 | catch (Exception e) |
|
605 | { |
|
606 | throw new BuildException( |
|
607 | "Generation failed" + ERR_MSG_FRAGMENT, |
|
608 | e); |
|
609 | } |
|
610 | } |
|
611 | ||
612 | /** |
|
613 | * This method filters the template and replaces some |
|
614 | * unwanted characters. For example it removes leading |
|
615 | * spaces in front of velocity commands and replaces |
|
616 | * tabs with spaces to prevent bounces in different |
|
617 | * code editors with different tab-width-setting. |
|
618 | * |
|
619 | * @param resource the input stream to filter |
|
620 | * |
|
621 | * @return the filtered input stream. |
|
622 | * |
|
623 | * @throws IOException if creating, reading or writing to a stream fails. |
|
624 | */ |
|
625 | protected InputStream filter(InputStream resource) throws IOException |
|
626 | { |
|
627 | InputStreamReader streamReader; |
|
628 | if (inputEncoding != null) |
|
629 | { |
|
630 | streamReader = new InputStreamReader(resource, inputEncoding); |
|
631 | } |
|
632 | else |
|
633 | { |
|
634 | streamReader = new InputStreamReader(resource); |
|
635 | } |
|
636 | LineNumberReader lineNumberReader = new LineNumberReader(streamReader); |
|
637 | String line = null; |
|
638 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
|
639 | PrintStream ps = null; |
|
640 | if (inputEncoding != null) |
|
641 | { |
|
642 | ps = new PrintStream(baos, true, inputEncoding); |
|
643 | } |
|
644 | else |
|
645 | { |
|
646 | ps = new PrintStream(baos, true); |
|
647 | } |
|
648 | ||
649 | while ((line = lineNumberReader.readLine()) != null) |
|
650 | { |
|
651 | // remove leading spaces in front of velocity commands and comments |
|
652 | line = line.replaceAll("^\\s*#", "#"); |
|
653 | // replace tabs with spaces to prevent bounces in editors |
|
654 | line = line.replaceAll("\t", " "); |
|
655 | ps.println(line); |
|
656 | } |
|
657 | ps.flush(); |
|
658 | ps.close(); |
|
659 | ||
660 | return new ByteArrayInputStream(baos.toByteArray()); |
|
661 | } |
|
662 | ||
663 | ||
664 | /** |
|
665 | * A custom classpath resource loader which filters tabs and removes spaces |
|
666 | * from lines with velocity commands. |
|
667 | */ |
|
668 | public static class TorqueClasspathResourceLoader |
|
669 | extends ClasspathResourceLoader |
|
670 | { |
|
671 | /** |
|
672 | * The task in which this resource loader is used. |
|
673 | */ |
|
674 | private TorqueDataModelTask task; |
|
675 | ||
676 | /** |
|
677 | * Constructor. |
|
678 | * |
|
679 | * @param task the task in which this resource loader is used. |
|
680 | */ |
|
681 | public TorqueClasspathResourceLoader(TorqueDataModelTask task) |
|
682 | { |
|
683 | this.task = task; |
|
684 | } |
|
685 | ||
686 | /** |
|
687 | * @see org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader#getResourceStream(java.lang.String) |
|
688 | */ |
|
689 | public synchronized InputStream getResourceStream(String name) |
|
690 | throws ResourceNotFoundException |
|
691 | { |
|
692 | InputStream source = null; |
|
693 | try |
|
694 | { |
|
695 | source = super.getResourceStream(name); |
|
696 | return task.filter(source); |
|
697 | } |
|
698 | catch (IOException uee) |
|
699 | { |
|
700 | task.log(uee.getMessage()); |
|
701 | throw new ResourceNotFoundException(uee.getMessage()); |
|
702 | } |
|
703 | finally |
|
704 | { |
|
705 | if (source != null) |
|
706 | { |
|
707 | try |
|
708 | { |
|
709 | source.close(); |
|
710 | } |
|
711 | catch (IOException e) |
|
712 | { |
|
713 | task.log(e.getMessage()); |
|
714 | } |
|
715 | } |
|
716 | } |
|
717 | } |
|
718 | } |
|
719 | ||
720 | /** |
|
721 | * A custom file resource loader which filters tabs and removes spaces |
|
722 | * from lines with velocity commands. |
|
723 | */ |
|
724 | public static class TorqueFileResourceLoader |
|
725 | extends FileResourceLoader |
|
726 | { |
|
727 | /** |
|
728 | * The task in which this resource loader is used. |
|
729 | */ |
|
730 | private TorqueDataModelTask task; |
|
731 | ||
732 | /** |
|
733 | * Constructor. |
|
734 | * |
|
735 | * @param task the task in which this resource loader is used. |
|
736 | */ |
|
737 | public TorqueFileResourceLoader(TorqueDataModelTask task) |
|
738 | 0 | { |
739 | 0 | this.task = task; |
740 | 0 | } |
741 | ||
742 | /** |
|
743 | * @see org.apache.velocity.runtime.resource.loader.FileResourceLoader#getResourceStream(java.lang.String) |
|
744 | */ |
|
745 | public synchronized InputStream getResourceStream( |
|
746 | String name) |
|
747 | throws ResourceNotFoundException |
|
748 | { |
|
749 | 0 | InputStream source = null; |
750 | try |
|
751 | { |
|
752 | 0 | source = super.getResourceStream(name); |
753 | 0 | return task.filter(source); |
754 | } |
|
755 | 0 | catch (IOException uee) |
756 | { |
|
757 | 0 | task.log(uee.getMessage()); |
758 | 0 | throw new ResourceNotFoundException(uee.getMessage()); |
759 | } |
|
760 | finally |
|
761 | { |
|
762 | 0 | if (source != null) |
763 | { |
|
764 | try |
|
765 | { |
|
766 | 0 | source.close(); |
767 | } |
|
768 | 0 | catch (IOException e) |
769 | { |
|
770 | 0 | task.log(e.getMessage()); |
771 | 0 | } |
772 | } |
|
773 | } |
|
774 | } |
|
775 | } |
|
776 | } |
|
777 | ||
778 |
This report is generated by jcoverage, Maven and Maven JCoverage Plugin. |