View Javadoc

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 }