View Javadoc

1   package com.imcode.ant.tasks;
2   
3   import com.imcode.util.LineReader;
4   import org.apache.tools.ant.BuildException;
5   import org.apache.tools.ant.DirectoryScanner;
6   import org.apache.tools.ant.Project;
7   import org.apache.tools.ant.Task;
8   import org.apache.tools.ant.types.FileSet;
9   import org.apache.tools.ant.types.FilterSet;
10  import org.apache.tools.ant.types.FilterSetCollection;
11  import org.apache.tools.ant.util.FileUtils;
12  import org.apache.commons.lang.StringUtils;
13  
14  import java.io.*;
15  import java.util.*;
16  import java.nio.charset.Charset;
17  
18  /*** @author kreiger */
19  public class Translate extends Task {
20  
21      private File toDir;
22  
23      private String startToken;
24      private String endToken;
25  
26      private File bundle;
27      private String bundleLanguage;
28      private List fileSets = new ArrayList();
29  
30      private int verbosity = Project.MSG_VERBOSE;
31  
32      private boolean filtering;
33      private List filterSets = new ArrayList();
34  
35      private FileUtils fileUtils = FileUtils.newFileUtils();
36      private Properties properties = new Properties();
37      private long propertiesFileLastModified;
38      private File propertiesFile;
39      private String encoding;
40  
41      public void addFileSet(FileSet fileSet) {
42          fileSets.add(fileSet);
43      }
44  
45      public void setToDir(File toDir) {
46          this.toDir = toDir;
47      }
48  
49      public void setBundle(File bundle) {
50          this.bundle = bundle;
51      }
52  
53      public void setBundleLanguage(String bundleLanguage) {
54          this.bundleLanguage = bundleLanguage;
55      }
56  
57      public void setPropertiesFile(File propertiesFile) {
58          this.propertiesFile = propertiesFile;
59      }
60  
61      public void setStartToken(String startToken) {
62          this.startToken = startToken;
63      }
64  
65      public void setEndToken(String endToken) {
66          this.endToken = endToken;
67      }
68  
69      public void setFiltering(boolean filtering) {
70          this.filtering = filtering;
71      }
72  
73      public void setEncoding(String encoding) {
74          this.encoding = encoding;
75      }
76  
77      /*** Create a nested filterset */
78      public FilterSet createFilterSet() {
79          FilterSet filterSet = new FilterSet();
80          filterSets.add(filterSet);
81          return filterSet;
82      }
83  
84      /*** Used to force listing of all names of copied files. */
85      public void setVerbose(boolean verbose) {
86          if ( verbose ) {
87              verbosity = Project.MSG_INFO;
88          } else {
89              verbosity = Project.MSG_VERBOSE;
90          }
91      }
92  
93      void setProperties(Properties properties) {
94          this.properties = properties;
95      }
96  
97      public void execute() {
98          validateAttributes();
99          loadBundle();
100         FilterSetCollection filters = getFilterSetCollection();
101         translateFiles(filters);
102     }
103 
104     private void translateFiles(FilterSetCollection filters) {
105         for ( Iterator it = fileSets.iterator(); it.hasNext(); ) {
106             FileSet fileSet = (FileSet) it.next();
107             DirectoryScanner directoryScanner = fileSet.getDirectoryScanner(getProject());
108             String[] sourceFiles = directoryScanner.getIncludedFiles();
109             log("Translating " + sourceFiles.length + ( sourceFiles.length == 1
110                                                         ? " file to "
111                                                         : " files to " ) + toDir, verbosity);
112             for ( int i = 0; i < sourceFiles.length; i++ ) {
113                 String sourcePath = sourceFiles[i];
114                 File destFile = fileUtils.resolveFile(toDir, sourcePath);
115                 File destDir = destFile.getParentFile();
116                 if ( !destDir.exists() ) {
117                     if ( !destDir.mkdirs() ) {
118                         throw new BuildException("Failed to create directory " + destDir);
119                     }
120                 }
121                 File sourceFile = fileUtils.resolveFile(directoryScanner.getBasedir(), sourcePath);
122                 long destFileLastModified = destFile.lastModified();
123                 boolean needsWork = destFileLastModified < sourceFile.lastModified()
124                                     || destFileLastModified < propertiesFileLastModified;
125                 if ( needsWork ) {
126                     log("Translating file " + sourceFile + " to file " + destFile, verbosity);
127                     translateFile(sourceFile, destFile, filters);
128                 } else {
129                     log("Omitting translation of " + sourceFile + " as " + destFile + " is up-to-date.", verbosity);
130                 }
131             }
132         }
133     }
134 
135     private void translateFile(File sourceFile, File destFile, FilterSetCollection filters) {
136         try {
137             InputStream sourceStream = new FileInputStream(sourceFile);
138             FileOutputStream destStream = new FileOutputStream(destFile);
139             translateStream(sourceStream, destStream, filters);
140         } catch ( IOException e ) {
141             destFile.delete();
142             throw new BuildException(e);
143         } catch ( PropertiesNotFoundException e ) {
144             destFile.delete();
145             String[] missingPropertyKeys = e.getPropertyKeys();
146             String missingPropertyKeysString = StringUtils.join(missingPropertyKeys, ", ");
147             String message = "\n" + sourceFile + ":0: " + ( missingPropertyKeys.length > 1 ? "Keys " : "Key " )
148                              + missingPropertyKeysString + " not found in " + propertiesFile;
149             throw new BuildException(message);
150         }
151     }
152 
153     void translateStream(InputStream sourceStream, OutputStream destStream,
154                          FilterSetCollection filters) throws IOException, PropertiesNotFoundException {
155         InputStreamReader inputStreamReader;
156         OutputStreamWriter outputStreamWriter;
157         if ( null != encoding ) {
158             inputStreamReader = new InputStreamReader(sourceStream, encoding);
159             outputStreamWriter = new OutputStreamWriter(destStream, encoding);
160         } else {
161             inputStreamReader = new InputStreamReader(sourceStream);
162             outputStreamWriter = new OutputStreamWriter(destStream);
163         }
164         LineReader lineReader = new LineReader(new BufferedReader(inputStreamReader));
165         BufferedWriter destWriter = new BufferedWriter(outputStreamWriter);
166         List propertiesNotFound = new ArrayList();
167         for ( String line; null != ( line = lineReader.readLine() ); ) {
168             try {
169                 String translatedLine = translateLine(line);
170                 String translatedAndFilteredLine = filters.replaceTokens(translatedLine);
171                 destWriter.write(translatedAndFilteredLine);
172             } catch ( PropertiesNotFoundException e ) {
173                 propertiesNotFound.addAll(Arrays.asList(e.getPropertyKeys()));
174             }
175         }
176         destWriter.flush();
177         destWriter.close();
178         if ( !propertiesNotFound.isEmpty() ) {
179             throw new PropertiesNotFoundException((String[]) propertiesNotFound.toArray(new String[propertiesNotFound.size()]));
180         }
181     }
182 
183     private String translateLine(String line) throws PropertiesNotFoundException {
184         String translatedLine = line;
185         List propertiesNotFound = new ArrayList();
186         int startTokenIndex = 0;
187         while ( -1 != ( startTokenIndex = translatedLine.indexOf(startToken, startTokenIndex) ) ) {
188             int endTokenIndex = translatedLine.indexOf(endToken, startTokenIndex + startToken.length());
189             if ( -1 != endTokenIndex ) {
190                 String bundleKey = translatedLine.substring(startTokenIndex + startToken.length(), endTokenIndex);
191                 String bundleValue = properties.getProperty(bundleKey);
192                 if ( null == bundleValue ) {
193                     bundleValue = bundleKey;
194                     propertiesNotFound.add(bundleKey);
195                 } else {
196                     log("Replacing key " + bundleKey + " with value " + bundleValue, Project.MSG_DEBUG);
197                     translatedLine = translatedLine.substring(0, startTokenIndex) + bundleValue
198                                      + translatedLine.substring(endTokenIndex + endToken.length());
199                 }
200                 startTokenIndex += bundleValue.length();
201             } else {
202                 break;
203             }
204         }
205         if ( !propertiesNotFound.isEmpty() ) {
206             throw new PropertiesNotFoundException((String[]) propertiesNotFound.toArray(new String[propertiesNotFound.size()]));
207         }
208         return translatedLine;
209     }
210 
211     private void loadBundle() {
212         if ( null == propertiesFile ) {
213             propertiesFile = new File(bundle.getPath() + '_' + bundleLanguage + ".properties");
214         }
215         propertiesFileLastModified = propertiesFile.lastModified();
216         try {
217             properties.load(new FileInputStream(propertiesFile));
218         } catch ( IOException e ) {
219             throw new BuildException(e.getMessage(), e);
220         }
221     }
222 
223     private void validateAttributes() {
224         if ( fileSets.size() == 0 ) {
225             throw new BuildException("Specify at least one fileset.");
226         }
227 
228         if ( null == toDir ) {
229             throw new BuildException("The todir attribute must be set.");
230         }
231 
232         if ( null == bundle == ( null == propertiesFile ) ) {
233             throw new BuildException("One of the bundle or the propertiesFile attributes must be set, but not both.");
234         }
235 
236         if ( null == bundleLanguage ) {
237             bundleLanguage = Locale.getDefault().getLanguage();
238         }
239 
240     }
241 
242     /*** Handle the filtersets. * */
243     private FilterSetCollection getFilterSetCollection() {
244         // handle filters
245         FilterSetCollection executionFilters = new FilterSetCollection();
246         if ( filtering ) {
247             executionFilters.addFilterSet(getProject().getGlobalFilterSet());
248         }
249         for ( Iterator filterEnum = filterSets.iterator(); filterEnum.hasNext(); ) {
250             executionFilters.addFilterSet((FilterSet) filterEnum.next());
251         }
252         return executionFilters;
253     }
254 
255     private static class PropertiesNotFoundException extends Exception {
256 
257         private String[] propertyKeys;
258 
259         private PropertiesNotFoundException(String[] propertyKeys) {
260             this.propertyKeys = propertyKeys;
261         }
262 
263         public String[] getPropertyKeys() {
264             return propertyKeys;
265         }
266 
267     }
268 }