123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459 |
- package com.winhc.max.compute.graph.util;
- import javax.annotation.Nullable;
- import java.io.*;
- import java.util.*;
- import java.util.concurrent.ConcurrentHashMap;
- /**
- * @author: XuJiakai
- * 2022/6/7 11:28
- */
- public class ParameterTool {
- protected transient Map<String, String> defaultData;
- protected transient Set<String> unrequestedParameters;
- protected static final String NO_VALUE_KEY = "__NO_VALUE_KEY";
- protected static final String DEFAULT_UNDEFINED = "<undefined>";
- // ------------------ Constructors ------------------------
- /**
- * Returns {@link ParameterTool} for the given arguments. The arguments are keys followed by
- * values. Keys have to start with '-' or '--'
- *
- * <p><strong>Example arguments:</strong> --key1 value1 --key2 value2 -key3 value3
- *
- * @param args Input array arguments
- * @return A {@link ParameterTool}
- */
- public static ParameterTool fromArgs(String[] args) {
- final Map<String, String> map = new HashMap<>(args.length / 2);
- int i = 0;
- while (i < args.length) {
- final String key = getKeyFromArgs(args, i);
- if (key.isEmpty()) {
- throw new IllegalArgumentException(
- "The input " + Arrays.toString(args) + " contains an empty argument");
- }
- i += 1; // try to find the value
- if (i >= args.length) {
- map.put(key, NO_VALUE_KEY);
- } else if (isNumber(args[i])) {
- map.put(key, args[i]);
- i += 1;
- } else if (args[i].startsWith("--") || args[i].startsWith("-")) {
- // the argument cannot be a negative number because we checked earlier
- // -> the next argument is a parameter name
- map.put(key, NO_VALUE_KEY);
- } else {
- map.put(key, args[i]);
- i += 1;
- }
- }
- return fromMap(map);
- }
- /**
- * Returns {@link ParameterTool} for the given {@link Properties} file.
- *
- * @param path Path to the properties file
- * @return A {@link ParameterTool}
- * @throws IOException If the file does not exist
- * @see Properties
- */
- public static ParameterTool fromPropertiesFile(String path) throws IOException {
- File propertiesFile = new File(path);
- return fromPropertiesFile(propertiesFile);
- }
- /**
- * Returns {@link ParameterTool} for the given {@link Properties} file.
- *
- * @param file File object to the properties file
- * @return A {@link ParameterTool}
- * @throws IOException If the file does not exist
- * @see Properties
- */
- public static ParameterTool fromPropertiesFile(File file) throws IOException {
- if (!file.exists()) {
- throw new FileNotFoundException(
- "Properties file " + file.getAbsolutePath() + " does not exist");
- }
- try (FileInputStream fis = new FileInputStream(file)) {
- return fromPropertiesFile(fis);
- }
- }
- /**
- * Returns {@link ParameterTool} for the given InputStream from {@link Properties} file.
- *
- * @param inputStream InputStream from the properties file
- * @return A {@link ParameterTool}
- * @throws IOException If the file does not exist
- * @see Properties
- */
- public static ParameterTool fromPropertiesFile(InputStream inputStream) throws IOException {
- Properties props = new Properties();
- props.load(inputStream);
- return fromMap((Map) props);
- }
- /**
- * Returns {@link ParameterTool} for the given map.
- *
- * @param map A map of arguments. Both Key and Value have to be Strings
- * @return A {@link ParameterTool}
- */
- public static ParameterTool fromMap(Map<String, String> map) {
- checkNotNull(map, "Unable to initialize from empty map");
- return new ParameterTool(map);
- }
- /**
- * Returns {@link ParameterTool} from the system properties. Example on how to pass system
- * properties: -Dkey1=value1 -Dkey2=value2
- *
- * @return A {@link ParameterTool}
- */
- public static ParameterTool fromSystemProperties() {
- return fromMap((Map) System.getProperties());
- }
- // ------------------ ParameterUtil ------------------------
- protected final Map<String, String> data;
- private ParameterTool(Map<String, String> data) {
- this.data = Collections.unmodifiableMap(new HashMap<>(data));
- this.defaultData = new ConcurrentHashMap<>(data.size());
- this.unrequestedParameters =
- Collections.newSetFromMap(new ConcurrentHashMap<>(data.size()));
- unrequestedParameters.addAll(data.keySet());
- }
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- ParameterTool that = (ParameterTool) o;
- return Objects.equals(data, that.data)
- && Objects.equals(defaultData, that.defaultData)
- && Objects.equals(unrequestedParameters, that.unrequestedParameters);
- }
- @Override
- public int hashCode() {
- return Objects.hash(data, defaultData, unrequestedParameters);
- }
- // ------------------ Get data from the util ----------------
- /**
- * Returns number of parameters in {@link ParameterTool}.
- */
- public int getNumberOfParameters() {
- return data.size();
- }
- /**
- * Returns the String value for the given key. If the key does not exist it will return null.
- */
- public String get(String key) {
- addToDefaults(key, null);
- unrequestedParameters.remove(key);
- return data.get(key);
- }
- public String getOrDefault(String key, String defaultValue) {
- String s = get(key);
- if (s == null) {
- return defaultValue;
- } else {
- return s;
- }
- }
- /**
- * Check if value is set.
- */
- public boolean has(String value) {
- addToDefaults(value, null);
- unrequestedParameters.remove(value);
- return data.containsKey(value);
- }
- /**
- * Returns a {@link Properties} object from this {@link ParameterTool}.
- *
- * @return A {@link Properties}
- */
- public Properties getProperties() {
- Properties props = new Properties();
- props.putAll(this.data);
- return props;
- }
- /**
- * Create a properties file with all the known parameters (call after the last get*() call). Set
- * the default value, if available.
- *
- * <p>Use this method to create a properties file skeleton.
- *
- * @param pathToFile Location of the default properties file.
- */
- public void createPropertiesFile(String pathToFile) throws IOException {
- createPropertiesFile(pathToFile, true);
- }
- /**
- * Create a properties file with all the known parameters (call after the last get*() call). Set
- * the default value, if overwrite is true.
- *
- * @param pathToFile Location of the default properties file.
- * @param overwrite Boolean flag indicating whether or not to overwrite the file
- * @throws IOException If overwrite is not allowed and the file exists
- */
- public void createPropertiesFile(String pathToFile, boolean overwrite) throws IOException {
- final File file = new File(pathToFile);
- if (file.exists()) {
- if (overwrite) {
- file.delete();
- } else {
- throw new RuntimeException(
- "File " + pathToFile + " exists and overwriting is not allowed");
- }
- }
- final Properties defaultProps = new Properties();
- defaultProps.putAll(this.defaultData);
- try (final OutputStream out = new FileOutputStream(file)) {
- defaultProps.store(
- out, "Default file created by Flink's ParameterUtil.createPropertiesFile()");
- }
- }
- @Override
- protected Object clone() throws CloneNotSupportedException {
- return new ParameterTool(this.data);
- }
- // ------------------------- Interaction with other ParameterUtils -------------------------
- /**
- * Merges two {@link ParameterTool}.
- *
- * @param other Other {@link ParameterTool} object
- * @return The Merged {@link ParameterTool}
- */
- public ParameterTool mergeWith(ParameterTool other) {
- final Map<String, String> resultData = new HashMap<>(data.size() + other.data.size());
- resultData.putAll(data);
- resultData.putAll(other.data);
- final ParameterTool ret = new ParameterTool(resultData);
- final HashSet<String> requestedParametersLeft = new HashSet<>(data.keySet());
- requestedParametersLeft.removeAll(unrequestedParameters);
- final HashSet<String> requestedParametersRight = new HashSet<>(other.data.keySet());
- requestedParametersRight.removeAll(other.unrequestedParameters);
- ret.unrequestedParameters.removeAll(requestedParametersLeft);
- ret.unrequestedParameters.removeAll(requestedParametersRight);
- return ret;
- }
- // ------------------------- ExecutionConfig.UserConfig interface -------------------------
- public Map<String, String> toMap() {
- return data;
- }
- // ------------------------- Serialization ---------------------------------------------
- private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
- in.defaultReadObject();
- defaultData = new ConcurrentHashMap<>(data.size());
- unrequestedParameters = Collections.newSetFromMap(new ConcurrentHashMap<>(data.size()));
- }
- public static String getKeyFromArgs(String[] args, int index) {
- String key;
- if (args[index].startsWith("--")) {
- key = args[index].substring(2);
- } else if (args[index].startsWith("-")) {
- key = args[index].substring(1);
- } else {
- throw new IllegalArgumentException(
- String.format(
- "Error parsing arguments '%s' on '%s'. Please prefix keys with -- or -.",
- Arrays.toString(args), args[index]));
- }
- if (key.isEmpty()) {
- throw new IllegalArgumentException(
- "The input " + Arrays.toString(args) + " contains an empty argument");
- }
- return key;
- }
- public static boolean isNumber(final String str) {
- if (isEmpty(str)) {
- return false;
- }
- final char[] chars = str.toCharArray();
- int sz = chars.length;
- boolean hasExp = false;
- boolean hasDecPoint = false;
- boolean allowSigns = false;
- boolean foundDigit = false;
- // deal with any possible sign up front
- final int start = (chars[0] == '-') ? 1 : 0;
- if (sz > start + 1 && chars[start] == '0') { // leading 0
- if (
- (chars[start + 1] == 'x') ||
- (chars[start + 1] == 'X')
- ) { // leading 0x/0X
- int i = start + 2;
- if (i == sz) {
- return false; // str == "0x"
- }
- // checking hex (it can't be anything else)
- for (; i < chars.length; i++) {
- if ((chars[i] < '0' || chars[i] > '9')
- && (chars[i] < 'a' || chars[i] > 'f')
- && (chars[i] < 'A' || chars[i] > 'F')) {
- return false;
- }
- }
- return true;
- } else if (Character.isDigit(chars[start + 1])) {
- // leading 0, but not hex, must be octal
- int i = start + 1;
- for (; i < chars.length; i++) {
- if (chars[i] < '0' || chars[i] > '7') {
- return false;
- }
- }
- return true;
- }
- }
- sz--; // don't want to loop to the last char, check it afterwords
- // for type qualifiers
- int i = start;
- // loop to the next to last char or to the last char if we need another digit to
- // make a valid number (e.g. chars[0..5] = "1234E")
- while (i < sz || (i < sz + 1 && allowSigns && !foundDigit)) {
- if (chars[i] >= '0' && chars[i] <= '9') {
- foundDigit = true;
- allowSigns = false;
- } else if (chars[i] == '.') {
- if (hasDecPoint || hasExp) {
- // two decimal points or dec in exponent
- return false;
- }
- hasDecPoint = true;
- } else if (chars[i] == 'e' || chars[i] == 'E') {
- // we've already taken care of hex.
- if (hasExp) {
- // two E's
- return false;
- }
- if (!foundDigit) {
- return false;
- }
- hasExp = true;
- allowSigns = true;
- } else if (chars[i] == '+' || chars[i] == '-') {
- if (!allowSigns) {
- return false;
- }
- allowSigns = false;
- foundDigit = false; // we need a digit after the E
- } else {
- return false;
- }
- i++;
- }
- if (i < chars.length) {
- if (chars[i] >= '0' && chars[i] <= '9') {
- // no type qualifier, OK
- return true;
- }
- if (chars[i] == 'e' || chars[i] == 'E') {
- // can't have an E at the last byte
- return false;
- }
- if (chars[i] == '.') {
- if (hasDecPoint || hasExp) {
- // two decimal points or dec in exponent
- return false;
- }
- // single trailing decimal point after non-exponent is ok
- return foundDigit;
- }
- if (!allowSigns
- && (chars[i] == 'd'
- || chars[i] == 'D'
- || chars[i] == 'f'
- || chars[i] == 'F')) {
- return foundDigit;
- }
- if (chars[i] == 'l'
- || chars[i] == 'L') {
- // not allowing L with an exponent or decimal point
- return foundDigit && !hasExp && !hasDecPoint;
- }
- // last character is illegal
- return false;
- }
- // allowSigns is true iff the val ends in 'E'
- // found digit it to make sure weird stuff like '.' and '1E-' doesn't pass
- return !allowSigns && foundDigit;
- }
- public static boolean isEmpty(final CharSequence cs) {
- return cs == null || cs.length() == 0;
- }
- protected void addToDefaults(String key, String value) {
- final String currentValue = defaultData.get(key);
- if (currentValue == null) {
- if (value == null) {
- value = DEFAULT_UNDEFINED;
- }
- defaultData.put(key, value);
- } else {
- // there is already an entry for this key. Check if the value is the undefined
- if (currentValue.equals(DEFAULT_UNDEFINED) && value != null) {
- // update key with better default value
- defaultData.put(key, value);
- }
- }
- }
- public static <T> T checkNotNull(@Nullable T reference, @Nullable String errorMessage) {
- if (reference == null) {
- throw new NullPointerException(String.valueOf(errorMessage));
- }
- return reference;
- }
- }
|