1 /*
2 * @(#)$Id: JavafxdocUtil.java 69 2010-05-13 14:21:43Z nderwin $
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 * under the License.
16 */
17
18 package net.sf.jfxdplugin;
19
20 import java.io.ByteArrayOutputStream;
21 import java.io.File;
22 import java.io.FileNotFoundException;
23 import java.io.OutputStream;
24 import java.io.OutputStreamWriter;
25 import java.io.UnsupportedEncodingException;
26 import java.util.ArrayList;
27 import java.util.List;
28 import java.util.regex.Matcher;
29 import java.util.regex.Pattern;
30 import java.util.regex.PatternSyntaxException;
31 import org.apache.commons.lang.SystemUtils;
32 import org.apache.maven.project.MavenProject;
33 import org.codehaus.plexus.util.FileUtils;
34 import org.codehaus.plexus.util.IOUtil;
35 import org.codehaus.plexus.util.StringUtils;
36 import org.codehaus.plexus.util.cli.CommandLineException;
37 import org.codehaus.plexus.util.cli.CommandLineUtils;
38 import org.codehaus.plexus.util.cli.Commandline;
39
40 /**
41 * Utility functions for the javafxdoc plugin.
42 * <p>
43 * Based on the Maven Javadoc Plugin version 2.6.
44 *
45 * @author Nathan Erwin
46 * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
47 * @version $Revision: 69 $ $Date: 2010-05-13 10:21:43 -0400 (Thu, 13 May 2010) $
48 * @since 1.0
49 * @see <a href="http://maven.apache.org/plugins/maven-javadoc-plugin/">
50 * http://maven.apache.org/plugins/maven-javadoc-plugin/</a>
51 */
52 final class JavafxdocUtil {
53
54 /**
55 * List of source file extensions that can be documented.
56 */
57 private static final String[] EXTENSIONS = {"fx"};
58
59 /**
60 * Hidden utility class constructor.
61 */
62 private JavafxdocUtil() {
63 }
64
65 /**
66 * Convenience method that gets the files to be included in the javafxdoc.
67 *
68 * @param sourceDirectory the directory where the source files are located
69 * @param files the variable that contains the appended filenames of the
70 * files to be included in the javafxdoc
71 * @param excludePackages the packages to be excluded from the javafxdocs
72 */
73 static void addFilesFromSource(final List<String> files,
74 final File sourceDirectory, final String[] excludePackages) {
75 String[] fileList =
76 FileUtils.getFilesFromExtension(sourceDirectory.getPath(),
77 EXTENSIONS);
78
79 if (fileList.length != 0) {
80 List<String> tmpFiles = JavafxdocUtil.getIncludedFiles(
81 sourceDirectory, fileList, excludePackages);
82 files.addAll(tmpFiles);
83 }
84 }
85
86 /**
87 * Method that gets all the source files to be excluded from the javafxdoc
88 * on the given source paths.
89 *
90 * @param sourcePaths the path to the source files
91 * @param subpackagesList list of subpackages to be included in the
92 * javafxdoc
93 * @param excludedPackages the package names to be excluded in the
94 * javafxdoc
95 * @return a List of the source files to be excluded in the generated
96 * javafxdoc
97 */
98 static List<String> getExcludedNames(final List<String> sourcePaths,
99 final String[] subpackagesList, final String[] excludedPackages) {
100 List<String> excludedNames = new ArrayList<String>();
101
102 for (String path : sourcePaths) {
103 for (int i = 0; i < subpackagesList.length; i++) {
104 List<String> excludes = JavafxdocUtil.getExcludedPackages(path,
105 excludedPackages);
106 excludedNames.addAll(excludes);
107 }
108 }
109
110 return excludedNames;
111 }
112
113 /**
114 * Method that gets the complete package names (including subpackages) of
115 * the packages that were defined in the excludePackageNames parameter.
116 *
117 * @param sourceDirectory the directory where the source files are located
118 * @param excludePackagenames package names to be excluded in the javafxdoc
119 * @return a List of the packagenames to be excluded
120 */
121 static List<String> getExcludedPackages(final String sourceDirectory,
122 final String[] excludePackagenames) {
123 List<String> files = new ArrayList<String>();
124
125 String[] fileList = FileUtils.getFilesFromExtension(sourceDirectory,
126 EXTENSIONS);
127
128 for (int i = 0; i < excludePackagenames.length; i++) {
129 for (int j = 0; j < fileList.length; j++) {
130 String[] excludeName = excludePackagenames[i].split("[*]");
131 int u = 0;
132 while (u < excludeName.length) {
133 if ((!"".equals(excludeName[u].trim()))
134 && (fileList[j].indexOf(excludeName[u]) != -1)
135 && (sourceDirectory.indexOf(excludeName[u])
136 == -1)) {
137 files.add(fileList[j]);
138 }
139 u++;
140 }
141 }
142 }
143
144 List<String> excluded = new ArrayList<String>();
145 for (String file : files) {
146 int idx = file.lastIndexOf(File.separatorChar);
147 String tmpStr = file.substring(0, idx);
148 tmpStr = tmpStr.replace('\\', '/');
149 String[] srcSplit =
150 tmpStr.split(sourceDirectory.replace('\\', '/') + '/');
151 String excludedPackage = srcSplit[1].replace('/', '.');
152
153 if (!excluded.contains(excludedPackage)) {
154 excluded.add(excludedPackage);
155 }
156 }
157
158 return excluded;
159 }
160
161 /**
162 * Method that gets the files or classes that would be included in the
163 * javafxdocs using the subpackages parameter.
164 *
165 * @param sourceDirectory the directory where the source files are located
166 * @param fileList the list of all files found in the sourceDirectory
167 * @param excludePackages package names to be excluded in the javafxdoc
168 * @return a StringBuffer that contains the appended file names of the files
169 * to be included in the javafxdoc
170 */
171 static List<String> getIncludedFiles(final File sourceDirectory,
172 final String[] fileList, final String[] excludePackages) {
173 List<String> files = new ArrayList<String>();
174
175 for (int j = 0; j < fileList.length; j++) {
176 boolean include = true;
177 for (int k = 0; (include) && (k < excludePackages.length); k++) {
178 // handle wildcards (*) in the excludePackageNames
179 String[] excludeName = excludePackages[k].split("[*]");
180
181 if (excludeName.length == 0) {
182 continue;
183 }
184
185 if (excludeName.length > 1) {
186 int u = 0;
187 while (include && u < excludeName.length) {
188 if ((!"".equals(excludeName[u].trim()))
189 && (fileList[j].indexOf(excludeName[u])
190 != -1)) {
191 include = false;
192 }
193 u++;
194 }
195 } else {
196 if (fileList[j].startsWith(sourceDirectory.toString()
197 + File.separatorChar + excludeName[0])) {
198 if (excludeName[0].endsWith(String.valueOf(
199 File.separatorChar))) {
200 int i = fileList[j].lastIndexOf(File.separatorChar);
201 String packageName =
202 fileList[j].substring(0, i + 1);
203 File currentPackage = new File(packageName);
204 File excludedPackage = new File(sourceDirectory,
205 excludeName[0]);
206
207 if (currentPackage.equals(excludedPackage)) {
208 for (String ext : EXTENSIONS) {
209 if (fileList[j].substring(i).indexOf("."
210 + ext)
211 != -1) {
212 include = true;
213 } else {
214 include = false;
215 }
216 }
217 } else {
218 include = false;
219 }
220 } else {
221 include = false;
222 }
223 }
224 }
225 }
226
227 if (include) {
228 files.add(JavafxdocUtil.quotedPathArgument(fileList[j]));
229 }
230 }
231
232 return files;
233 }
234
235 /**
236 * Call the Javafxdoc tool and parse its output to find its version.
237 *
238 * @param javafxdocExe not null file
239 * @return the javafxdoc version as float
240 * @throws CommandLineException if any
241 * @throws FileNotFoundException if javafxdocExe is null, doesn't exist or
242 * is not a file
243 * @throws IllegalArgumentException if no output was found in the command
244 * line
245 * @see #parseJavadocVersion(String)
246 */
247 static float getJavafxdocVersion(final File javafxdocExe) throws
248 CommandLineException, FileNotFoundException {
249 if ((javafxdocExe == null) || (!javafxdocExe.exists())
250 || (!javafxdocExe.isFile())) {
251 throw new FileNotFoundException();
252 }
253
254 Commandline cmd = new Commandline();
255 cmd.setExecutable(javafxdocExe.getAbsolutePath());
256 cmd.setWorkingDirectory(javafxdocExe.getParentFile());
257 cmd.createArg().setValue("-version");
258
259 CommandLineUtils.StringStreamConsumer out =
260 new CommandLineUtils.StringStreamConsumer();
261 CommandLineUtils.StringStreamConsumer err =
262 new CommandLineUtils.StringStreamConsumer();
263
264 int exitCode = CommandLineUtils.executeCommandLine(cmd, out, err);
265
266 if (exitCode != 0) {
267 StringBuilder msg = new StringBuilder(exitCode + " - " + err.
268 getOutput());
269 msg.append(SystemUtils.LINE_SEPARATOR);
270 msg.append(CommandLineUtils.toString(cmd.getCommandline()));
271 throw new CommandLineException(msg.toString());
272 }
273
274 if (StringUtils.isNotEmpty(err.getOutput())) {
275 return parseJavadocVersion(err.getOutput());
276 } else if (StringUtils.isNotEmpty(out.getOutput())) {
277 return parseJavadocVersion(out.getOutput());
278 }
279
280 throw new IllegalArgumentException();
281 }
282
283 /**
284 * Method that removes the invalid directories in the specified directories.
285 * <b>Note</b>: All elements in <code>dirs</code> could be absolute or
286 * relative against the project's base directory <code>String</code> path.
287 *
288 * @param project the current Maven project
289 * @param dirs the list of <code>String</code> directory paths that will be
290 * validated
291 * @return a List of valid <code>String</code> directory absolute paths
292 */
293 static List<String> pruneDirs(final MavenProject project,
294 final List<String> dirs) {
295 List<String> pruned = new ArrayList<String>(dirs.size());
296
297 for (String dir : dirs) {
298 if (null == dir) {
299 continue;
300 }
301
302 File directory = new File(dir);
303 if (!directory.isAbsolute()) {
304 directory = new File(project.getBasedir(), directory.getPath());
305 }
306
307 if ((directory.isDirectory())
308 && (!pruned.contains(directory.getAbsolutePath()))) {
309 pruned.add(directory.getAbsolutePath());
310 }
311 }
312
313 return pruned;
314 }
315
316 /**
317 * Convenience method to wrap an argument value in single quotes
318 * (i.e. <code>'</code>). Intended for values which may contain whitespaces.
319 * <br />
320 * To prevent javafxdoc errors, the line separators (i.e. <code>\n</code>)
321 * are skipped.
322 *
323 * @param value the argument value
324 * @return argument with quote
325 */
326 static String quotedArgument(final String value) {
327 String arg = value;
328
329 if (StringUtils.isNotEmpty(arg)) {
330 if (arg.indexOf("'") != -1) {
331 arg = StringUtils.replace(arg, "'", "\\'");
332 }
333 arg = "'" + arg + "'";
334
335 // To prevent javafxdoc error
336 arg = StringUtils.replace(arg, "\n", " ");
337 }
338
339 return arg;
340 }
341
342 /**
343 * Convenience method to format a path argument so that it is properly
344 * interpreted by the javafxdoc tool. Intended for path values which may
345 * contain whitespaces.
346 *
347 * @param value the argument value
348 * @return path argument with quote
349 */
350 static String quotedPathArgument(final String value) {
351 String path = value;
352
353 if (StringUtils.isNotEmpty(path)) {
354 path = path.replace('\\', '/');
355 if (path.indexOf("\'") != -1) {
356 // pass negative number to split in case the string ends with
357 // one or more single quotes so we won't lose them
358 String[] split = path.split("\'", -1);
359 path = "";
360
361 for (int i = 0; i < split.length; i++) {
362 if (i != split.length - 1) {
363 path = path + split[i] + "\\'";
364 } else {
365 path = path + split[i];
366 }
367 }
368 }
369 path = "'" + path + "'";
370 }
371
372 return path;
373 }
374
375 /**
376 * Validate if a charset is supported on this platform.
377 *
378 * @param charsetName the charsetName to be check.
379 * @return <code>true</code> if the charset is supported by the JVM,
380 * <code>false</code> otherwise.
381 */
382 static boolean validateEncoding(final String charsetName) {
383 if (StringUtils.isEmpty(charsetName)) {
384 return false;
385 }
386
387 OutputStream ost = new ByteArrayOutputStream();
388 OutputStreamWriter osw = null;
389 try {
390 osw = new OutputStreamWriter(ost, charsetName);
391 } catch (UnsupportedEncodingException exc) {
392 return false;
393 } finally {
394 IOUtil.close(osw);
395 }
396 return true;
397 }
398
399 /**
400 * Parse the output for 'javafxdoc -J-version' and return the javafxdoc
401 * version recognized.
402 *
403 * @param output for 'javafxdoc -J-version'
404 * @return the version of the javafxdoc for the output.
405 * @throws PatternSyntaxException if the output doesn't match with the
406 * output pattern
407 * <tt>(?s).*?([0-9]+\\.[0-9]+)(\\.([0-9]+))?.*</tt>.
408 * @throws IllegalArgumentException if the output is null
409 */
410 private static float parseJavadocVersion(final String output) {
411 if (StringUtils.isEmpty(output)) {
412 throw new IllegalArgumentException();
413 }
414
415 Pattern pattern = Pattern.compile(
416 "(?s).*?([0-9]+\\.[0-9]+)(\\.([0-9]+))?.*");
417
418 Matcher matcher = pattern.matcher(output);
419 if (!matcher.matches()) {
420 throw new PatternSyntaxException("'" + output + "'",
421 pattern.pattern(), pattern.toString().length() - 1);
422 }
423
424 String version = matcher.group(3);
425 if (version == null) {
426 version = matcher.group(1);
427 } else {
428 version = matcher.group(1) + version;
429 }
430
431 return Float.parseFloat(version);
432 }
433 }