KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > uitags > build > SimpleJsFileSizeReducer


1 /**
2  * Nov 20, 2004
3  *
4  * Copyright 2004 uitags
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */

18 package net.sf.uitags.build;
19
20 import java.io.BufferedReader JavaDoc;
21 import java.io.File JavaDoc;
22 import java.io.FileFilter JavaDoc;
23 import java.io.FileInputStream JavaDoc;
24 import java.io.FileNotFoundException JavaDoc;
25 import java.io.FileOutputStream JavaDoc;
26 import java.io.IOException JavaDoc;
27 import java.io.InputStreamReader JavaDoc;
28 import java.io.PrintStream JavaDoc;
29 import java.util.ArrayList JavaDoc;
30 import java.util.HashSet JavaDoc;
31 import java.util.List JavaDoc;
32 import java.util.Set JavaDoc;
33
34 import org.apache.maven.plugin.AbstractMojo;
35 import org.apache.maven.plugin.MojoFailureException;
36
37 /**
38  * @goal reduceSize
39  * @description Reduces the size of the JS source files.
40  * @author hgani
41  * @author jonni
42  * @version $Id: SimpleJsFileSizeReducer.java,v 1.8 2006/08/16 12:06:22 jonni Exp $
43  */

44 public final class SimpleJsFileSizeReducer extends AbstractMojo {
45   //////////////////////////////////////
46
////////// Maven parameters //////////
47
//////////////////////////////////////
48

49   /**
50    * The directory containing the JavaScript source files.
51    * @parameter
52    * @required
53    */

54   private String JavaDoc inputDir;
55   /**
56    * The directory to contained process JavaScript files.
57    * @parameter
58    * @required
59    */

60   private String JavaDoc outputDir;
61
62
63
64   ////////////////////////////
65
////////// Fields //////////
66
////////////////////////////
67

68   private File JavaDoc jsInputDirectory;
69   private File JavaDoc jsOutputDirectory;
70   private JsFileFilter fileFilter;
71   private JsDirectoryFilter directoryFilter;
72
73
74
75   //////////////////////////////////
76
////////// Constructors //////////
77
//////////////////////////////////
78

79   /**
80    * Processes Maven parameters and save the results to instance variables.
81    */

82   private void init() throws MojoFailureException {
83     this.jsInputDirectory = getDirectoryHandle(this.inputDir, false);
84     this.jsOutputDirectory = getDirectoryHandle(this.outputDir, true);
85
86     this.fileFilter = new JsFileFilter();
87     this.directoryFilter = new JsDirectoryFilter();
88   }
89
90   private File JavaDoc getDirectoryHandle(String JavaDoc dirPath, boolean createIfNotExists)
91       throws MojoFailureException {
92     File JavaDoc dir = new File JavaDoc(dirPath);
93     if (!dir.exists() && createIfNotExists) {
94       dir.mkdirs();
95     }
96
97     if (!dir.isDirectory()) {
98       getLog().error("Invalid directory name: " + dirPath);
99       throw new MojoFailureException("Invalid directory name: " + dirPath);
100     }
101     return dir;
102   }
103
104
105
106   ////////////////////////////////////
107
////////// Action Methods //////////
108
////////////////////////////////////
109

110   private File JavaDoc[] getJsFilePaths(File JavaDoc baseDir) {
111     File JavaDoc[] jsFiles = baseDir.listFiles(this.fileFilter);
112     List JavaDoc allJsFiles = new ArrayList JavaDoc();
113     addFilesToList(jsFiles, allJsFiles);
114
115     File JavaDoc[] subDirs = baseDir.listFiles(this.directoryFilter);
116     for (int i = 0; i < subDirs.length; ++i) {
117       jsFiles = getJsFilePaths(subDirs[i]);
118       addFilesToList(jsFiles, allJsFiles);
119     }
120     return (File JavaDoc[]) allJsFiles.toArray(new File JavaDoc[allJsFiles.size()]);
121   }
122
123   private void addFilesToList(File JavaDoc[] files, List JavaDoc list) {
124     for (int i = 0; i < files.length; ++i) {
125       list.add(files[i]);
126     }
127   }
128
129   private File JavaDoc[] getAllSourceJsFilePaths() {
130     return getJsFilePaths(this.jsInputDirectory);
131   }
132
133   private File JavaDoc[] getAllTargetJsFilePaths() {
134     return getJsFilePaths(this.jsOutputDirectory);
135   }
136
137   private String JavaDoc stripFileContent(File JavaDoc jsFile) throws IOException JavaDoc {
138     BufferedReader JavaDoc reader = new BufferedReader JavaDoc(
139         new InputStreamReader JavaDoc(new FileInputStream JavaDoc(jsFile)));
140
141     String JavaDoc line;
142     CommentStripper stripper = new CommentStripper();
143     StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
144     while ((line = reader.readLine()) != null) {
145       line = stripComments(line, stripper) + "\n";
146       line = stripEverythingIfOnlyContainSpaces(line);
147       buffer.append(line);
148     }
149     return buffer.toString();
150   }
151
152   private void performFileSizeReduction(File JavaDoc jsFile) {
153     try {
154       String JavaDoc content = stripFileContent(jsFile);
155       getOutputStreamFromInputFile(jsFile).print(content);
156     }
157     catch (IOException JavaDoc e) {
158       throw new RuntimeException JavaDoc(e);
159     }
160   }
161
162   private PrintStream JavaDoc getOutputStreamFromInputFile(File JavaDoc inputFile)
163       throws FileNotFoundException JavaDoc {
164     String JavaDoc relativePath = getPathRelativeToInputDirectory(inputFile);
165     String JavaDoc outputPath = this.jsOutputDirectory.getAbsolutePath() +
166         "/" + relativePath;
167     ensureDirectoryExists(new File JavaDoc(outputPath).getParentFile());
168     return new PrintStream JavaDoc(new FileOutputStream JavaDoc(outputPath));
169   }
170
171   private void ensureDirectoryExists(File JavaDoc directory) {
172     if (!directory.exists()) {
173       directory.mkdir();
174     }
175     else if (!directory.isDirectory()) {
176       throw new IllegalArgumentException JavaDoc(
177           directory.getName() + " is not a directory.");
178     }
179   }
180
181   private String JavaDoc stripComments(String JavaDoc line, CommentStripper stripper) {
182     stripper.startLine();
183     for (int i = 0; i < line.length(); ++i) {
184       if (!stripper.processCharacter(line.charAt(i))) {
185         return stripper.endLine();
186       }
187     }
188     return stripper.endLine();
189   }
190
191   private String JavaDoc stripEverythingIfOnlyContainSpaces(String JavaDoc line) {
192     return line.replaceFirst("^[ \t\n]*$", "");
193   }
194
195   private String JavaDoc getPathRelativeToInputDirectory(File JavaDoc file) {
196     String JavaDoc baseName = StringUtils.escapePattern(
197         this.jsInputDirectory.getPath());
198     String JavaDoc path = file.getPath().replaceFirst(baseName, "");
199     return path.replaceFirst("^/*", "");
200   }
201
202   public void execute() throws MojoFailureException {
203     init();
204
205     File JavaDoc[] targetJsFiles = getAllTargetJsFilePaths();
206     getLog().info("Deleting files in target directory:");
207     for (int i = 0; i < targetJsFiles.length; ++i) {
208       getLog().info("Deleting " + targetJsFiles[i].getAbsolutePath());
209       targetJsFiles[i].delete();
210     }
211
212     File JavaDoc[] sourceJsFiles = getAllSourceJsFilePaths();
213     System.out.println("Reducing the size of source javascript files:");
214     for (int i = 0; i < sourceJsFiles.length; ++i) {
215       getLog().info("Stripping " + sourceJsFiles[i].getAbsolutePath());
216       performFileSizeReduction(sourceJsFiles[i]);
217     }
218   }
219
220
221
222   ////////////////////////////////////
223
////////// Static Methods //////////
224
////////////////////////////////////
225

226   public static void main(String JavaDoc args[]) throws MojoFailureException {
227     if (args.length != 2) {
228       printUsage();
229     }
230
231     SimpleJsFileSizeReducer reducer = new SimpleJsFileSizeReducer();
232     reducer.inputDir = args[0];
233     reducer.outputDir = args[1];
234     reducer.execute();
235   }
236
237   private static void printUsage() {
238     System.err.println("Usage: PROGRAM INPUT_BASE_DIR OUTPUT_BASE_DIR");
239     System.exit(1);
240   }
241
242
243
244   ///////////////////////////////////
245
////////// Inner Classes //////////
246
///////////////////////////////////
247

248   private static class JsFileFilter implements FileFilter JavaDoc {
249     public boolean accept(File JavaDoc file) {
250       if (file.isFile() && file.getPath().endsWith(".js")) {
251         return true;
252       }
253       return false;
254     }
255   }
256
257   private static class JsDirectoryFilter implements FileFilter JavaDoc {
258     private static Set JavaDoc blackList = new HashSet JavaDoc();
259
260     static {
261       blackList.add("CVS");
262       blackList.add("excludes");
263     }
264
265     public boolean accept(File JavaDoc file) {
266       if (file.isDirectory() &&
267           !blackList.contains(file.getName())) {
268         return true;
269       }
270       return false;
271     }
272   }
273
274   private static class CommentStripper {
275     private StringBuffer JavaDoc buffer;
276     private boolean insideSingleQuote;
277     private boolean insideDoubleQuote;
278     private boolean slashEncountered;
279     private boolean asteriskEncountered;
280     private boolean insideMultiLinesComment;
281
282     private CommentStripper() {
283       this.insideMultiLinesComment = false;
284       this.asteriskEncountered = false;
285     }
286
287     private void startLine() {
288       this.buffer = new StringBuffer JavaDoc();
289       this.insideSingleQuote = false;
290       this.insideDoubleQuote = false;
291       this.slashEncountered = false;
292 // this.asteriskEncountered = false;
293
}
294
295     /**
296      * @param currentChar currently processed char
297      * @return true if the processing can continue, false if there is
298      * no need to process the rest of the line
299      */

300     private boolean processCharacter(char currentChar) {
301       if (shouldIgnoreMultiLinesCommentContent(currentChar)) {
302         return true;
303       }
304
305       switch (currentChar) {
306         case '\'' :
307           toggleSingleQuote();
308           break;
309         case '"' :
310           toggleDoubleQuote();
311           break;
312         case '/' :
313           if (checkIfSingleLineCommentStarted()) {
314             return false;
315           }
316           if (this.insideMultiLinesComment &&
317               checkIfMultiLinesCommentEnded()) {
318             return true;
319           }
320           break;
321         case '*' :
322           if (this.insideMultiLinesComment) {
323             this.asteriskEncountered = true;
324           }
325           else {
326             this.insideMultiLinesComment = checkIfMultiLinesCommentStarted();
327           }
328           break;
329         default :
330           makeUpForAnyIgnoredSlash();
331           dismissAnyPosibility();
332       }
333       storeOnlyIfConfirmed(currentChar);
334
335       return true;
336     }
337
338     private boolean shouldIgnoreMultiLinesCommentContent(char currentChar) {
339       return this.insideMultiLinesComment &&
340           !hasPosibilityToEndMultiLinesComment(currentChar);
341     }
342
343     private boolean hasPosibilityToEndMultiLinesComment(char currentChar) {
344       return this.asteriskEncountered || currentChar == '*';
345     }
346
347     private void toggleSingleQuote() {
348       this.insideSingleQuote = !this.insideSingleQuote;
349     }
350
351     private void toggleDoubleQuote() {
352       this.insideDoubleQuote = !this.insideDoubleQuote;
353     }
354
355     private boolean insideQuote() {
356       return this.insideSingleQuote || this.insideDoubleQuote;
357     }
358
359     private boolean checkIfSingleLineCommentStarted() {
360       if (!insideQuote()) {
361         if (this.slashEncountered) {
362           this.slashEncountered = false;
363           return true;
364         }
365         this.slashEncountered = true;
366       }
367       return false;
368     }
369
370     private boolean checkIfMultiLinesCommentStarted() {
371       if (!insideQuote()) {
372         if (this.slashEncountered) {
373           this.slashEncountered = false;
374           return true;
375         }
376       }
377       return false;
378     }
379
380     private boolean checkIfMultiLinesCommentEnded() {
381       if (this.asteriskEncountered) {
382         this.insideMultiLinesComment = false;
383         this.asteriskEncountered = false;
384         this.slashEncountered = false;
385         return true;
386       }
387       return false;
388     }
389
390     private void dismissAnyPosibility() {
391       this.slashEncountered = false;
392       this.asteriskEncountered = false;
393     }
394
395     private void makeUpForAnyIgnoredSlash() {
396       if (this.slashEncountered) {
397         storeIfNotInsideComment('/');
398       }
399     }
400
401     private void storeOnlyIfConfirmed(char currentChar) {
402       if (!this.slashEncountered) {
403         storeIfNotInsideComment(currentChar);
404       }
405     }
406
407     private void storeIfNotInsideComment(char currentChar) {
408       if (!this.insideMultiLinesComment) {
409         this.buffer.append(currentChar);
410       }
411     }
412
413     private String JavaDoc endLine() {
414       makeUpForAnyIgnoredSlash();
415       return this.buffer.toString();
416     }
417   }
418 }
419
Popular Tags