1 /* 2 * Copyright (C) 2018 The Android Open Source Project 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 */ 16 17 package com.android.dialer.commandline; 18 19 import android.support.annotation.Nullable; 20 import com.android.dialer.commandline.Command.IllegalCommandLineArgumentException; 21 import com.google.auto.value.AutoValue; 22 import com.google.common.collect.ImmutableList; 23 import com.google.common.collect.ImmutableMap; 24 import com.google.common.collect.Iterators; 25 import com.google.common.collect.PeekingIterator; 26 import com.google.common.collect.UnmodifiableIterator; 27 28 /** 29 * Parses command line arguments into optional flags (--foo, --key=value, --key value) and required 30 * positionals (which must be passed in order). Flags must start with "--" and are always before 31 * positionals. If flags are used "--" must be placed before positionals. 32 * 33 * <p>--flag will be interpreted as --flag=true, and --noflag as --flag=false 34 * 35 * <p>Grammar:<br> 36 * dialer-cmd.py <cmd> <args><br> 37 * <args> = (<flags> -- <positionals>) | <positionals><br> 38 * <flags> = "no"?<name>(<separator><value>)?<br> 39 * <separator> = " " | "=" 40 */ 41 @AutoValue 42 public abstract class Arguments { 43 44 public static final Arguments EMPTY = 45 new AutoValue_Arguments(ImmutableMap.of(), ImmutableList.of()); 46 getFlags()47 public abstract ImmutableMap<String, String> getFlags(); 48 getPositionals()49 public abstract ImmutableList<String> getPositionals(); 50 51 /** 52 * Return the positional at {@code position}. Throw {@link IllegalCommandLineArgumentException} if 53 * it is absent and reports to the user {@code name} is expected. 54 */ expectPositional(int position, String name)55 public String expectPositional(int position, String name) 56 throws IllegalCommandLineArgumentException { 57 if (getPositionals().size() <= position) { 58 throw new IllegalCommandLineArgumentException(name + " expected"); 59 } 60 return getPositionals().get(position); 61 } 62 getBoolean(String flag, boolean defaultValue)63 public Boolean getBoolean(String flag, boolean defaultValue) 64 throws IllegalCommandLineArgumentException { 65 if (!getFlags().containsKey(flag)) { 66 return defaultValue; 67 } 68 switch (getFlags().get(flag)) { 69 case "true": 70 return true; 71 case "false": 72 return false; 73 default: 74 throw new IllegalCommandLineArgumentException("boolean value expected for " + flag); 75 } 76 } 77 parse(@ullable String[] rawArguments)78 public static Arguments parse(@Nullable String[] rawArguments) 79 throws IllegalCommandLineArgumentException { 80 if (rawArguments == null) { 81 return EMPTY; 82 } 83 return parse(Iterators.forArray(rawArguments)); 84 } 85 parse(Iterable<String> rawArguments)86 public static Arguments parse(Iterable<String> rawArguments) 87 throws IllegalCommandLineArgumentException { 88 return parse(Iterators.unmodifiableIterator(rawArguments.iterator())); 89 } 90 parse(UnmodifiableIterator<String> iterator)91 public static Arguments parse(UnmodifiableIterator<String> iterator) 92 throws IllegalCommandLineArgumentException { 93 PeekingIterator<String> peekingIterator = Iterators.peekingIterator(iterator); 94 ImmutableMap<String, String> flags = parseFlags(peekingIterator); 95 ImmutableList<String> positionals = parsePositionals(peekingIterator); 96 97 return new AutoValue_Arguments(flags, positionals); 98 } 99 parseFlags(PeekingIterator<String> iterator)100 private static ImmutableMap<String, String> parseFlags(PeekingIterator<String> iterator) 101 throws IllegalCommandLineArgumentException { 102 ImmutableMap.Builder<String, String> flags = ImmutableMap.builder(); 103 if (!iterator.hasNext()) { 104 return flags.build(); 105 } 106 if (!iterator.peek().startsWith("--")) { 107 return flags.build(); 108 } 109 110 while (iterator.hasNext()) { 111 String peek = iterator.peek(); 112 if (peek.equals("--")) { 113 iterator.next(); 114 return flags.build(); 115 } 116 if (peek.startsWith("--")) { 117 String key = iterator.next().substring(2); 118 String value; 119 if (iterator.hasNext() && !iterator.peek().startsWith("--")) { 120 value = iterator.next(); 121 } else if (key.contains("=")) { 122 String[] entry = key.split("=", 2); 123 key = entry[0]; 124 value = entry[1]; 125 } else if (key.startsWith("no")) { 126 key = key.substring(2); 127 value = "false"; 128 } else { 129 value = "true"; 130 } 131 flags.put(key, value); 132 } else { 133 throw new IllegalCommandLineArgumentException("flag or '--' expected"); 134 } 135 } 136 return flags.build(); 137 } 138 parsePositionals(PeekingIterator<String> iterator)139 private static ImmutableList<String> parsePositionals(PeekingIterator<String> iterator) { 140 ImmutableList.Builder<String> positionals = ImmutableList.builder(); 141 positionals.addAll(iterator); 142 return positionals.build(); 143 } 144 } 145