View Javadoc

1   /*
2    * Licensed under the Apache License, Version 2.0 (the "License");
3    * you may not use this file except in compliance with the License.
4    * You may obtain a copy of the License at
5    *
6    *      http://www.apache.org/licenses/LICENSE-2.0
7    *
8    * Unless required by applicable law or agreed to in writing, software
9    * distributed under the License is distributed on an "AS IS" BASIS,
10   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11   * See the License for the specific language governing permissions and
12   * limitations under the License.
13   */
14  package gr.abiss.mvn.plugins.jstools;
15  
16  import gr.abiss.mvn.plugins.jstools.utils.FileSystemDirectoryUtils;
17  import java.io.File;
18  import java.io.FileNotFoundException;
19  import java.io.FileOutputStream;
20  import java.io.IOException;
21  import java.io.InputStream;
22  import java.io.OutputStream;
23  import java.io.ByteArrayOutputStream;
24  import java.io.PrintStream;
25  import java.util.Iterator;
26  import java.util.Locale;
27  import java.util.Set;
28  import org.apache.commons.io.IOUtils;
29  import org.apache.maven.reporting.MavenReportException;
30  import org.codehaus.doxia.sink.Sink;
31  import org.mozilla.javascript.Context;
32  import org.mozilla.javascript.Scriptable;
33  
34  /***
35   * Goal to generate JavaScript coverage reports using JSLint
36   * 
37   * @goal jslint
38   * @phase post-site
39   * @version $Id$
40   * @author manos
41   */
42  public class JsLintMojo extends AbstractBaseJstoolsReport {
43  
44  	/***
45  	 * A path pointing to some custom JS code to drive JSLint via Rhino. You can provide your own,
46  	 * or let the plugin use its own internal version. 
47  	 * @parameter expression="${project.build.directory}/jslint/fulljslint_init.js"
48  	 */
49  	private String jslintInitJsFilePath;
50  	/***
51  	 * A path pointing to a full version of JSLint (e.g.
52  	 * /home/username/stuff/fulljslint.js). If no value is provided the plugin wil
53  	 * use its own internal version. You can find the file at 
54  	 * <a href="http://www.jslint.com/fulljslint.js">http://www.jslint.com/fulljslint.js</a>
55  	 * 
56  	 * @parameter expression="${project.build.directory}/jslint/fulljslint.js"
57  	 */
58  	private String jslintJsFilePath;
59  	
60  	/***
61  	 * Wether to only include errors in the report. Default is true (errors only)
62  	 * @parameter expression="true"
63  	 */
64  	private boolean reportJslintErrorsOnly;
65  
66  	/***
67  	 * if use of some browser features should be restricted (unused JSLint
68  	 * option)
69  	 */
70  	boolean adsafe = true;
71  
72  	/*** if bitwise operators should not be allowed (JSLint option) */
73  	boolean bitwise = true;
74  
75  	/*** if the standard browser globals should be predefined (JSLint option) */
76  	boolean browser = true;
77  
78  	/*** if upper case HTML should be allowed (JSLint option) */
79  	boolean cap = true;
80  
81  	/*** if debugger statements should be allowed (JSLint option) */
82  	boolean debug = true;
83  
84  	/*** if === should be required (JSLint option) */
85  	boolean eqeqeq = true;
86  
87  	/*** if eval should be allowed (JSLint option) */
88  	boolean evil = true;
89  
90  	/*** if HTML fragments should be allowed (JSLint option) */
91  	boolean fragment = true;
92  
93  	/*** if line breaks should not be checked (JSLint option) */
94  	boolean laxbreak = true;
95  
96  	/*** if names should be checked (JSLint option) */
97  	boolean nomen = true;
98  
99  	/*** if the scan should stop on first error (JSLint option) */
100 	boolean passfail = true;
101 
102 	/*** if increment/decrement should not be allowed (JSLint option) */
103 	boolean plusplus = true;
104 
105 	/*** if the Rhino environment globals should be predefined (JSLint option) */
106 	boolean rhino = true;
107 
108 	/*** if variables should be declared before used (JSLint option) */
109 	boolean undef = true;
110 
111 	/*** if strict whitespace rules apply (JSLint option) */
112 	boolean white = true;
113 
114 	/*** if the Yahoo Widgets globals should be predefined (JSLint option) */
115 	boolean widget = true;
116 
117 	/***
118 	 * Use JSLint to generate a code analysis report
119 	 * @see gr.abiss.mvn.plugins.jstools.AbstractBaseJstoolsReport#doGenerateReport(java.util.Locale)
120 	 */
121 	public void doGenerateReport(Locale defaultLocale) throws MavenReportException {
122 		try {
123 			File outDir = new File(this.getOutputDirectory());
124 			if (!outDir.exists()) {
125 				outDir.mkdirs();
126 			}
127 			Set jsFiles = this.getJavaScriptFiles();
128 			Sink sink = getSink();
129 			sink.head();
130 			sink.title();
131 			sink.text("JSLint Coverage Report");
132 			sink.title_();
133 			sink.head_();
134 			sink.body();
135 			sink.section1();
136 			sink.sectionTitle1();
137 			sink.text("Overview");
138 			sink.sectionTitle1_();
139 			sink.paragraph();
140 			sink.rawText("This report was build with <a href=\"http://www.jslint.com/\">JSLint</a>, the JavaScript Verifier. " +
141 					"JSLint is a JavaScript program that looks for problems in other JavaScript programs.");
142 			sink.paragraph_();
143 			if (jsFiles.size() > 0) {
144 				String jsLintCode = getFileAsString(this.jslintJsFilePath) + this.getFileAsString(this.jslintInitJsFilePath);
145 				sink.paragraph();
146 				sink.text("Here is the list of files in this report:");
147 				sink.paragraph_();
148 				sink.list();
149 				for (Iterator iter = jsFiles.iterator(); iter.hasNext();) {
150 					File jsFile = (File) iter.next();
151 					sink.listItem();
152 					sink.rawText("<a href=\"#_"
153 							+ jsFile.getName().replaceAll(" ", "") + "\">"
154 							+ jsFile.getName() + "</a>");
155 					sink.listItem_();
156 				}
157 				sink.list_();
158 				sink.section1_();
159 				for (Iterator iter = jsFiles.iterator(); iter.hasNext();) {
160 					File jsFile = (File) iter.next();
161 					ByteArrayOutputStream buff = runJslintOnSource(jsLintCode, jsFile
162 							.getAbsolutePath());
163 					sink.rawText("<a id=\"_"
164 									+ jsFile.getName().replaceAll(" ", "")
165 									+ "\"> </a>");
166 					sink.sectionTitle1();
167 					sink.text(jsFile.getName());
168 					sink.sectionTitle1_();
169 					sink.rawText("<div class=\"source\"><pre>"
170 							+ (buff.size() > 0 ? buff.toString()
171 									: "No problems found") + "</pre></div>");
172 				}
173 			} else {
174 				sink.paragraph();
175 				sink.text("There are currently no files in this report.");
176 				sink.paragraph_();
177 			}
178 			sink.body_();
179 			sink.flush();
180 			sink.close();
181 		} catch (Exception e) {
182 			e.printStackTrace();
183 			throw new MavenReportException("Error building JSLint report", e);
184 		}
185 	}
186 
187 	/***
188 	 * @param jsLintCode
189 	 * @param sourceFilePath
190 	 * @return
191 	 */
192 	private ByteArrayOutputStream runJslintOnSource(String jsLintCode, String sourceFilePath){
193 		ByteArrayOutputStream buff = new ByteArrayOutputStream();
194 		PrintStream out = new PrintStream(buff);
195 		Context context = Context.enter();
196 		Scriptable scope = context.initStandardObjects(null);
197 		Scriptable varOut = Context.toObject(out, scope);
198 		scope.put("out", scope, varOut);
199 		Scriptable varSourceFilePath = Context.toObject(sourceFilePath, scope);
200 		scope.put("sourceFilePath", scope, varSourceFilePath);
201 		Scriptable varReportJslintErrorsOnly = Context.toObject(this.reportJslintErrorsOnly, scope);
202 		scope.put("reportJslintErrorsOnly", scope, varReportJslintErrorsOnly);
203 		context.evaluateString(scope, jsLintCode,
204 				"mvn-jstools jslint script", 1, null);
205 		return buff;
206 	}
207 
208 	/***
209 	 * Setup the plugin's internal JSLint version
210 	 * 
211 	 * @param defaultLocale
212 	 * @throws MavenReportException
213 	 */
214 	protected void setUp(Locale defaultLocale) throws MavenReportException {
215 		// String jrhinoSemiPath = "/jslint/rhino.js";
216 		// copy the internal jslint distribution archive to target/
217 		try {
218 			File destDir = new File(this.buildDir+"/jslint");
219 			destDir.mkdirs();
220 			copyScript("/jslint/fulljslint.js", this.buildDir+"/jslint/fulljslint.js");
221 			copyScript("/jslint/fulljslint_init.js", this.buildDir+"/jslint/fulljslint_init.js");
222 			// copyScript(jrhinoSemiPath, this.buildDir + jrhinoSemiPath );
223 		} catch (FileNotFoundException fe) {
224 			throw new MavenReportException("Cannot find resource in classpath", fe);
225 		} catch (IOException ioe) {
226 			throw new MavenReportException("Error copying resource to target dir", ioe);
227 		}
228 	}
229 	/***
230 	 * Tear down the plugin's internal JSLint version
231 	 * 
232 	 * @param defaultLocale
233 	 * @throws MavenReportException
234 	 */
235 	protected void tearDown(Locale defaultLocale) throws MavenReportException {
236 		try {
237 			File runDir = new File(this.buildDir+"/jslint");
238 			if(runDir.exists()){
239 				FileSystemDirectoryUtils.deleteDirectory(runDir);
240 			}
241 		} catch (Exception ioe) {
242 			throw new MavenReportException("Error clearing up", ioe);
243 		}
244 	}
245 
246 	/***
247 	 * @param jslintSemiPath
248 	 * @throws FileNotFoundException
249 	 * @throws IOException
250 	 */
251 	private void copyScript(String classpath, String filePath)
252 			throws FileNotFoundException, IOException {
253 		InputStream input = this.getClass().getResourceAsStream(classpath);
254 		OutputStream output = new FileOutputStream(filePath);
255 		IOUtils.copy(input, output);
256 		input.close();
257 		output.close();
258 	}
259 
260 
261 	/***
262 	 * @see org.apache.maven.reporting.AbstractMavenReport#isExternalReport()
263 	 */
264 	public boolean isExternalReport() {
265 		return false;
266 	}
267 
268 	
269 
270 	/***
271 	 * @see gr.abiss.mvn.plugins.jstools.AbstractBaseJstoolsReport#getBundleKey()
272 	 */
273 	public String getBundleKey() {
274 		return "jslint";
275 	}
276     
277 }