1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2  * JFlex Anttask                                                           *
3  * Copyright (C) 2001       Rafal Mantiuk <Rafal.Mantiuk@bellstream.pl>    *
4  * Copyright (C) 2003       changes by Gerwin Klein <lsf@jflex.de>         *
5  * All rights reserved.                                                    *
6  *                                                                         *
7  * License: BSD                                                            *
8  *                                                                         *
9  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
10 
11 package jflex.anttask;
12 
13 import java.io.File;
14 import java.io.IOException;
15 import java.io.LineNumberReader;
16 import java.io.Reader;
17 import java.nio.charset.StandardCharsets;
18 import java.nio.file.Files;
19 import java.util.regex.Matcher;
20 import java.util.regex.Pattern;
21 import jflex.core.OptionUtils;
22 import jflex.exceptions.GeneratorException;
23 import jflex.generator.LexGenerator;
24 import jflex.option.Options;
25 import org.apache.tools.ant.BuildException;
26 import org.apache.tools.ant.Task;
27 
28 /**
29  * JFlex ant task.
30  *
31  * @author Rafal Mantiuk
32  * @version JFlex 1.8.1
33  */
34 public class JFlexTask extends Task {
35   private static final Pattern PACKAGE_PATTERN = Pattern.compile("package\\s+(\\S+)\\s*;");
36   private static final Pattern CLASS_PATTERN = Pattern.compile("%class\\s+(\\S+)");
37 
38   private File inputFile;
39 
40   // found out by looking into .flex file
41   private String className = null;
42   private String packageName = null;
43 
44   /** for javac-like dest dir behaviour */
45   private File destinationDir;
46 
47   /** the actual output directory (outputDir = destinationDir + package)) */
48   private File outputDir = null;
49 
50   /** Constructor for JFlexTask. */
JFlexTask()51   public JFlexTask() {
52     OptionUtils.setDefaultOptions();
53     // ant default is different from the rest of JFlex
54     setVerbose(false);
55     setUnusedWarning(true);
56     Options.progress = false;
57   }
58 
59   /**
60    * Executes the ant task.
61    *
62    * @throws BuildException if any.
63    */
64   @Override
execute()65   public void execute() {
66     try {
67       if (inputFile == null)
68         throw new BuildException("Input file needed. Use <jflex file=\"your_scanner.flex\"/>");
69 
70       if (!inputFile.canRead()) throw new BuildException("Cannot read input file " + inputFile);
71 
72       try {
73         findPackageAndClass();
74         normalizeOutdir();
75         File destFile = new File(outputDir, className + ".java");
76 
77         if (inputFile.lastModified() > destFile.lastModified()) {
78           new LexGenerator(inputFile).generate();
79           if (!Options.verbose) System.out.println("Generated: " + destFile.getName());
80         }
81       } catch (IOException e1) {
82         throw new BuildException("IOException: " + e1.toString());
83       }
84     } catch (GeneratorException e) {
85       throw new BuildException("JFlex: generation failed!");
86     }
87   }
88 
89   /**
90    * Peek into .flex file to get package and class name
91    *
92    * @throws java.io.IOException if there is a problem reading the .flex file
93    */
findPackageAndClass()94   public void findPackageAndClass() throws IOException {
95     // find name of the package and class in jflex source file
96     packageName = null;
97     className = null;
98     Reader r = Files.newBufferedReader(inputFile.toPath(), StandardCharsets.UTF_8);
99     try (LineNumberReader reader = new LineNumberReader(r)) {
100       while (className == null || packageName == null) {
101         String line = reader.readLine();
102         if (line == null) {
103           break;
104         }
105 
106         if (packageName == null) {
107           Matcher matcher = PACKAGE_PATTERN.matcher(line);
108           if (matcher.find()) {
109             packageName = matcher.group(1);
110           }
111         }
112 
113         if (className == null) {
114           Matcher matcher = CLASS_PATTERN.matcher(line);
115           if (matcher.find()) {
116             className = matcher.group(1);
117           }
118         }
119       }
120 
121       // package name may be null, but class name not
122       if (className == null) {
123         className = "Yylex";
124       }
125     }
126   }
127 
128   /**
129    * Sets the actual output directory if not already set.
130    *
131    * <p>Uses javac logic to determine output dir = dest dir + package name If not destdir has been
132    * set, output dir = parent of input file
133    *
134    * <p>Assumes that package name is already set.
135    */
normalizeOutdir()136   public void normalizeOutdir() {
137     if (outputDir != null) return;
138 
139     // find out what the destination directory is. Append packageName to dest
140     // dir.
141     File destDir;
142 
143     // this is not the default the jflex logic, but javac-like
144     if (destinationDir != null) {
145       if (packageName == null) {
146         destDir = destinationDir;
147       } else {
148         String path = packageName.replace('.', File.separatorChar);
149         destDir = new File(destinationDir, path);
150       }
151     } else { // save parser to the same dir as .flex
152       destDir = new File(inputFile.getParent());
153     }
154 
155     setOutdir(destDir);
156   }
157 
158   /**
159    * getPackage.
160    *
161    * @return package name of input file
162    * @see #findPackageAndClass()
163    */
getPackage()164   public String getPackage() {
165     return packageName;
166   }
167 
168   /**
169    * Getter for the field {@code className}.
170    *
171    * @return class name of input file
172    * @see #findPackageAndClass()
173    */
getClassName()174   public String getClassName() {
175     return className;
176   }
177 
178   /**
179    * setDestdir.
180    *
181    * @param destinationDir a {@link java.io.File} object.
182    */
setDestdir(File destinationDir)183   public void setDestdir(File destinationDir) {
184     this.destinationDir = destinationDir;
185   }
186 
187   /**
188    * setOutdir.
189    *
190    * @param outDir a {@link java.io.File} object.
191    */
setOutdir(File outDir)192   public void setOutdir(File outDir) {
193     this.outputDir = outDir;
194     OptionUtils.setDir(outputDir);
195   }
196 
197   /**
198    * setFile.
199    *
200    * @param file a {@link java.io.File} object.
201    */
setFile(File file)202   public void setFile(File file) {
203     this.inputFile = file;
204   }
205 
206   /**
207    * setGenerateDot.
208    *
209    * @param genDot a boolean.
210    */
setGenerateDot(boolean genDot)211   public void setGenerateDot(boolean genDot) {
212     setDot(genDot);
213   }
214 
215   /**
216    * setTimeStatistics.
217    *
218    * @param displayTime a boolean.
219    */
setTimeStatistics(boolean displayTime)220   public void setTimeStatistics(boolean displayTime) {
221     Options.time = displayTime;
222   }
223 
224   /**
225    * setTime.
226    *
227    * @param displayTime a boolean.
228    */
setTime(boolean displayTime)229   public void setTime(boolean displayTime) {
230     setTimeStatistics(displayTime);
231   }
232 
233   /**
234    * setVerbose.
235    *
236    * @param verbose a boolean.
237    */
setVerbose(boolean verbose)238   public void setVerbose(boolean verbose) {
239     Options.verbose = verbose;
240     Options.unused_warning = verbose;
241   }
242 
243   /**
244    * setUnusedWarning.
245    *
246    * @param warn a boolean.
247    */
setUnusedWarning(boolean warn)248   public void setUnusedWarning(boolean warn) {
249     Options.unused_warning = warn;
250   }
251 
252   /**
253    * setSkeleton.
254    *
255    * @param skeleton a {@link java.io.File} object.
256    */
setSkeleton(File skeleton)257   public void setSkeleton(File skeleton) {
258     OptionUtils.setSkeleton(skeleton);
259   }
260 
261   /**
262    * setSkel.
263    *
264    * @param skeleton a {@link java.io.File} object.
265    */
setSkel(File skeleton)266   public void setSkel(File skeleton) {
267     setSkeleton(skeleton);
268   }
269 
270   /**
271    * setSkipMinimization.
272    *
273    * @param skipMin a boolean.
274    */
setSkipMinimization(boolean skipMin)275   public void setSkipMinimization(boolean skipMin) {
276     setNomin(skipMin);
277   }
278 
279   /**
280    * setNomin.
281    *
282    * @param b a boolean.
283    */
setNomin(boolean b)284   public void setNomin(boolean b) {
285     Options.no_minimize = b;
286   }
287 
288   /**
289    * setNobak.
290    *
291    * @param b a boolean.
292    */
setNobak(boolean b)293   public void setNobak(boolean b) {
294     Options.no_backup = b;
295   }
296 
297   /**
298    * setPack.
299    *
300    * @param b a boolean.
301    */
setPack(boolean b)302   public void setPack(boolean b) {
303     /* no-op - this is the only available generation method */
304   }
305 
306   /**
307    * setDot.
308    *
309    * @param b a boolean.
310    */
setDot(boolean b)311   public void setDot(boolean b) {
312     Options.dot = b;
313   }
314 
315   /**
316    * setDump.
317    *
318    * @param b a boolean.
319    */
setDump(boolean b)320   public void setDump(boolean b) {
321     Options.dump = b;
322   }
323 
324   /**
325    * setJLex.
326    *
327    * @param b a boolean.
328    */
setJLex(boolean b)329   public void setJLex(boolean b) {
330     Options.jlex = b;
331   }
332 
333   /**
334    * setLegacyDot.
335    *
336    * @param b a boolean.
337    */
setLegacyDot(boolean b)338   public void setLegacyDot(boolean b) {
339     Options.legacy_dot = b;
340   }
341 
342   /**
343    * Set the input encoding. If unset will use the JVM default.
344    *
345    * @param encodingName the name of the encoding to set (e.g. "utf-8").
346    */
setEncoding(String encodingName)347   public void setEncoding(String encodingName) {
348     OptionUtils.setEncoding(encodingName);
349   }
350 }
351