com.google.googlejavaformat.java.CommandLineOptionsParser.java Source code

Java tutorial

Introduction

Here is the source code for com.google.googlejavaformat.java.CommandLineOptionsParser.java

Source

/*
 * Copyright 2016 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package com.google.googlejavaformat.java;

import static java.nio.charset.StandardCharsets.UTF_8;

import com.google.common.base.CharMatcher;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableRangeSet;
import com.google.common.collect.Range;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/** A parser for {@link CommandLineOptions}. */
final class CommandLineOptionsParser {

    private static final Splitter COMMA_SPLITTER = Splitter.on(',');
    private static final Splitter COLON_SPLITTER = Splitter.on(':');
    private static final Splitter ARG_SPLITTER = Splitter.on(CharMatcher.breakingWhitespace()).omitEmptyStrings()
            .trimResults();

    /** Parses {@link CommandLineOptions}. */
    static CommandLineOptions parse(Iterable<String> options) {
        CommandLineOptions.Builder optionsBuilder = CommandLineOptions.builder();
        List<String> expandedOptions = new ArrayList<>();
        expandParamsFiles(options, expandedOptions);
        Iterator<String> it = expandedOptions.iterator();
        while (it.hasNext()) {
            String option = it.next();
            if (!option.startsWith("-")) {
                optionsBuilder.filesBuilder().add(option).addAll(it);
                break;
            }
            String flag;
            String value;
            int idx = option.indexOf('=');
            if (idx >= 0) {
                flag = option.substring(0, idx);
                value = option.substring(idx + 1, option.length());
            } else {
                flag = option;
                value = null;
            }
            // NOTE: update usage information in UsageException when new flags are added
            switch (flag) {
            case "-i":
            case "-r":
            case "-replace":
            case "--replace":
                optionsBuilder.inPlace(true);
                break;
            case "--lines":
            case "-lines":
            case "--line":
            case "-line":
                parseRangeSet(optionsBuilder.linesBuilder(), getValue(flag, it, value));
                break;
            case "--offset":
            case "-offset":
                optionsBuilder.addOffset(parseInteger(it, flag, value));
                break;
            case "--length":
            case "-length":
                optionsBuilder.addLength(parseInteger(it, flag, value));
                break;
            case "--aosp":
            case "-aosp":
            case "-a":
                optionsBuilder.aosp(true);
                break;
            case "--version":
            case "-version":
            case "-v":
                optionsBuilder.version(true);
                break;
            case "--help":
            case "-help":
            case "-h":
                optionsBuilder.help(true);
                break;
            case "--fix-imports-only":
                optionsBuilder.fixImportsOnly(true);
                break;
            case "--skip-sorting-imports":
                optionsBuilder.sortImports(false);
                break;
            case "--skip-removing-unused-imports":
                optionsBuilder.removeUnusedImports(false);
                break;
            case "--skip-reflowing-long-strings":
                optionsBuilder.reflowLongStrings(false);
                break;
            case "-":
                optionsBuilder.stdin(true);
                break;
            case "-n":
            case "--dry-run":
                optionsBuilder.dryRun(true);
                break;
            case "--set-exit-if-changed":
                optionsBuilder.setExitIfChanged(true);
                break;
            case "-assume-filename":
            case "--assume-filename":
                optionsBuilder.assumeFilename(getValue(flag, it, value));
                break;
            default:
                throw new IllegalArgumentException("unexpected flag: " + flag);
            }
        }
        return optionsBuilder.build();
    }

    private static Integer parseInteger(Iterator<String> it, String flag, String value) {
        try {
            return Integer.valueOf(getValue(flag, it, value));
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException(String.format("invalid integer value for %s: %s", flag, value), e);
        }
    }

    private static String getValue(String flag, Iterator<String> it, String value) {
        if (value != null) {
            return value;
        }
        if (!it.hasNext()) {
            throw new IllegalArgumentException("required value was not provided for: " + flag);
        }
        return it.next();
    }

    /**
     * Parse multiple --lines flags, like {"1:12,14,20:36", "40:45,50"}. Multiple ranges can be given
     * with multiple --lines flags or separated by commas. A single line can be set by a single
     * number. Line numbers are {@code 1}-based, but are converted to the {@code 0}-based numbering
     * used internally by google-java-format.
     */
    private static void parseRangeSet(ImmutableRangeSet.Builder<Integer> result, String ranges) {
        for (String range : COMMA_SPLITTER.split(ranges)) {
            result.add(parseRange(range));
        }
    }

    /**
     * Parse a range, as in "1:12" or "42". Line numbers provided are {@code 1}-based, but are
     * converted here to {@code 0}-based.
     */
    private static Range<Integer> parseRange(String arg) {
        List<String> args = COLON_SPLITTER.splitToList(arg);
        switch (args.size()) {
        case 1:
            int line = Integer.parseInt(args.get(0)) - 1;
            return Range.closedOpen(line, line + 1);
        case 2:
            int line0 = Integer.parseInt(args.get(0)) - 1;
            int line1 = Integer.parseInt(args.get(1)) - 1;
            return Range.closedOpen(line0, line1 + 1);
        default:
            throw new IllegalArgumentException(arg);
        }
    }

    /**
     * Pre-processes an argument list, expanding arguments of the form {@code @filename} by reading
     * the content of the file and appending whitespace-delimited options to {@code arguments}.
     */
    private static void expandParamsFiles(Iterable<String> args, List<String> expanded) {
        for (String arg : args) {
            if (arg.isEmpty()) {
                continue;
            }
            if (!arg.startsWith("@")) {
                expanded.add(arg);
            } else if (arg.startsWith("@@")) {
                expanded.add(arg.substring(1));
            } else {
                Path path = Paths.get(arg.substring(1));
                try {
                    String sequence = new String(Files.readAllBytes(path), UTF_8);
                    expandParamsFiles(ARG_SPLITTER.split(sequence), expanded);
                } catch (IOException e) {
                    throw new UncheckedIOException(path + ": could not read file: " + e.getMessage(), e);
                }
            }
        }
    }
}