/*
* Copyright © 2014 Cask Data, 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 co.cask.cdap.api.common;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* Utility class to convert String array to Map<String, String>.
*/
public final class RuntimeArguments {
public static final Map<String, String> NO_ARGUMENTS = Collections.emptyMap();
private static final String ASTERISK = "*";
private static final String DOT = ".";
private RuntimeArguments() {
}
/**
* Converts a POSIX compliant program argument array to a String-to-String Map.
* @param args Array of Strings where each element is a POSIX compliant program argument (Ex: "--os=Linux" ).
* @return Map of argument Keys and Values (Ex: Key = "os" and Value = "Linux").
*/
public static Map<String, String> fromPosixArray(String[] args) {
Map<String, String> kvMap = new HashMap<>();
for (String arg : args) {
int idx = arg.indexOf('=');
int keyOff = arg.startsWith("--") ? "--".length() : 0;
String key = idx < 0 ? arg.substring(keyOff) : arg.substring(keyOff, idx);
String value = idx < 0 ? "" : arg.substring(idx + 1);
kvMap.put(key, value);
}
return kvMap;
}
/**
* Converts a String-to-String Map to POSIX compliant program argument array.
* @param kvMap Map of argument Keys and Values.
* @return Array of Strings in POSIX compliant format.
*/
public static String[] toPosixArray(Map<String, String> kvMap) {
String[] args = new String[kvMap.size()];
int index = 0;
for (Map.Entry<String, String> kv : kvMap.entrySet()) {
args[index++] = String.format("--%s=%s", kv.getKey(), kv.getValue());
}
return args;
}
/**
* Converts a Iterable of Map.Entry<String, String> to a POSIX compliant program argument array.
* @param iterable of Map.Entry of argument Key and Value.
* @return Array of Strings in POSIX compliant format.
*/
public static String[] toPosixArray(Iterable<Map.Entry<String, String>> iterable) {
Map<String, String> userArgs = new HashMap<>();
for (Map.Entry<String, String> kv : iterable) {
userArgs.put(kv.getKey(), kv.getValue());
}
return toPosixArray(userArgs);
}
/**
* Identifies arguments with a given scope prefix and adds them back without the scope prefix.
*
* 1. An argument can be prefixed by "<scope>.<name>.". e.g. mapreduce.myMapReduce.read.timeout=30. In this case
* the MapReduce program named 'myMapReduce' will receive two arguments - mapreduce.myMapReduce.read.timeout=30 and
* read.timeout=30. However MapReduce programs other than 'myMapReduce' will receive only one argument -
* mapreduce.myMapReduce.read.timeout=30
* 2. An argument can be prefixed by "<scope>.*.". e.g. mapreduce.*.read.timeout=30. In this case all the
* underlying MapReduce programs will receive the arguments mapreduce.*.read.timeout=30 and read.timeout=30.
* 3. An argument not prefixed with any scope is passed further without any changes. e.g. read.timeout=30
*
* @param scope The type of the scope
* @param name The name of the scope, e.g. "myTable"
* @param arguments the runtime arguments of the enclosing scope
* @return a new map that contains the arguments with and without prefix, never null
*/
public static Map<String, String> extractScope(Scope scope, String name, Map<String, String> arguments) {
if (arguments == null || arguments.isEmpty()) {
return new HashMap<>();
}
String prefix = scope + DOT + name + DOT;
String wildCardPrefix = scope + DOT + ASTERISK + DOT;
Map<String, String> result = new HashMap<>();
result.putAll(arguments);
Map<String, String> prefixMatchedArgs = new HashMap<>();
Map<String, String> wildCardPrefixMatchedArgs = new HashMap<>();
// Group the arguments into different categories based on wild card prefix match, named prefix match or no match
for (Map.Entry<String, String> entry : arguments.entrySet()) {
if (entry.getKey().startsWith(wildCardPrefix)) {
// Argument is prefixed with "<scope>.*."
wildCardPrefixMatchedArgs.put(entry.getKey().substring(wildCardPrefix.length()), entry.getValue());
} else if (entry.getKey().startsWith(prefix)) {
// Argument is prefixed with "<scope>.<name>."
prefixMatchedArgs.put(entry.getKey().substring(prefix.length()), entry.getValue());
}
}
result.putAll(wildCardPrefixMatchedArgs);
result.putAll(prefixMatchedArgs);
return result;
}
/**
* Add a scope prefix to all arguments.
* @param scope The type of the scope
* @param name The name of the scope, e.g. "myTable"
* @param arguments the runtime arguments to be scoped
* @return a map that contains all keys, prefixed with with <scope>.<name>.
*/
public static Map<String, String> addScope(Scope scope, String name, Map<String, String> arguments) {
if (arguments == null || arguments.isEmpty()) {
return arguments;
}
final String prefix = scope + "." + name + ".";
Map<String, String> result = new HashMap<>();
for (Map.Entry<String, String> entry : arguments.entrySet()) {
result.put(prefix + entry.getKey(), entry.getValue());
}
return result;
}
}