Java tutorial
/* Copyright (C) 2014 Chr. Clemens Lee <clemens@kclee.com>. This file is part of JavaNCSS (http://javancss.codehaus.org/). This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA*/ package javancss; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.*; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.OptionBuilder; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import javancss.parser.JavaParser; import javancss.parser.JavaParserDebug; import javancss.parser.JavaParserInterface; import javancss.parser.JavaParserTokenManager; import javancss.parser.TokenMgrError; import javax.swing.*; /** * While the Java parser class might be the heart of JavaNCSS, * this class is the brain. This class controls input and output and * invokes the Java parser. * * @author Chr. Clemens Lee <clemens@kclee.com> * , recursive feature by Pk Hannu * , additional javadoc metrics by Emilio Gongora <emilio@sms.nl> * , and Guillermo Rodriguez <guille@sms.nl>. * @version $Id$ */ public class Javancss { private Logger log = Logger.getLogger(getClass().getName()); private static final String DEFAULT_ENCODING = null; private boolean exit = false; private List<File> _vJavaSourceFiles = null; private String encoding = DEFAULT_ENCODING; private String _sErrorMessage = null; private Throwable _thrwError = null; private JavaParserInterface _pJavaParser = null; private int _ncss = 0; private int _loc = 0; private List<FunctionMetric> _vFunctionMetrics = new ArrayList<FunctionMetric>(); private List<ObjectMetric> _vObjectMetrics = new ArrayList<ObjectMetric>(); private List<PackageMetric> _vPackageMetrics = null; private List<Object[]> _vImports = null; private Map<String, PackageMetric> _htPackages = null; private Object[] _aoPackage = null; /** * Just used for parseImports. */ private File _sJavaSourceFile = null; private Reader createSourceReader(File sSourceFile_) { try { return newReader(sSourceFile_); } catch (IOException pIOException) { if (_sErrorMessage == null || _sErrorMessage.trim().length() == 0) { _sErrorMessage = ""; } else { _sErrorMessage += "\n"; } _sErrorMessage += "File not found: " + sSourceFile_.getAbsolutePath(); _thrwError = pIOException; return null; } } private void _measureSource(File sSourceFile_) throws Exception, Error { Reader reader; // opens the file try { reader = newReader(sSourceFile_); } catch (IOException pIOException) { if (_sErrorMessage == null || _sErrorMessage.trim().length() == 0) { _sErrorMessage = ""; } else { _sErrorMessage += "\n"; } _sErrorMessage += "File not found: " + sSourceFile_.getAbsolutePath(); _thrwError = pIOException; throw pIOException; } String sTempErrorMessage = _sErrorMessage; try { // the same method but with a Reader _measureSource(reader); } catch (Exception pParseException) { if (sTempErrorMessage == null) { sTempErrorMessage = ""; } sTempErrorMessage += "ParseException in " + sSourceFile_.getAbsolutePath() + "\nLast useful checkpoint: \"" + _pJavaParser.getLastFunction() + "\"\n"; sTempErrorMessage += pParseException.getMessage() + "\n"; _sErrorMessage = sTempErrorMessage; _thrwError = pParseException; throw pParseException; } catch (Error pTokenMgrError) { if (sTempErrorMessage == null) { sTempErrorMessage = ""; } sTempErrorMessage += "TokenMgrError in " + sSourceFile_.getAbsolutePath() + "\n" + pTokenMgrError.getMessage() + "\n"; _sErrorMessage = sTempErrorMessage; _thrwError = pTokenMgrError; throw pTokenMgrError; } } private void _measureSource(Reader reader) throws Exception, Error { log.fine("_measureSource(Reader).ENTER"); try { // create a parser object if (log.isLoggable(Level.FINE)) { log.fine("creating JavaParserDebug"); _pJavaParser = new JavaParserDebug(reader); } else { log.fine("creating JavaParser"); _pJavaParser = new JavaParser(reader); } // execute the parser _pJavaParser.parse(); log.fine("Javancss._measureSource(DataInputStream).SUCCESSFULLY_PARSED"); _ncss += _pJavaParser.getNcss(); // increment the ncss _loc += _pJavaParser.getLOC(); // and loc // add new data to global vector _vFunctionMetrics.addAll(_pJavaParser.getFunction()); _vObjectMetrics.addAll(_pJavaParser.getObject()); Map<String, PackageMetric> htNewPackages = _pJavaParser.getPackage(); /* List vNewPackages = new Vector(); */ for (Map.Entry<String, PackageMetric> entry : htNewPackages.entrySet()) { String sPackage = entry.getKey(); PackageMetric pckmNext = htNewPackages.get(sPackage); pckmNext.name = sPackage; PackageMetric pckmPrevious = _htPackages.get(sPackage); pckmNext.add(pckmPrevious); _htPackages.put(sPackage, pckmNext); } } catch (Exception pParseException) { if (_sErrorMessage == null) { _sErrorMessage = ""; } _sErrorMessage += "ParseException in STDIN"; if (_pJavaParser != null) { _sErrorMessage += "\nLast useful checkpoint: \"" + _pJavaParser.getLastFunction() + "\"\n"; } _sErrorMessage += pParseException.getMessage() + "\n"; _thrwError = pParseException; throw pParseException; } catch (Error pTokenMgrError) { if (_sErrorMessage == null) { _sErrorMessage = ""; } _sErrorMessage += "TokenMgrError in STDIN\n"; _sErrorMessage += pTokenMgrError.getMessage() + "\n"; _thrwError = pTokenMgrError; throw pTokenMgrError; } } private void _measureFiles(List<File> sourceFiles) throws TokenMgrError { for (File file : sourceFiles) { if (!exit) { try { _measureSource(file); } catch (Throwable pThrowable) { // hmm, do nothing? Use getLastError() or so to check for details. // error details have been written into lastError } } } } /** * If arguments were provided, they are used, otherwise * the input stream is used. */ private void _measureRoot(Reader reader) throws Exception, Error { _htPackages = new HashMap<String, PackageMetric>(); // either there are argument files, or stdin is used if (_vJavaSourceFiles == null) { _measureSource(reader); } else { // the collection of files get measured _measureFiles(_vJavaSourceFiles); } _vPackageMetrics = new ArrayList<PackageMetric>(); for (PackageMetric pkm : _htPackages.values()) { _vPackageMetrics.add(pkm); } Collections.sort(_vPackageMetrics); } public List<Object[]> getImports() { return _vImports; } /** * Return info about package statement. * First element has name of package, * then begin of line, etc. */ public Object[] getPackage() { return _aoPackage; } /** * The same as getFunctionMetrics?! */ public List<FunctionMetric> getFunctions() { return _vFunctionMetrics; } public void printObjectNcss(Writer w) throws IOException { getFormatter().printObjectNcss(w); } public void printFunctionNcss(Writer w) throws IOException { getFormatter().printFunctionNcss(w); } public void printPackageNcss(Writer w) throws IOException { getFormatter().printPackageNcss(w); } public void printJavaNcss(Writer w) throws IOException { getFormatter().printJavaNcss(w); } public void printStart(Writer pw) throws IOException { getFormatter().printStart(pw); } public void printEnd(Writer pw) throws IOException { getFormatter().printEnd(pw); } public Javancss(List<File> vJavaSourceFiles_) { this(vJavaSourceFiles_, DEFAULT_ENCODING); } public Javancss(List<File> vJavaSourceFiles_, String encoding_) { setEncoding(encoding_); _vJavaSourceFiles = vJavaSourceFiles_; _measureRoot(); } private void _measureRoot() throws Error { try { _measureRoot(newReader(System.in)); } catch (Throwable pThrowable) { log.fine("Javancss._measureRoot().e: " + pThrowable); pThrowable.printStackTrace(System.err); } } public Javancss(File sJavaSourceFile_) { this(sJavaSourceFile_, DEFAULT_ENCODING); } public Javancss(File sJavaSourceFile_, String encoding_) { log.fine("Javancss.<init>(String).sJavaSourceFile_: " + sJavaSourceFile_); setEncoding(encoding_); _sErrorMessage = null; _vJavaSourceFiles = new ArrayList<File>(); _vJavaSourceFiles.add(sJavaSourceFile_); _measureRoot(); } /** * Only way to create object that does not immediately * start to parse. */ public Javancss() { _sErrorMessage = null; _thrwError = null; } public boolean parseImports() { if (_sJavaSourceFile == null) { log.fine("Javancss.parseImports().NO_FILE"); return true; } Reader reader = createSourceReader(_sJavaSourceFile); if (reader == null) { log.fine("Javancss.parseImports().NO_DIS"); return true; } try { log.fine("Javancss.parseImports().START_PARSING"); if (!log.isLoggable(Level.FINE)) { _pJavaParser = new JavaParser(reader); } else { _pJavaParser = new JavaParserDebug(reader); } _pJavaParser.parseImportUnit(); _vImports = _pJavaParser.getImports(); _aoPackage = _pJavaParser.getPackageObjects(); log.fine("Javancss.parseImports().END_PARSING"); } catch (Exception pParseException) { log.fine("Javancss.parseImports().PARSE_EXCEPTION"); if (_sErrorMessage == null) { _sErrorMessage = ""; } _sErrorMessage += "ParseException in STDIN"; if (_pJavaParser != null) { _sErrorMessage += "\nLast useful checkpoint: \"" + _pJavaParser.getLastFunction() + "\"\n"; } _sErrorMessage += pParseException.getMessage() + "\n"; _thrwError = pParseException; return true; } catch (Error pTokenMgrError) { log.fine("Javancss.parseImports().TOKEN_ERROR"); if (_sErrorMessage == null) { _sErrorMessage = ""; } _sErrorMessage += "TokenMgrError in STDIN\n"; _sErrorMessage += pTokenMgrError.getMessage() + "\n"; _thrwError = pTokenMgrError; return true; } return false; } public void setSourceFile(File javaSourceFile_) { _sJavaSourceFile = javaSourceFile_; _vJavaSourceFiles = new ArrayList<File>(); _vJavaSourceFiles.add(javaSourceFile_); } public Javancss(Reader reader) { this(reader, DEFAULT_ENCODING); } public Javancss(Reader reader, String encoding_) { setEncoding(encoding_); try { _measureRoot(reader); } catch (Throwable pThrowable) { log.fine("Javancss.<init>(Reader).e: " + pThrowable); pThrowable.printStackTrace(System.err); } } /** * recursively adds *.java files * @param dir the base directory to search * @param v the list of file to add found files to */ private static void _addJavaFiles(File dir, List<File> v) { File[] files = dir.listFiles(); if (files == null || files.length == 0) { return; } for (File file : files) { if (file.isDirectory()) { // Recurse!!! _addJavaFiles(file, v); } else { if (file.getName().endsWith(".java")) { v.add(file); } } } } private List<File> findFiles(List<String> filenames, boolean recursive) throws IOException { if (log.isLoggable(Level.FINE)) { log.fine("filenames: " + filenames); } if (filenames.size() == 0) { if (recursive) { // If no files then add current directory! filenames.add("."); } else { return null; } } Set<String> _processedAtFiles = new HashSet<String>(); List<File> newFiles = new ArrayList<File>(); for (String filename : filenames) { // if the file specifies other files... if (filename.startsWith("@")) { filename = filename.substring(1); if (filename.length() > 1) { filename = normalizeFileName(filename); if (_processedAtFiles.add(filename)) { String sJavaSourceFileNames = null; try { sJavaSourceFileNames = readFile(filename); } catch (IOException pIOException) { _sErrorMessage = "File Read Error: " + filename; _thrwError = pIOException; throw pIOException; } String[] vTheseJavaSourceFiles = sJavaSourceFileNames.split("\n"); for (String name : vTheseJavaSourceFiles) { newFiles.add(new File(name)); } } } } else { filename = normalizeFileName(filename); File file = new File(filename); if (file.isDirectory()) { _addJavaFiles(file, newFiles); } else { newFiles.add(file); } } } if (log.isLoggable(Level.FINE)) { log.fine("resolved filenames: " + newFiles); } return newFiles; } /** * @deprecated use Javancss(String[]) instead, since the sRcsHeader_ parameter is not useful */ @Deprecated public Javancss(String[] args, String sRcsHeader_) throws IOException { this(args); } /** * This is the constructor used in the main routine in * javancss.Main. * Other constructors might be helpful to use Javancss out * of other programs. */ public Javancss(String[] args) throws IOException { Options options = new Options(); options.addOption(OptionBuilder.create("help")); options.addOption(OptionBuilder.create("version")); options.addOption(OptionBuilder.create("debug")); options.addOption(OptionBuilder.withDescription("Counts the program NCSS (default).").create("ncss")); options.addOption( OptionBuilder.withDescription("Assembles a statistic on package level.").create("package")); options.addOption(OptionBuilder.withDescription("Counts the object NCSS.").create("object")); options.addOption(OptionBuilder.withDescription("Counts the function NCSS.").create("function")); options.addOption(OptionBuilder.withDescription("The same as '-function -object -package'.").create("all")); options.addOption(OptionBuilder .withDescription("Opens a GUI to present the '-all' output in tabbed panels.").create("gui")); options.addOption(OptionBuilder.withDescription("Output in XML format.").create("xml")); options.addOption(OptionBuilder.withDescription("Output file name. By default output goes to standard out.") .create("out")); options.addOption(OptionBuilder.withDescription("Recurse to subdirs.").create("recursive")); options.addOption(OptionBuilder .withDescription("Encoding used while reading source files (default: platform encoding).").hasArg() .create("encoding")); CommandLine cl; try { cl = new DefaultParser().parse(options, args); } catch (ParseException e) { System.err.println("javancss: " + e.getMessage()); System.err.println("Try `javancss -help' for more information."); return; } if (cl.hasOption("help")) { HelpFormatter helpFormatter = new HelpFormatter(); helpFormatter.printHelp("javancss [options] @srcfiles.txt | *.java | <directory> | <stdin>", options); return; } if (cl.hasOption("version")) { System.out.println( "JavaNCSS " + getClass().getPackage().getSpecificationVersion() + " by Chr. Clemens Lee & co"); return; } if (cl.hasOption("debug")) { log.setLevel(Level.FINE); } setEncoding(cl.getOptionValue("encoding")); setXML(cl.hasOption("xml")); // the arguments (the files) to be processed _vJavaSourceFiles = findFiles(cl.getArgList(), cl.hasOption("recursive")); if (cl.hasOption("gui")) { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception e) { } JavancssFrame pJavancssFrame = new JavancssFrame(cl.getArgList()); /* final Thread pThread = Thread.currentThread(); */ pJavancssFrame.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent event) { setExit(); } }); pJavancssFrame.setVisible(true); try { _measureRoot(newReader(System.in)); } catch (Throwable pThrowable) { // shouldn't we print something here? // error details have been written into lastError } pJavancssFrame.showJavancss(this); pJavancssFrame.setSelectedTab(JavancssFrame.S_PACKAGES); return; } // this initiates the measurement try { _measureRoot(newReader(System.in)); } catch (Throwable pThrowable) { log.fine("Javancss.<init>(String[]).e: " + pThrowable); pThrowable.printStackTrace(System.err); } if (getLastErrorMessage() != null) { System.err.println(getLastErrorMessage() + "\n"); if (getNcss() <= 0) { return; } } String sOutputFile = cl.getOptionValue("out"); OutputStream out = System.out; if (sOutputFile != null) { try { out = new FileOutputStream(normalizeFileName(sOutputFile)); } catch (Exception exception) { System.err.println("Error opening output file '" + sOutputFile + "': " + exception.getMessage()); sOutputFile = null; } } // TODO: encoding configuration support for result output final PrintWriter pw = useXML() ? new PrintWriter(new OutputStreamWriter(out, "UTF-8")) : new PrintWriter(out); try { format(pw, cl.hasOption("package"), cl.hasOption("object"), cl.hasOption("function"), cl.hasOption("all")); } finally { if (sOutputFile != null) { pw.close(); } else { // stdout is used: don't close but ensure everything is flushed pw.flush(); } } } private void format(PrintWriter pw, boolean packages, boolean object, boolean function, boolean all) throws IOException { printStart(pw); boolean bNoNCSS = false; if (packages || all) { printPackageNcss(pw); bNoNCSS = true; } if (object || all) { if (bNoNCSS) { pw.println(); } printObjectNcss(pw); bNoNCSS = true; } if (function || all) { if (bNoNCSS) { pw.println(); } printFunctionNcss(pw); bNoNCSS = true; } if (!bNoNCSS) { printJavaNcss(pw); } printEnd(pw); } public int getNcss() { return _ncss; } public int getLOC() { return _loc; } public int getJvdc() { return _pJavaParser.getJvdc(); } /** * JDCL stands for javadoc comment lines (while jvdc stands * for number of javadoc comments). */ public int getJdcl() { return JavaParserTokenManager._iFormalComments; } public int getSl() { return JavaParserTokenManager._iSingleComments; } public int getMl() { return JavaParserTokenManager._iMultiComments; } public List<FunctionMetric> getFunctionMetrics() { return _vFunctionMetrics; } public List<ObjectMetric> getObjectMetrics() { return _vObjectMetrics; } /** * Returns list of packages in the form * PackageMetric objects. */ public List<PackageMetric> getPackageMetrics() { return _vPackageMetrics; } public String getLastErrorMessage() { return _sErrorMessage; } public Throwable getLastError() { return _thrwError; } public void setExit() { exit = true; } private boolean _bXML = false; public void setXML(boolean bXML) { _bXML = bXML; } public boolean useXML() { return _bXML; } public Formatter getFormatter() { if (useXML()) { return new XmlFormatter(this); } return new AsciiFormatter(this); } public String getEncoding() { return encoding; } public void setEncoding(String encoding) { this.encoding = encoding; } private Reader newReader(InputStream stream) throws UnsupportedEncodingException { return (encoding == null) ? new InputStreamReader(stream) : new InputStreamReader(stream, encoding); } private Reader newReader(File file) throws FileNotFoundException, UnsupportedEncodingException { return newReader(new FileInputStream(file)); } private String normalizeFileName(String filename) { String userdir = (String) System.getProperties().get("user.dir"); filename = filename.trim(); if (filename.length() == 0 || filename.equals(".")) { filename = userdir; } else if (!new File(filename).isAbsolute()) { filename = new File(userdir, filename).getPath(); } try { return new File(filename).getCanonicalPath(); } catch (IOException e) { return filename; } } private String readFile(String filename) throws IOException { StringBuilder content = new StringBuilder(100000); BufferedReader in = null; try { in = new BufferedReader(new FileReader(filename)); String line; while ((line = in.readLine()) != null) { content.append(line).append('\n'); } } finally { if (in != null) { in.close(); } } return content.toString(); } }