callgraph/src/main/java/org/netbeans/lib/callgraph/Arguments.java
author Tim Boudreau <tboudreau@netbeans.org>
Sat, 03 Sep 2016 02:41:36 -0400
changeset 18374 04a79821e760
parent 18372 25e1d840480b
child 18400 c87c223efe6a
permissions -rw-r--r--
Eliminate duplicates in graph files
tboudreau@18372
     1
/*
tboudreau@18301
     2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
tboudreau@18301
     3
 *
tboudreau@18301
     4
 * Copyright (C) 1997-2015 Oracle and/or its affiliates. All rights reserved.
tboudreau@18301
     5
 *
tboudreau@18301
     6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
tboudreau@18301
     7
 * Other names may be trademarks of their respective owners.
tboudreau@18301
     8
 *
tboudreau@18301
     9
 * The contents of this file are subject to the terms of either the GNU
tboudreau@18301
    10
 * General Public License Version 2 only ("GPL") or the Common
tboudreau@18301
    11
 * Development and Distribution License("CDDL") (collectively, the
tboudreau@18301
    12
 * "License"). You may not use this file except in compliance with the
tboudreau@18301
    13
 * License. You can obtain a copy of the License at
tboudreau@18301
    14
 * http://www.netbeans.org/cddl-gplv2.html
tboudreau@18301
    15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
tboudreau@18301
    16
 * specific language governing permissions and limitations under the
tboudreau@18301
    17
 * License.  When distributing the software, include this License Header
tboudreau@18301
    18
 * Notice in each file and include the License file at
tboudreau@18301
    19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
tboudreau@18301
    20
 * particular file as subject to the "Classpath" exception as provided
tboudreau@18301
    21
 * by Oracle in the GPL Version 2 section of the License file that
tboudreau@18301
    22
 * accompanied this code. If applicable, add the following below the
tboudreau@18301
    23
 * License Header, with the fields enclosed by brackets [] replaced by
tboudreau@18301
    24
 * your own identifying information:
tboudreau@18301
    25
 * "Portions Copyrighted [year] [name of copyright owner]"
tboudreau@18301
    26
 *
tboudreau@18301
    27
 * Contributor(s):
tboudreau@18301
    28
 *
tboudreau@18301
    29
 * The Original Software is NetBeans. The Initial Developer of the Original
tboudreau@18301
    30
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
tboudreau@18301
    31
 * Microsystems, Inc. All Rights Reserved.
tboudreau@18301
    32
 *
tboudreau@18301
    33
 * If you wish your version of this file to be governed by only the CDDL
tboudreau@18301
    34
 * or only the GPL Version 2, indicate your decision by adding
tboudreau@18301
    35
 * "[Contributor] elects to include this software in this distribution
tboudreau@18301
    36
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
tboudreau@18301
    37
 * single choice of license, a recipient has the option to distribute
tboudreau@18301
    38
 * your version of this file under either the CDDL, the GPL Version 2 or
tboudreau@18301
    39
 * to extend the choice of license to its licensees as provided above.
tboudreau@18301
    40
 * However, if you add GPL Version 2 code and therefore, elected the GPL
tboudreau@18301
    41
 * Version 2 license, then the option applies only if the new code is
tboudreau@18301
    42
 * made subject to such option by the copyright holder.
tboudreau@18301
    43
 */
tboudreau@18301
    44
package org.netbeans.lib.callgraph;
tboudreau@18301
    45
tboudreau@18301
    46
import java.io.File;
tboudreau@18372
    47
import java.io.IOException;
tboudreau@18301
    48
import java.util.Collections;
tboudreau@18301
    49
import java.util.HashSet;
tboudreau@18301
    50
import java.util.Iterator;
tboudreau@18301
    51
import java.util.LinkedList;
tboudreau@18301
    52
import java.util.List;
tboudreau@18301
    53
import java.util.Set;
tboudreau@18301
    54
tboudreau@18301
    55
/**
tboudreau@18301
    56
 * Parses and validates command-line arguments.
tboudreau@18301
    57
 *
tboudreau@18301
    58
 * @author Tim Boudreau
tboudreau@18301
    59
 */
tboudreau@18301
    60
final class Arguments implements CallgraphControl {
tboudreau@18301
    61
tboudreau@18301
    62
    private Set<File> folders = new HashSet<>();
tboudreau@18301
    63
    private static final Command[] commands = new Command[]{
tboudreau@18301
    64
        new NoSelfReferencesCommand(),
tboudreau@18301
    65
        new ShortNamesCommand(),
tboudreau@18374
    66
        new ExtendedPropertiesCommand(),
tboudreau@18374
    67
        new AntCommand(),
tboudreau@18301
    68
        new MavenCommand(),
tboudreau@18372
    69
        new GradleCommand(),
tboudreau@18372
    70
        new IgnoreCommand(),
tboudreau@18301
    71
        new OutfileCommand(),
tboudreau@18301
    72
        new ExcludeCommand(),
tboudreau@18301
    73
        new PackageGraphFileCommand(),
tboudreau@18301
    74
        new ClassGraphFileCommand(),
tboudreau@18301
    75
        new OmitAbstractCommand(),
tboudreau@18301
    76
        new DisableEightBitStringsCommand(),
tboudreau@18301
    77
        new OmitAbstractCommand(),
tboudreau@18301
    78
        new QuietCommand(),
tboudreau@18301
    79
        new ReverseCommand(),
tboudreau@18374
    80
        new AggressiveCommand(),
tboudreau@18301
    81
        new VerboseCommand()
tboudreau@18301
    82
    };
tboudreau@18301
    83
    private boolean noSelfReferences = false;
tboudreau@18301
    84
    private boolean shortNames = false;
tboudreau@18301
    85
    private boolean maven = false;
tboudreau@18372
    86
    private boolean gradle = false;
tboudreau@18374
    87
    private boolean ant = false;
tboudreau@18374
    88
    private boolean xprop = false;
tboudreau@18374
    89
    private boolean aggressive = false;
tboudreau@18301
    90
    private File outfile;
tboudreau@18301
    91
    private Set<String> exclude = new HashSet<>();
tboudreau@18372
    92
    private Set<String> ignore = new HashSet<>();
tboudreau@18301
    93
    private boolean quiet;
tboudreau@18301
    94
    private File classGraphFile;
tboudreau@18301
    95
    private File packageGraphFile;
tboudreau@18301
    96
    private boolean verbose;
tboudreau@18301
    97
    private boolean omitAbstract;
tboudreau@18301
    98
    private boolean disableEightBitStrings;
tboudreau@18301
    99
    private boolean reverse;
tboudreau@18301
   100
tboudreau@18372
   101
    Arguments(String... args) throws IOException {
tboudreau@18301
   102
        this(true, args);
tboudreau@18301
   103
    }
tboudreau@18301
   104
tboudreau@18372
   105
    Arguments(boolean abortIfWillNotOutput, String... args) throws IOException {
tboudreau@18301
   106
        List<String> unknownArguments = new LinkedList<>();
tboudreau@18301
   107
        List<String> errors = new LinkedList<>();
tboudreau@18301
   108
        for (int i = 0; i < args.length;) {
tboudreau@18301
   109
            int oldPos = i;
tboudreau@18301
   110
            for (Command c : commands) {
tboudreau@18301
   111
                try {
tboudreau@18301
   112
                    @SuppressWarnings("LeakingThisInConstructor")
tboudreau@18301
   113
                    int increment = c.parse(i, args, this);
tboudreau@18301
   114
                    if (increment > 0) {
tboudreau@18301
   115
                        i += increment;
tboudreau@18301
   116
                        break;
tboudreau@18301
   117
                    }
tboudreau@18301
   118
                } catch (IllegalArgumentException ex) {
tboudreau@18301
   119
                    errors.add(c.name + ": " + ex.getMessage());
tboudreau@18301
   120
                }
tboudreau@18301
   121
            }
tboudreau@18301
   122
            if (oldPos == i) {
tboudreau@18301
   123
                unknownArguments.add(args[i]);
tboudreau@18301
   124
                i++;
tboudreau@18301
   125
            }
tboudreau@18301
   126
        }
tboudreau@18301
   127
        Set<String> folders = new HashSet<>();
tboudreau@18301
   128
        for (String unknown : unknownArguments) {
tboudreau@18301
   129
            if (unknown.startsWith("-")) {
tboudreau@18301
   130
                // bad line switch
tboudreau@18301
   131
                errors.add("Unknown argument '" + unknown + "'");
tboudreau@18301
   132
            } else {
tboudreau@18301
   133
                folders.add(unknown);
tboudreau@18301
   134
            }
tboudreau@18301
   135
        }
tboudreau@18301
   136
        if (folders.isEmpty()) {
tboudreau@18301
   137
            errors.add("No folders of Java sources specified");
tboudreau@18301
   138
        } else {
tboudreau@18301
   139
            for (String folderName : folders) {
tboudreau@18301
   140
                File folder = new File(folderName);
tboudreau@18301
   141
                if (!folder.exists()) {
tboudreau@18301
   142
                    errors.add("Folder does not exist: " + folderName);
tboudreau@18301
   143
                } else if (!folder.isDirectory()) {
tboudreau@18301
   144
                    errors.add("Not a folder: " + folderName);
tboudreau@18301
   145
                } else {
tboudreau@18372
   146
                    this.folders.add(folder.getCanonicalFile());
tboudreau@18301
   147
                }
tboudreau@18301
   148
            }
tboudreau@18301
   149
        }
tboudreau@18374
   150
        if ((maven && gradle) || (ant && gradle) || (ant && maven)) {
tboudreau@18374
   151
            errors.add("--maven, --ant and --gradle are mutually exclusive");
tboudreau@18372
   152
        } else if (maven) {
tboudreau@18301
   153
            findMavenSubfolders(errors);
tboudreau@18372
   154
        } else if (gradle) {
tboudreau@18372
   155
            findGradleSubfolders(errors);
tboudreau@18374
   156
        } else if (ant) {
tboudreau@18374
   157
            findAntSubfolders(errors);
tboudreau@18301
   158
        }
tboudreau@18372
   159
        Set<File> toIgnore = new HashSet<>();
tboudreau@18372
   160
        for (String ig : ignore) {
tboudreau@18372
   161
            File ff = new File(ig);
tboudreau@18372
   162
            if (ff.exists() && ff.isDirectory()) {
tboudreau@18372
   163
                ff = ff.getCanonicalFile();
tboudreau@18372
   164
                for (File f : this.folders()) {
tboudreau@18372
   165
                    File f1 = f.getCanonicalFile();
tboudreau@18372
   166
                    if (f1.equals(ff)) {
tboudreau@18372
   167
                        toIgnore.add(f1);
tboudreau@18372
   168
                    } else {
tboudreau@18372
   169
                        if (f1.getPath().startsWith(ff.getPath())) {
tboudreau@18372
   170
                            toIgnore.add(f1);
tboudreau@18372
   171
                        }
tboudreau@18372
   172
                    }
tboudreau@18372
   173
                }
tboudreau@18372
   174
            } else {
tboudreau@18372
   175
                for (String u : unknownArguments) {
tboudreau@18372
   176
                    if (!u.startsWith("-")) {
tboudreau@18372
   177
                        File f = new File(u);
tboudreau@18372
   178
                        if (f.exists()) {
tboudreau@18372
   179
                            f = f.getCanonicalFile();
tboudreau@18372
   180
                            File maybeIgnore = new File(f, ig);
tboudreau@18372
   181
                            if (maybeIgnore.exists() && maybeIgnore.isDirectory()) {
tboudreau@18372
   182
                                maybeIgnore = maybeIgnore.getCanonicalFile();
tboudreau@18372
   183
                                for (File fld : this.folders()) {
tboudreau@18372
   184
                                    if (fld.equals(maybeIgnore)) {
tboudreau@18372
   185
                                        toIgnore.add(fld);
tboudreau@18372
   186
                                    } else if (fld.getAbsolutePath().startsWith(maybeIgnore.getAbsolutePath())) {
tboudreau@18372
   187
                                        toIgnore.add(fld);
tboudreau@18372
   188
                                    }
tboudreau@18372
   189
                                }
tboudreau@18372
   190
                            }
tboudreau@18372
   191
                        }
tboudreau@18372
   192
                    }
tboudreau@18372
   193
                }
tboudreau@18372
   194
            }
tboudreau@18372
   195
        }
tboudreau@18372
   196
        if (verbose && !toIgnore.isEmpty()) {
tboudreau@18372
   197
            System.err.println("Ignoring the following projects:");
tboudreau@18372
   198
            for (File ti : toIgnore) {
tboudreau@18372
   199
                System.err.println(" - " + ti.getAbsolutePath());
tboudreau@18372
   200
            }
tboudreau@18372
   201
        }
tboudreau@18372
   202
        this.folders.removeAll(toIgnore);
tboudreau@18374
   203
        if (verbose && !this.folders.isEmpty()) {
tboudreau@18374
   204
            System.err.println("Will scan the following source roots:");
tboudreau@18374
   205
            for (File f : folders()) {
tboudreau@18374
   206
                System.err.println("  " + f.getAbsolutePath());
tboudreau@18374
   207
            }
tboudreau@18374
   208
        }
tboudreau@18372
   209
tboudreau@18301
   210
        if (packageGraphFile != null) {
tboudreau@18301
   211
            File parent = packageGraphFile.getParentFile();
tboudreau@18301
   212
            if (!parent.exists() || !parent.isDirectory()) {
tboudreau@18301
   213
                errors.add("Parent folder for package graph output file does not exist: " + parent);
tboudreau@18301
   214
            }
tboudreau@18301
   215
        }
tboudreau@18301
   216
        if (classGraphFile != null) {
tboudreau@18301
   217
            File parent = classGraphFile.getParentFile();
tboudreau@18301
   218
            if (!parent.exists() || !parent.isDirectory()) {
tboudreau@18301
   219
                errors.add("Parent folder for class graph output file does not exist: " + parent);
tboudreau@18301
   220
            }
tboudreau@18301
   221
        }
tboudreau@18301
   222
        if (outfile != null) {
tboudreau@18301
   223
            File parent = outfile.getParentFile();
tboudreau@18301
   224
            if (!parent.exists() || !parent.isDirectory()) {
tboudreau@18301
   225
                errors.add("Parent folder for output file does not exist: " + parent);
tboudreau@18301
   226
            }
tboudreau@18301
   227
        } else if (abortIfWillNotOutput && quiet && packageGraphFile == null && classGraphFile == null) {
tboudreau@18301
   228
            errors.add("-q or --quiet specified, but no output file specified - would not produce any output at all");
tboudreau@18301
   229
        }
tboudreau@18301
   230
        // XXX check if any folders are children of each other?
tboudreau@18301
   231
        if (!errors.isEmpty()) {
tboudreau@18301
   232
            throw new InvalidArgumentsException(help(errors), errors);
tboudreau@18301
   233
        }
tboudreau@18301
   234
    }
tboudreau@18301
   235
tboudreau@18301
   236
    private void findMavenSubfolders(List<String> errors) {
tboudreau@18301
   237
        Set<File> flds = new HashSet<>(this.folders);
tboudreau@18301
   238
        this.folders.clear();
tboudreau@18301
   239
        for (File f : flds) {
tboudreau@18301
   240
            recurseSubfoldersLookingForMavenProjects(f);
tboudreau@18301
   241
        }
tboudreau@18301
   242
        if (this.folders.isEmpty()) {
tboudreau@18301
   243
            errors.add("Did not find any maven projects (looked for pom.xml and src/main/java in all subfolders of folder list)");
tboudreau@18301
   244
        }
tboudreau@18301
   245
    }
tboudreau@18301
   246
tboudreau@18301
   247
    private void recurseSubfoldersLookingForMavenProjects(File file) {
tboudreau@18301
   248
        if (file.isDirectory()) {
tboudreau@18301
   249
            if (hasPom(file)) {
tboudreau@18301
   250
                File sources = srcMainJavaFolder(file);
tboudreau@18301
   251
                if (sources != null) {
tboudreau@18301
   252
                    this.folders.add(sources);
tboudreau@18301
   253
                }
tboudreau@18301
   254
            }
tboudreau@18301
   255
            for (File child : file.listFiles()) {
tboudreau@18301
   256
                recurseSubfoldersLookingForMavenProjects(child);
tboudreau@18301
   257
            }
tboudreau@18301
   258
        }
tboudreau@18301
   259
    }
tboudreau@18301
   260
tboudreau@18301
   261
    private boolean hasPom(File fld) {
tboudreau@18301
   262
        File pom = new File(fld, "pom.xml");
tboudreau@18301
   263
        return pom.exists() && pom.isFile() && pom.canRead();
tboudreau@18301
   264
    }
tboudreau@18301
   265
tboudreau@18374
   266
    void findAntSubfolders(List<String> errors) {
tboudreau@18374
   267
        Set<File> flds = new HashSet<>(this.folders);
tboudreau@18374
   268
        this.folders.clear();
tboudreau@18374
   269
        for (File f : flds) {
tboudreau@18374
   270
            recurseSubfoldersLookingForAntProjects(f);
tboudreau@18374
   271
        }
tboudreau@18374
   272
        if (this.folders.isEmpty()) {
tboudreau@18374
   273
            errors.add("Did not find any ant projects (looked for build.xml and src/ in all subfolders of folder list)");
tboudreau@18374
   274
        }
tboudreau@18374
   275
    }
tboudreau@18374
   276
tboudreau@18374
   277
    private void recurseSubfoldersLookingForAntProjects(File file) {
tboudreau@18374
   278
        if (file.isDirectory()) {
tboudreau@18374
   279
            if (hasBuildXml(file)) {
tboudreau@18374
   280
                File sources = new File(file, "src");
tboudreau@18374
   281
                if (sources.exists() && sources.isDirectory()) {
tboudreau@18374
   282
                    this.folders.add(sources);
tboudreau@18374
   283
                }
tboudreau@18374
   284
            }
tboudreau@18374
   285
            for (File child : file.listFiles()) {
tboudreau@18374
   286
                recurseSubfoldersLookingForAntProjects(child);
tboudreau@18374
   287
            }
tboudreau@18374
   288
        }
tboudreau@18374
   289
    }
tboudreau@18374
   290
tboudreau@18374
   291
    private boolean hasBuildXml(File fld) {
tboudreau@18374
   292
        File pom = new File(fld, "build.xml");
tboudreau@18374
   293
        return pom.exists() && pom.isFile() && pom.canRead();
tboudreau@18374
   294
    }
tboudreau@18374
   295
tboudreau@18372
   296
    void findGradleSubfolders(List<String> errors) {
tboudreau@18372
   297
        Set<File> flds = new HashSet<>(this.folders);
tboudreau@18372
   298
        this.folders.clear();
tboudreau@18372
   299
        for (File f : flds) {
tboudreau@18372
   300
            recurseSubfoldersLookingForGradleProjects(f);
tboudreau@18372
   301
        }
tboudreau@18372
   302
        if (this.folders.isEmpty()) {
tboudreau@18372
   303
            errors.add("Did not find any gradle projects (looked for *.gradle and src/main/java in all subfolders of folder list)");
tboudreau@18372
   304
        }
tboudreau@18372
   305
    }
tboudreau@18372
   306
tboudreau@18372
   307
    private void recurseSubfoldersLookingForGradleProjects(File file) {
tboudreau@18372
   308
        if (file.isDirectory()) {
tboudreau@18372
   309
            if (hasGradleFile(file)) {
tboudreau@18372
   310
                File sources = srcMainJavaFolder(file);
tboudreau@18372
   311
                if (sources != null) {
tboudreau@18372
   312
                    this.folders.add(sources);
tboudreau@18372
   313
                }
tboudreau@18372
   314
            }
tboudreau@18372
   315
            for (File child : file.listFiles()) {
tboudreau@18372
   316
                recurseSubfoldersLookingForGradleProjects(child);
tboudreau@18372
   317
            }
tboudreau@18372
   318
        }
tboudreau@18372
   319
    }
tboudreau@18372
   320
tboudreau@18372
   321
    boolean hasGradleFile(File fld) {
tboudreau@18372
   322
        return fld.listFiles((File pathname) -> {
tboudreau@18372
   323
            return pathname.getName().endsWith(".gradle") && pathname.isFile() && pathname.canRead();
tboudreau@18372
   324
        }).length > 0;
tboudreau@18372
   325
    }
tboudreau@18372
   326
tboudreau@18372
   327
    public boolean isGradle() {
tboudreau@18372
   328
        return gradle;
tboudreau@18372
   329
    }
tboudreau@18372
   330
tboudreau@18301
   331
    private File srcMainJavaFolder(File projectFolder) {
tboudreau@18301
   332
        File src = new File(projectFolder, "src");
tboudreau@18301
   333
        File main = new File(src, "main");
tboudreau@18301
   334
        File java = new File(main, "java");
tboudreau@18301
   335
        if (java.exists() && java.isDirectory()) {
tboudreau@18301
   336
            return java;
tboudreau@18301
   337
        }
tboudreau@18301
   338
        return null;
tboudreau@18301
   339
    }
tboudreau@18301
   340
tboudreau@18301
   341
    public boolean isVerbose() {
tboudreau@18301
   342
        return verbose;
tboudreau@18301
   343
    }
tboudreau@18301
   344
tboudreau@18301
   345
    public boolean isDisableEightBitStrings() {
tboudreau@18301
   346
        return disableEightBitStrings;
tboudreau@18301
   347
    }
tboudreau@18301
   348
tboudreau@18301
   349
    public boolean isReverse() {
tboudreau@18301
   350
        return reverse;
tboudreau@18301
   351
    }
tboudreau@18301
   352
tboudreau@18301
   353
    @Override
tboudreau@18301
   354
    public File methodGraphFile() {
tboudreau@18301
   355
        return outfile;
tboudreau@18301
   356
    }
tboudreau@18301
   357
tboudreau@18301
   358
    @Override
tboudreau@18301
   359
    public Set<File> folders() {
tboudreau@18301
   360
        return Collections.unmodifiableSet(folders);
tboudreau@18301
   361
    }
tboudreau@18301
   362
tboudreau@18301
   363
    @Override
tboudreau@18301
   364
    public boolean isSelfReferences() {
tboudreau@18301
   365
        return noSelfReferences == false;
tboudreau@18301
   366
    }
tboudreau@18301
   367
tboudreau@18301
   368
    @Override
tboudreau@18301
   369
    public boolean isShortNames() {
tboudreau@18301
   370
        return shortNames;
tboudreau@18301
   371
    }
tboudreau@18301
   372
tboudreau@18301
   373
    @Override
tboudreau@18301
   374
    public boolean isMaven() {
tboudreau@18301
   375
        return maven;
tboudreau@18301
   376
    }
tboudreau@18301
   377
tboudreau@18301
   378
    @Override
tboudreau@18374
   379
    public boolean isAnt() {
tboudreau@18374
   380
        return ant;
tboudreau@18374
   381
    }
tboudreau@18374
   382
tboudreau@18374
   383
    @Override
tboudreau@18374
   384
    public boolean isExtendedProperties() {
tboudreau@18374
   385
        return xprop;
tboudreau@18374
   386
    }
tboudreau@18374
   387
tboudreau@18374
   388
    public boolean isAggressive() {
tboudreau@18374
   389
        return aggressive;
tboudreau@18374
   390
    }
tboudreau@18374
   391
tboudreau@18374
   392
    @Override
tboudreau@18301
   393
    public File classGraphFile() {
tboudreau@18301
   394
        return classGraphFile;
tboudreau@18301
   395
    }
tboudreau@18301
   396
tboudreau@18301
   397
    @Override
tboudreau@18301
   398
    public File packageGraphFile() {
tboudreau@18301
   399
        return packageGraphFile;
tboudreau@18301
   400
    }
tboudreau@18301
   401
tboudreau@18301
   402
    @Override
tboudreau@18301
   403
    public Iterator<File> iterator() {
tboudreau@18301
   404
        return folders().iterator();
tboudreau@18301
   405
    }
tboudreau@18301
   406
tboudreau@18301
   407
    @Override
tboudreau@18301
   408
    public Set<String> excludePrefixes() {
tboudreau@18301
   409
        return Collections.unmodifiableSet(exclude);
tboudreau@18301
   410
    }
tboudreau@18301
   411
tboudreau@18301
   412
    public boolean isOmitAbstract() {
tboudreau@18301
   413
        return omitAbstract;
tboudreau@18301
   414
    }
tboudreau@18301
   415
tboudreau@18301
   416
    public boolean isExcluded(String qname) {
tboudreau@18301
   417
        for (String ex : exclude) {
tboudreau@18301
   418
            if (qname.startsWith(ex)) {
tboudreau@18301
   419
                return true;
tboudreau@18301
   420
            }
tboudreau@18301
   421
        }
tboudreau@18301
   422
        return false;
tboudreau@18301
   423
    }
tboudreau@18301
   424
tboudreau@18301
   425
    @Override
tboudreau@18301
   426
    public boolean isQuiet() {
tboudreau@18301
   427
        return quiet;
tboudreau@18301
   428
    }
tboudreau@18301
   429
tboudreau@18301
   430
    private String help(List<String> errors) {
tboudreau@18301
   431
        StringBuilder sb = new StringBuilder("Callgraph prints a graph of things that call each other in a tree of Java sources,\n"
tboudreau@18301
   432
                + "and can output graphs of what methods / classes / packages (or all of the above) call each other\nwithin that"
tboudreau@18301
   433
                + "source tree."
tboudreau@18301
   434
                + "\n\nUsage:\njava -jar callgraph.jar ");
tboudreau@18301
   435
        for (Command c : commands) {
tboudreau@18301
   436
            if (c.optional) {
tboudreau@18301
   437
                sb.append('[');
tboudreau@18301
   438
            }
tboudreau@18301
   439
            sb.append("--").append(c.name).append(" | -").append(c.shortcut);
tboudreau@18301
   440
            if (c.takesArgument) {
tboudreau@18301
   441
                sb.append(" ").append(c.name);
tboudreau@18301
   442
            }
tboudreau@18301
   443
            if (c.optional) {
tboudreau@18301
   444
                sb.append(']');
tboudreau@18301
   445
            }
tboudreau@18301
   446
            sb.append(' ');
tboudreau@18301
   447
        }
tboudreau@18301
   448
        sb.append("dir1 [dir2 dir3 ...]");
tboudreau@18301
   449
        sb.append('\n');
tboudreau@18301
   450
        for (Command c : commands) {
tboudreau@18301
   451
            sb.append("\n\t");
tboudreau@18301
   452
            sb.append("--").append(c.name).append(" / -").append(c.shortcut).append(" :\t").append(c.help());
tboudreau@18301
   453
        }
tboudreau@18301
   454
        if (!errors.isEmpty()) {
tboudreau@18301
   455
            sb.append("\nErrors:\n");
tboudreau@18301
   456
            for (String err : errors) {
tboudreau@18301
   457
                sb.append('\t').append(err).append('\n');
tboudreau@18301
   458
            }
tboudreau@18301
   459
        }
tboudreau@18301
   460
        return sb.toString();
tboudreau@18301
   461
    }
tboudreau@18301
   462
tboudreau@18301
   463
    private static abstract class Command {
tboudreau@18301
   464
tboudreau@18301
   465
        protected final String name;
tboudreau@18301
   466
        protected final String shortcut;
tboudreau@18301
   467
        protected final boolean optional;
tboudreau@18301
   468
        private final boolean takesArgument;
tboudreau@18301
   469
tboudreau@18301
   470
        Command(String name, String shortcut, boolean optional, boolean takesArgument) {
tboudreau@18301
   471
            this.name = name;
tboudreau@18301
   472
            this.shortcut = shortcut;
tboudreau@18301
   473
            this.optional = optional;
tboudreau@18301
   474
            this.takesArgument = takesArgument;
tboudreau@18301
   475
        }
tboudreau@18301
   476
tboudreau@18301
   477
        public int parse(int position, String[] args, Arguments toSet) {
tboudreau@18301
   478
            boolean match = ("-" + shortcut).equals(args[position])
tboudreau@18301
   479
                    || ("--" + name).equals(args[position]);
tboudreau@18301
   480
            if (!match) {
tboudreau@18301
   481
                return 0;
tboudreau@18301
   482
            }
tboudreau@18301
   483
            return doParse(position, args, toSet);
tboudreau@18301
   484
        }
tboudreau@18301
   485
tboudreau@18301
   486
        protected abstract int doParse(int i, String[] args, Arguments toSet);
tboudreau@18301
   487
tboudreau@18301
   488
        protected abstract String help();
tboudreau@18301
   489
tboudreau@18301
   490
        public String toString() {
tboudreau@18301
   491
            return name;
tboudreau@18301
   492
        }
tboudreau@18301
   493
    }
tboudreau@18301
   494
tboudreau@18301
   495
    private static final class NoSelfReferencesCommand extends Command {
tboudreau@18301
   496
tboudreau@18301
   497
        NoSelfReferencesCommand() {
tboudreau@18301
   498
            super(CMD_NOSELF, "n", true, false);
tboudreau@18301
   499
        }
tboudreau@18301
   500
tboudreau@18301
   501
        @Override
tboudreau@18301
   502
        protected int doParse(int i, String[] args, Arguments toSet) {
tboudreau@18301
   503
            toSet.noSelfReferences = true;
tboudreau@18301
   504
            return 1;
tboudreau@18301
   505
        }
tboudreau@18301
   506
tboudreau@18301
   507
        @Override
tboudreau@18301
   508
        protected String help() {
tboudreau@18301
   509
            return "Hide intra-class calls (i.e. if Foo.bar() calls Foo.baz(), don't include it)";
tboudreau@18301
   510
        }
tboudreau@18301
   511
    }
tboudreau@18301
   512
tboudreau@18301
   513
    private static final class DisableEightBitStringsCommand extends Command {
tboudreau@18301
   514
tboudreau@18301
   515
        DisableEightBitStringsCommand() {
tboudreau@18301
   516
            super(CMD_DISABLE_EIGHT_BIT_STRINGS, "u", true, false);
tboudreau@18301
   517
        }
tboudreau@18301
   518
tboudreau@18301
   519
        @Override
tboudreau@18301
   520
        protected int doParse(int i, String[] args, Arguments toSet) {
tboudreau@18301
   521
            toSet.noSelfReferences = true;
tboudreau@18301
   522
            return 1;
tboudreau@18301
   523
        }
tboudreau@18301
   524
tboudreau@18301
   525
        @Override
tboudreau@18301
   526
        protected String help() {
tboudreau@18374
   527
            return "Disable string memory optimizations - runs faster and supports unicode class names, but may run out of memory";
tboudreau@18301
   528
        }
tboudreau@18301
   529
    }
tboudreau@18301
   530
tboudreau@18301
   531
    private static final class ReverseCommand extends Command {
tboudreau@18301
   532
tboudreau@18301
   533
        ReverseCommand() {
tboudreau@18301
   534
            super(CMD_REVERSE, "r", true, false);
tboudreau@18301
   535
        }
tboudreau@18301
   536
tboudreau@18301
   537
        @Override
tboudreau@18301
   538
        protected int doParse(int i, String[] args, Arguments toSet) {
tboudreau@18301
   539
            toSet.reverse = true;
tboudreau@18301
   540
            return 1;
tboudreau@18301
   541
        }
tboudreau@18301
   542
tboudreau@18301
   543
        @Override
tboudreau@18301
   544
        protected String help() {
tboudreau@18301
   545
            return "Reverse the edges of graph nodes";
tboudreau@18301
   546
        }
tboudreau@18301
   547
    }
tboudreau@18301
   548
tboudreau@18374
   549
    private static final class AggressiveCommand extends Command {
tboudreau@18374
   550
tboudreau@18374
   551
        AggressiveCommand() {
tboudreau@18374
   552
            super(CMD_AGGRESSIVE_MEMORY, "z", true, false);
tboudreau@18374
   553
        }
tboudreau@18374
   554
tboudreau@18374
   555
        @Override
tboudreau@18374
   556
        protected int doParse(int i, String[] args, Arguments toSet) {
tboudreau@18374
   557
            toSet.aggressive = true;
tboudreau@18374
   558
            return 1;
tboudreau@18374
   559
        }
tboudreau@18374
   560
tboudreau@18374
   561
        @Override
tboudreau@18374
   562
        protected String help() {
tboudreau@18374
   563
            return "Aggressively optimize the 8-bit string intern table "
tboudreau@18374
   564
                    + "for large graphs, sacrificing performace for space";
tboudreau@18374
   565
        }
tboudreau@18374
   566
    }
tboudreau@18374
   567
tboudreau@18301
   568
    private static final class ShortNamesCommand extends Command {
tboudreau@18301
   569
tboudreau@18301
   570
        ShortNamesCommand() {
tboudreau@18301
   571
            super(CMD_SIMPLE, "s", true, false);
tboudreau@18301
   572
        }
tboudreau@18301
   573
tboudreau@18301
   574
        @Override
tboudreau@18301
   575
        protected int doParse(int i, String[] args, Arguments toSet) {
tboudreau@18301
   576
            toSet.shortNames = true;
tboudreau@18301
   577
            return 1;
tboudreau@18301
   578
        }
tboudreau@18301
   579
tboudreau@18301
   580
        @Override
tboudreau@18301
   581
        protected String help() {
tboudreau@18301
   582
            return "Use simple class names without the package (may confuse results if two classes have the same name)";
tboudreau@18301
   583
        }
tboudreau@18301
   584
    }
tboudreau@18301
   585
tboudreau@18374
   586
    private static final class ExtendedPropertiesCommand extends Command {
tboudreau@18374
   587
tboudreau@18374
   588
        ExtendedPropertiesCommand() {
tboudreau@18374
   589
            super(CMD_EXTENDED_PROPERTIES, "x", true, false);
tboudreau@18374
   590
        }
tboudreau@18374
   591
tboudreau@18374
   592
        @Override
tboudreau@18374
   593
        protected int doParse(int i, String[] args, Arguments toSet) {
tboudreau@18374
   594
            toSet.xprop = true;
tboudreau@18374
   595
            return 1;
tboudreau@18374
   596
        }
tboudreau@18374
   597
tboudreau@18374
   598
        @Override
tboudreau@18374
   599
        protected String help() {
tboudreau@18374
   600
            return "Find all maven projects that are children of the passed folders, and scan their src/main/java subfolders";
tboudreau@18374
   601
        }
tboudreau@18374
   602
    }
tboudreau@18374
   603
tboudreau@18374
   604
    private static final class AntCommand extends Command {
tboudreau@18374
   605
tboudreau@18374
   606
        AntCommand() {
tboudreau@18374
   607
            super(CMD_ANT, "a", true, false);
tboudreau@18374
   608
        }
tboudreau@18374
   609
tboudreau@18374
   610
        @Override
tboudreau@18374
   611
        protected int doParse(int i, String[] args, Arguments toSet) {
tboudreau@18374
   612
            toSet.ant = true;
tboudreau@18374
   613
            return 1;
tboudreau@18374
   614
        }
tboudreau@18374
   615
tboudreau@18374
   616
        @Override
tboudreau@18374
   617
        protected String help() {
tboudreau@18374
   618
            return "Find all ant projects that are children of the passed folders, and scan their src/ subfolders";
tboudreau@18374
   619
        }
tboudreau@18374
   620
    }
tboudreau@18374
   621
tboudreau@18301
   622
    private static final class MavenCommand extends Command {
tboudreau@18301
   623
tboudreau@18301
   624
        MavenCommand() {
tboudreau@18301
   625
            super(CMD_MAVEN, "m", true, false);
tboudreau@18301
   626
        }
tboudreau@18301
   627
tboudreau@18301
   628
        @Override
tboudreau@18301
   629
        protected int doParse(int i, String[] args, Arguments toSet) {
tboudreau@18301
   630
            toSet.maven = true;
tboudreau@18301
   631
            return 1;
tboudreau@18301
   632
        }
tboudreau@18301
   633
tboudreau@18301
   634
        @Override
tboudreau@18301
   635
        protected String help() {
tboudreau@18301
   636
            return "Find all maven projects that are children of the passed folders, and scan their src/main/java subfolders";
tboudreau@18301
   637
        }
tboudreau@18301
   638
    }
tboudreau@18301
   639
tboudreau@18372
   640
    private static final class GradleCommand extends Command {
tboudreau@18372
   641
tboudreau@18372
   642
        GradleCommand() {
tboudreau@18372
   643
            super(CMD_GRADLE, "g", true, false);
tboudreau@18372
   644
        }
tboudreau@18372
   645
tboudreau@18372
   646
        @Override
tboudreau@18372
   647
        protected int doParse(int i, String[] args, Arguments toSet) {
tboudreau@18372
   648
            toSet.gradle = true;
tboudreau@18372
   649
            return 1;
tboudreau@18372
   650
        }
tboudreau@18372
   651
tboudreau@18372
   652
        @Override
tboudreau@18372
   653
        protected String help() {
tboudreau@18372
   654
            return "Find all gradle projects that are children of the passed folders, and scan their src/main/java subfolders";
tboudreau@18372
   655
        }
tboudreau@18372
   656
    }
tboudreau@18372
   657
tboudreau@18301
   658
    private static final class OmitAbstractCommand extends Command {
tboudreau@18301
   659
tboudreau@18301
   660
        OmitAbstractCommand() {
tboudreau@18301
   661
            super(CMD_OMIT_ABSTRACT, "a", true, false);
tboudreau@18301
   662
        }
tboudreau@18301
   663
tboudreau@18301
   664
        @Override
tboudreau@18301
   665
        protected int doParse(int i, String[] args, Arguments toSet) {
tboudreau@18301
   666
            toSet.omitAbstract = true;
tboudreau@18301
   667
            return 1;
tboudreau@18301
   668
        }
tboudreau@18301
   669
tboudreau@18301
   670
        @Override
tboudreau@18301
   671
        protected String help() {
tboudreau@18301
   672
            return "Don't emit calls to abstract methods";
tboudreau@18301
   673
        }
tboudreau@18301
   674
    }
tboudreau@18301
   675
tboudreau@18301
   676
    private static final class VerboseCommand extends Command {
tboudreau@18301
   677
tboudreau@18301
   678
        VerboseCommand() {
tboudreau@18301
   679
            super(CMD_VERBOSE, "v", true, false);
tboudreau@18301
   680
        }
tboudreau@18301
   681
tboudreau@18301
   682
        @Override
tboudreau@18301
   683
        protected int doParse(int i, String[] args, Arguments toSet) {
tboudreau@18301
   684
            toSet.verbose = true;
tboudreau@18301
   685
            return 1;
tboudreau@18301
   686
        }
tboudreau@18301
   687
tboudreau@18301
   688
        @Override
tboudreau@18301
   689
        protected String help() {
tboudreau@18301
   690
            return "Print output about what is being processed";
tboudreau@18301
   691
        }
tboudreau@18301
   692
    }
tboudreau@18301
   693
tboudreau@18301
   694
    private static final class QuietCommand extends Command {
tboudreau@18301
   695
tboudreau@18301
   696
        QuietCommand() {
tboudreau@18301
   697
            super(CMD_QUIET, "q", true, false);
tboudreau@18301
   698
        }
tboudreau@18301
   699
tboudreau@18301
   700
        @Override
tboudreau@18301
   701
        protected int doParse(int i, String[] args, Arguments toSet) {
tboudreau@18301
   702
            toSet.quiet = true;
tboudreau@18301
   703
            return 1;
tboudreau@18301
   704
        }
tboudreau@18301
   705
tboudreau@18301
   706
        @Override
tboudreau@18301
   707
        protected String help() {
tboudreau@18301
   708
            return "Supress writing the graph to the standard output";
tboudreau@18301
   709
        }
tboudreau@18301
   710
    }
tboudreau@18301
   711
tboudreau@18301
   712
    private static final class OutfileCommand extends Command {
tboudreau@18301
   713
tboudreau@18301
   714
        OutfileCommand() {
tboudreau@18372
   715
            super(CMD_METHODGRAPH, "o", true, true);
tboudreau@18301
   716
        }
tboudreau@18301
   717
tboudreau@18301
   718
        @Override
tboudreau@18301
   719
        protected int doParse(int index, String[] args, Arguments toSet) {
tboudreau@18301
   720
            if (args.length == index + 1) {
tboudreau@18301
   721
                throw new IllegalArgumentException("--outfile or -o present but no output file specified");
tboudreau@18301
   722
            }
tboudreau@18301
   723
            toSet.outfile = new File(args[index + 1]);
tboudreau@18301
   724
            return 2;
tboudreau@18301
   725
        }
tboudreau@18301
   726
tboudreau@18301
   727
        @Override
tboudreau@18301
   728
        protected String help() {
tboudreau@18301
   729
            return "Set the output file for the method call graph";
tboudreau@18301
   730
        }
tboudreau@18301
   731
    }
tboudreau@18301
   732
tboudreau@18301
   733
    private static final class ClassGraphFileCommand extends Command {
tboudreau@18301
   734
tboudreau@18301
   735
        ClassGraphFileCommand() {
tboudreau@18301
   736
            super(CMD_CLASSGRAPH, "c", true, true);
tboudreau@18301
   737
        }
tboudreau@18301
   738
tboudreau@18301
   739
        @Override
tboudreau@18301
   740
        protected int doParse(int index, String[] args, Arguments toSet) {
tboudreau@18301
   741
            if (args.length == index + 1) {
tboudreau@18301
   742
                throw new IllegalArgumentException("--outfile or -o present but no output file specified");
tboudreau@18301
   743
            }
tboudreau@18301
   744
            toSet.classGraphFile = new File(args[index + 1]);
tboudreau@18301
   745
            return 2;
tboudreau@18301
   746
        }
tboudreau@18301
   747
tboudreau@18301
   748
        @Override
tboudreau@18301
   749
        protected String help() {
tboudreau@18301
   750
            return "Set the output file for the class call graph";
tboudreau@18301
   751
        }
tboudreau@18301
   752
    }
tboudreau@18301
   753
tboudreau@18301
   754
    private static final class PackageGraphFileCommand extends Command {
tboudreau@18301
   755
tboudreau@18301
   756
        PackageGraphFileCommand() {
tboudreau@18301
   757
            super(CMD_PACKAGEGRAPH, "p", true, true);
tboudreau@18301
   758
        }
tboudreau@18301
   759
tboudreau@18301
   760
        @Override
tboudreau@18301
   761
        protected int doParse(int index, String[] args, Arguments toSet) {
tboudreau@18301
   762
            if (args.length == index + 1) {
tboudreau@18301
   763
                throw new IllegalArgumentException("--outfile or -o present but no output file specified");
tboudreau@18301
   764
            }
tboudreau@18301
   765
            toSet.packageGraphFile = new File(args[index + 1]);
tboudreau@18301
   766
            return 2;
tboudreau@18301
   767
        }
tboudreau@18301
   768
tboudreau@18301
   769
        @Override
tboudreau@18301
   770
        protected String help() {
tboudreau@18301
   771
            return "Set the output file for the package call graph";
tboudreau@18301
   772
        }
tboudreau@18301
   773
    }
tboudreau@18301
   774
tboudreau@18301
   775
    private static final class ExcludeCommand extends Command {
tboudreau@18301
   776
tboudreau@18301
   777
        ExcludeCommand() {
tboudreau@18301
   778
            super("exclude", "e", true, true);
tboudreau@18301
   779
        }
tboudreau@18301
   780
tboudreau@18301
   781
        @Override
tboudreau@18301
   782
        protected int doParse(int i, String[] args, Arguments toSet) {
tboudreau@18301
   783
            if (args.length == i + 1) {
tboudreau@18301
   784
                throw new IllegalArgumentException("--exclude or -e present but no exclusion list present");
tboudreau@18301
   785
            }
tboudreau@18301
   786
            for (String s : args[i + 1].split(",")) {
tboudreau@18301
   787
                toSet.exclude.add(s);
tboudreau@18301
   788
            }
tboudreau@18301
   789
            return 2;
tboudreau@18301
   790
        }
tboudreau@18301
   791
tboudreau@18301
   792
        @Override
tboudreau@18301
   793
        protected String help() {
tboudreau@18301
   794
            return "Exclude any relationships where the fully qualified class name starts with any pattern in this comma-delimited list of strings, e.g. -e foo.bar,foo.baz";
tboudreau@18301
   795
        }
tboudreau@18301
   796
    }
tboudreau@18301
   797
tboudreau@18372
   798
    private static final class IgnoreCommand extends Command {
tboudreau@18372
   799
tboudreau@18372
   800
        IgnoreCommand() {
tboudreau@18372
   801
            super(CMD_IGNORE, "i", true, true);
tboudreau@18372
   802
        }
tboudreau@18372
   803
tboudreau@18372
   804
        @Override
tboudreau@18372
   805
        protected int doParse(int i, String[] args, Arguments toSet) {
tboudreau@18372
   806
            if (args.length == i + 1) {
tboudreau@18372
   807
                throw new IllegalArgumentException("--exclude or -e present but no exclusion list present");
tboudreau@18372
   808
            }
tboudreau@18372
   809
            for (String s : args[i + 1].split(",")) {
tboudreau@18372
   810
                toSet.ignore.add(s);
tboudreau@18372
   811
            }
tboudreau@18372
   812
            return 2;
tboudreau@18372
   813
        }
tboudreau@18372
   814
tboudreau@18372
   815
        @Override
tboudreau@18372
   816
        protected String help() {
tboudreau@18372
   817
            return "Comma delimited list of folders or subfolders to ignore, absolute or relative to the base directory.";
tboudreau@18372
   818
        }
tboudreau@18372
   819
    }
tboudreau@18372
   820
tboudreau@18301
   821
    static final class InvalidArgumentsException extends IllegalArgumentException {
tboudreau@18301
   822
tboudreau@18301
   823
        private final List<String> errors;
tboudreau@18301
   824
tboudreau@18301
   825
        InvalidArgumentsException(String msg, List<String> errors) {
tboudreau@18301
   826
            super(msg);
tboudreau@18301
   827
            this.errors = errors;
tboudreau@18301
   828
        }
tboudreau@18301
   829
tboudreau@18301
   830
        public List<String> errors() {
tboudreau@18301
   831
            return errors;
tboudreau@18301
   832
        }
tboudreau@18301
   833
tboudreau@18301
   834
        public boolean errorContains(String test) { //for tests
tboudreau@18301
   835
            for (String err : errors) {
tboudreau@18301
   836
                if (err.contains(test)) {
tboudreau@18301
   837
                    return true;
tboudreau@18301
   838
                }
tboudreau@18301
   839
            }
tboudreau@18301
   840
            return false;
tboudreau@18301
   841
        }
tboudreau@18301
   842
    }
tboudreau@18301
   843
}