Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/test/tools/pack200/pack200-verifier/src/xmlkit/CommandLineParser.java
38867 views
/*1* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/24package xmlkit; // -*- mode: java; indent-tabs-mode: nil -*-2526import java.util.*;27/*28* @author jrose29*/30public class CommandLineParser {3132public CommandLineParser(String optionString) {33setOptionMap(optionString);34}35TreeMap<String, String[]> optionMap;3637public void setOptionMap(String options) {38// Convert options string into optLines dictionary.39TreeMap<String, String[]> optmap = new TreeMap<String, String[]>();40loadOptmap:41for (String optline : options.split("\n")) {42String[] words = optline.split("\\p{Space}+");43if (words.length == 0) {44continue loadOptmap;45}46String opt = words[0];47words[0] = ""; // initial word is not a spec48if (opt.length() == 0 && words.length >= 1) {49opt = words[1]; // initial "word" is empty due to leading ' '50words[1] = "";51}52if (opt.length() == 0) {53continue loadOptmap;54}55String[] prevWords = optmap.put(opt, words);56if (prevWords != null) {57throw new RuntimeException("duplicate option: "58+ optline.trim());59}60}61optionMap = optmap;62}6364public String getOptionMap() {65TreeMap<String, String[]> optmap = optionMap;66StringBuffer sb = new StringBuffer();67for (String opt : optmap.keySet()) {68sb.append(opt);69for (String spec : optmap.get(opt)) {70sb.append(' ').append(spec);71}72sb.append('\n');73}74return sb.toString();75}7677/**78* Remove a set of command-line options from args,79* storing them in the properties map in a canonicalized form.80*/81public String parse(List<String> args, Map<String, String> properties) {82//System.out.println(args+" // "+properties);8384String resultString = null;85TreeMap<String, String[]> optmap = optionMap;8687// State machine for parsing a command line.88ListIterator<String> argp = args.listIterator();89ListIterator<String> pbp = new ArrayList<String>().listIterator();90doArgs:91for (;;) {92// One trip through this loop per argument.93// Multiple trips per option only if several options per argument.94String arg;95if (pbp.hasPrevious()) {96arg = pbp.previous();97pbp.remove();98} else if (argp.hasNext()) {99arg = argp.next();100} else {101// No more arguments at all.102break doArgs;103}104tryOpt:105for (int optlen = arg.length();; optlen--) {106// One time through this loop for each matching arg prefix.107String opt;108// Match some prefix of the argument to a key in optmap.109findOpt:110for (;;) {111opt = arg.substring(0, optlen);112if (optmap.containsKey(opt)) {113break findOpt;114}115if (optlen == 0) {116break tryOpt;117}118// Decide on a smaller prefix to search for.119SortedMap<String, String[]> pfxmap = optmap.headMap(opt);120// pfxmap.lastKey is no shorter than any prefix in optmap.121int len = pfxmap.isEmpty() ? 0 : pfxmap.lastKey().length();122optlen = Math.min(len, optlen - 1);123opt = arg.substring(0, optlen);124// (Note: We could cut opt down to its common prefix with125// pfxmap.lastKey, but that wouldn't save many cycles.)126}127opt = opt.intern();128assert (arg.startsWith(opt));129assert (opt.length() == optlen);130String val = arg.substring(optlen); // arg == opt+val131132// Execute the option processing specs for this opt.133// If no actions are taken, then look for a shorter prefix.134boolean didAction = false;135boolean isError = false;136137int pbpMark = pbp.nextIndex(); // in case of backtracking138String[] specs = optmap.get(opt);139eachSpec:140for (String spec : specs) {141if (spec.length() == 0) {142continue eachSpec;143}144if (spec.startsWith("#")) {145break eachSpec;146}147int sidx = 0;148char specop = spec.charAt(sidx++);149150// Deal with '+'/'*' prefixes (spec conditions).151boolean ok;152switch (specop) {153case '+':154// + means we want an non-empty val suffix.155ok = (val.length() != 0);156specop = spec.charAt(sidx++);157break;158case '*':159// * means we accept empty or non-empty160ok = true;161specop = spec.charAt(sidx++);162break;163default:164// No condition prefix means we require an exact165// match, as indicated by an empty val suffix.166ok = (val.length() == 0);167break;168}169if (!ok) {170continue eachSpec;171}172173String specarg = spec.substring(sidx);174switch (specop) {175case '.': // terminate the option sequence176resultString = (specarg.length() != 0) ? specarg.intern() : opt;177break doArgs;178case '?': // abort the option sequence179resultString = (specarg.length() != 0) ? specarg.intern() : arg;180isError = true;181break eachSpec;182case '@': // change the effective opt name183opt = specarg.intern();184break;185case '>': // shift remaining arg val to next arg186pbp.add(specarg + val); // push a new argument187val = "";188break;189case '!': // negation option190String negopt = (specarg.length() != 0) ? specarg.intern() : opt;191properties.remove(negopt);192properties.put(negopt, null); // leave placeholder193didAction = true;194break;195case '$': // normal "boolean" option196String boolval;197if (specarg.length() != 0) {198// If there is a given spec token, store it.199boolval = specarg;200} else {201String old = properties.get(opt);202if (old == null || old.length() == 0) {203boolval = "1";204} else {205// Increment any previous value as a numeral.206boolval = "" + (1 + Integer.parseInt(old));207}208}209properties.put(opt, boolval);210didAction = true;211break;212case '=': // "string" option213case '&': // "collection" option214// Read an option.215boolean append = (specop == '&');216String strval;217if (pbp.hasPrevious()) {218strval = pbp.previous();219pbp.remove();220} else if (argp.hasNext()) {221strval = argp.next();222} else {223resultString = arg + " ?";224isError = true;225break eachSpec;226}227if (append) {228String old = properties.get(opt);229if (old != null) {230// Append new val to old with embedded delim.231String delim = specarg;232if (delim.length() == 0) {233delim = " ";234}235strval = old + specarg + strval;236}237}238properties.put(opt, strval);239didAction = true;240break;241default:242throw new RuntimeException("bad spec for "243+ opt + ": " + spec);244}245}246247// Done processing specs.248if (didAction && !isError) {249continue doArgs;250}251252// The specs should have done something, but did not.253while (pbp.nextIndex() > pbpMark) {254// Remove anything pushed during these specs.255pbp.previous();256pbp.remove();257}258259if (isError) {260throw new IllegalArgumentException(resultString);261}262263if (optlen == 0) {264// We cannot try a shorter matching option.265break tryOpt;266}267}268269// If we come here, there was no matching option.270// So, push back the argument, and return to caller.271pbp.add(arg);272break doArgs;273}274// Report number of arguments consumed.275args.subList(0, argp.nextIndex()).clear();276// Report any unconsumed partial argument.277while (pbp.hasPrevious()) {278args.add(0, pbp.previous());279}280//System.out.println(args+" // "+properties+" -> "+resultString);281return resultString;282}283}284285286