/*
* Copyright (c) 2014 tabletoptool.com team.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*
* Contributors:
* rptools.com team - initial implementation
* tabletoptool.com team - further development
*/
package com.t3.macro.api.functions.input;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public enum InputType {
// The regexp for the option strings is strict: no spaces, and trailing semicolon required.
// @formatter: off
TEXT(false, false, "WIDTH=16;SPAN=FALSE;"),
LIST(true, false, "VALUE=NUMBER;TEXT=TRUE;ICON=FALSE;ICONSIZE=50;SELECT=0;SPAN=FALSE;"),
CHECK(false, false, "SPAN=FALSE;"),
RADIO(true, false,"ORIENT=V;VALUE=NUMBER;SELECT=0;SPAN=FALSE;"),
LABEL(false, false, "TEXT=TRUE;ICON=FALSE;ICONSIZE=50;SPAN=FALSE;"),
PROPS(false, true, "SETVARS=NONE;SPAN=FALSE;"),
TAB(false, true,"SELECT=FALSE;");
// @formatter: on
public final OptionMap defaultOptions; // maps option name to default value
public final boolean isValueComposite; // can "value" section be a list of values?
public final boolean isControlComposite; // does this control contain sub-controls?
InputType(boolean isValueComposite, boolean isControlComposite, String nameval) {
this.isValueComposite = isValueComposite;
this.isControlComposite = isControlComposite;
defaultOptions = new OptionMap();
Pattern pattern = Pattern.compile("(\\w+)=([\\w-]+)\\;"); // no spaces allowed, semicolon required
Matcher matcher = pattern.matcher(nameval);
while (matcher.find()) {
defaultOptions.put(matcher.group(1).toUpperCase(), matcher.group(2).toUpperCase());
}
}
/**
* Obtain one of the enum values, or null if <code>strName</code>
* doesn't match any of them.
*/
public static InputType inputTypeFromName(String strName) {
for (InputType it : InputType.values()) {
if (strName.equalsIgnoreCase(it.name()))
return it;
}
return null;
}
/** Gets the default value for an option. */
public String getDefault(String option) {
return defaultOptions.get(option.toUpperCase());
}
/**
* Parses a string and returns a Map of options for the given type.
* Options not found are set to the default value for the type.
*/
public OptionMap parseOptionString(String s) throws OptionException {
OptionMap ret = new OptionMap();
ret.putAll(defaultOptions); // copy the default values first
Pattern pattern = Pattern.compile("\\s*(\\w+)\\s*\\=\\s*([\\w-]+)\\s*");
Matcher matcher = pattern.matcher(s);
while (matcher.find()) {
String key = matcher.group(1);
String value = matcher.group(2);
if (ret.get(key) == null)
throw new OptionException(this, key, value);
if (ret.getNumeric(key, -9998) != -9998) { // minor hack to detect if the option is numeric
boolean valueIsNumeric;
try {
Integer.decode(value);
valueIsNumeric = true;
} catch (Exception e) {
valueIsNumeric = false;
}
if (!valueIsNumeric)
throw new OptionException(this, key, value);
}
ret.put(key, value);
}
return ret;
}
/*********************************************************
* Stores option settings as case-insensitive strings.
*********************************************************/
@SuppressWarnings("serial")
public final class OptionMap extends HashMap<String, String> {
/** Case-insensitive put. */
@Override
public String put(String key, String value) {
return super.put(key.toUpperCase(), value.toUpperCase());
}
/** Case-insensitive string get. */
@Override
public String get(Object key) {
return super.get(key.toString().toUpperCase());
}
/**
* Case-insensitive numeric get. <br>
* Returns <code>defaultValue</code> if the option's value is
* non-numeric. <br>
* Use when caller wants to override erroneous option settings.
*/
public int getNumeric(String key, int defaultValue) {
int ret;
try {
ret = Integer.decode(get(key));
} catch (Exception e) {
ret = defaultValue;
}
return ret;
}
/**
* Case-insensitive numeric get. <br>
* Returns the default value for the input type if option's value is
* non-numeric. <br>
* Use when caller wants to ignore erroneous option settings.
*/
public int getNumeric(String key) {
String defstr = getDefault(key);
int def;
try {
def = Integer.decode(defstr);
} catch (Exception e) {
def = -1;
// Should never happen, since the defaults are set in the source code.
}
return getNumeric(key, def);
}
/** Tests for a given option value. */
public boolean optionEquals(String key, String value) {
if (get(key) == null)
return false;
return get(key).equalsIgnoreCase(value);
}
} ////////////////////////// end of OptionMap class
/** Thrown when an option value is invalid. */
@SuppressWarnings("serial")
public class OptionException extends Exception {
public String key, value, type;
public OptionException(InputType it, String key, String value) {
super();
this.key = key;
this.value = value;
this.type = it.name();
}
}
}