View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  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,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.myfaces.tobago.ant.sniplet;
21  
22  import org.apache.tools.ant.BuildException;
23  import org.apache.tools.ant.DirectoryScanner;
24  import org.apache.tools.ant.Task;
25  import org.apache.tools.ant.Project;
26  import org.apache.tools.ant.types.FileSet;
27  
28  import java.util.regex.Pattern;
29  import java.util.regex.Matcher;
30  import java.util.List;
31  import java.util.ArrayList;
32  import java.io.File;
33  import java.io.LineNumberReader;
34  import java.io.FileReader;
35  import java.io.FileNotFoundException;
36  import java.io.IOException;
37  import java.io.PrintWriter;
38  import java.io.FileOutputStream;
39  
40  /**
41   * This task extracts lines from source fileSets marked with a tag. Such a sniplet starts with
42   * <p/>
43   * code-sniplet-start id="id"
44   * <p/>
45   * and ends with
46   * <p/>
47   * code-sniplet-end id="id"
48   * <p/>
49   * Its allowed to have nested and even entangled tags. Each sniplet gets ist own output file
50   * in the output directory. The name of the file is [id].txt.
51   * <p/>
52   * The fileSets to process are specified by a fileset.
53   * <p/>
54   * Example:
55   * <pre>
56   * &lt;target name="code-extract">
57   *   &lt;taskdef name="code-extract" classname="org.apache.myfaces.tobago.ant.sniplet.CodeSnipletExtractTask"/>
58   *   &lt;code-extract outputDir="build/sniplets">
59   *     &lt;fileset dir="src">
60   *       &lt;include name="*.java"/>
61   *     &lt;/fileset>
62   *   &lt;/code-extract>
63   * &lt;/target>
64   * </pre>
65   */
66  public class CodeSnipletExtractTask extends Task {
67  
68    private List<CodeSniplet> sniplets;
69    private List<FileSet> fileSets;
70    private Pattern startPattern;
71    private Pattern endPattern;
72    private File outputDir;
73    private String outputFileNamePattern;
74    private boolean stripLeadingSpaces;
75  
76    public void init() throws BuildException {
77      startPattern = Pattern.compile(".*code-sniplet-start\\s*id\\s*=\\s*\"(\\w*)\".*");
78      endPattern = Pattern.compile(".*code-sniplet-end\\s*id\\s*=\\s*\"(\\w*)\".*");
79      this.sniplets = new ArrayList<CodeSniplet>();
80      this.fileSets = new ArrayList<FileSet>();
81    }
82  
83    public String getOutputFileNamePattern() {
84      return outputFileNamePattern;
85    }
86  
87    public void setOutputFileNamePattern(final String outputFileNamePattern) {
88      this.outputFileNamePattern = outputFileNamePattern;
89    }
90  
91    public File getOutputDir() {
92      return outputDir;
93    }
94  
95    public void setOutputDir(final File outputDir) {
96      this.outputDir = outputDir;
97    }
98  
99    public void addConfiguredFileSet(final FileSet fileSet) {
100     this.fileSets.add(fileSet);
101   }
102 
103   public boolean isStripLeadingSpaces() {
104     return stripLeadingSpaces;
105   }
106 
107   public void setStripLeadingSpaces(final boolean stripLeadingSpaces) {
108     this.stripLeadingSpaces = stripLeadingSpaces;
109   }
110 
111   public void execute() throws BuildException {
112     for (int k = 0; k < fileSets.size(); k++) {
113       final FileSet fileSet = fileSets.get(k);
114       final DirectoryScanner dirScanner = fileSet.getDirectoryScanner(getProject());
115       dirScanner.scan();
116       final String[] includedFiles = dirScanner.getIncludedFiles();
117       for (int i = 0; i < includedFiles.length; i++) {
118         final String fileS = includedFiles[i];
119         LineNumberReader in = null;
120         try {
121           in = new LineNumberReader(new FileReader(fileSet.getDir(getProject())
122               + File.separator + fileS));
123           String line = in.readLine();
124           while (line != null) {
125             final Matcher startMatcher = startPattern.matcher(line);
126             if (startMatcher.matches()) {
127               startSniplet(startMatcher.group(1), fileS, in.getLineNumber());
128             } else {
129               final Matcher endMatcher = endPattern.matcher(line);
130               if (endMatcher.matches()) {
131                 endSniplet(endMatcher.group(1), fileS, in.getLineNumber());
132               } else {
133                 addLine(line);
134               }
135             }
136             line = in.readLine();
137           }
138           for (int j = 0; j < sniplets.size(); j++) {
139             final CodeSniplet codeSniplet = (CodeSniplet) sniplets.get(j);
140             if (codeSniplet.getLineEnd() == 0) {
141               codeSniplet.setLineEnd(in.getLineNumber());
142               log("Unclosed sniplet '" + codeSniplet.getId() + "' in file '" + codeSniplet.getFileName() + "' at line '"
143                   + codeSniplet.getLineStart() + "'. Forcing close", Project.MSG_WARN);
144             }
145           }
146           createOutput();
147           sniplets = new ArrayList<CodeSniplet>();
148         } catch (final IOException e) {
149           throw new BuildException(e);
150         } finally {
151           if (in != null) {
152             try {
153               in.close();
154             } catch (final IOException e) {
155               throw new BuildException(e);
156             }
157           }
158         }
159       }
160     }
161   }
162 
163   private void createOutput() throws FileNotFoundException {
164     for (int i = 0; i < sniplets.size(); i++) {
165       final CodeSniplet codeSniplet = (CodeSniplet) sniplets.get(i);
166       final String fileName = codeSniplet.getId() + ".snip";
167       final File file = new File(outputDir, fileName);
168       final PrintWriter out = new PrintWriter(new FileOutputStream(file));
169       final StringBuffer code = codeSniplet.getCode(stripLeadingSpaces);
170       out.print(code);
171       out.close();
172       log("Wrote: " + file.getName(), Project.MSG_INFO);
173     }
174   }
175 
176   private void startSniplet(final String id, final String fileName, final int lineNumber) {
177     for (int i = 0; i < sniplets.size(); i++) {
178       final CodeSniplet codeSniplet = (CodeSniplet) sniplets.get(i);
179       if (codeSniplet.getId().equals(id)) {
180         throw new BuildException("Duplicate sniplet declaration '" + id + "' in file '" + fileName + "' at line '"
181             + lineNumber + "'. First declaration was in file '" + codeSniplet.getFileName() + "' at line '"
182             + codeSniplet.getLineStart() + "'.");
183       }
184     }
185     final CodeSniplet codeSniplet = new CodeSniplet(id, fileName, lineNumber);
186     sniplets.add(codeSniplet);
187   }
188 
189   private void endSniplet(final String id, final String fileName, final int lineNumber) {
190     for (int i = 0; i < sniplets.size(); i++) {
191       final CodeSniplet codeSniplet = (CodeSniplet) sniplets.get(i);
192       if (codeSniplet.getId().equals(id)) {
193         codeSniplet.setLineEnd(lineNumber);
194         return;
195       }
196     }
197     throw new BuildException("No start of sniplet '" + id + "' in file '" + fileName + "' at line '" + lineNumber
198         + "' found.");
199   }
200 
201   private void addLine(final String line) {
202     for (int i = 0; i < sniplets.size(); i++) {
203       final CodeSniplet codeSniplet = (CodeSniplet) sniplets.get(i);
204       if (codeSniplet.getLineEnd() == 0) {
205         codeSniplet.addLine(line);
206         log("Adding: " + line + " -> " + codeSniplet.getFileName() + ":" + codeSniplet.getId(), Project.MSG_DEBUG);
207       }
208     }
209   }
210 
211 }