/*
* JaamSim Discrete Event Simulation
* Copyright (C) 2010-2012 Ausenco Engineering Canada Inc.
* Copyright (C) 2016 JaamSim Software 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 com.jaamsim.input;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.regex.Pattern;
import com.jaamsim.Samples.SampleConstant;
import com.jaamsim.Samples.SampleExpression;
import com.jaamsim.Samples.SampleProvider;
import com.jaamsim.Samples.TimeSeriesConstantDouble;
import com.jaamsim.StringProviders.StringProvExpression;
import com.jaamsim.StringProviders.StringProvSample;
import com.jaamsim.StringProviders.StringProvider;
import com.jaamsim.basicsim.Entity;
import com.jaamsim.basicsim.Group;
import com.jaamsim.basicsim.ObjectType;
import com.jaamsim.datatypes.BooleanVector;
import com.jaamsim.datatypes.DoubleVector;
import com.jaamsim.datatypes.IntegerVector;
import com.jaamsim.math.Color4d;
import com.jaamsim.ui.NaturalOrderComparator;
import com.jaamsim.units.DimensionlessUnit;
import com.jaamsim.units.TimeUnit;
import com.jaamsim.units.Unit;
import com.jaamsim.units.UserSpecifiedUnit;
public abstract class Input<T> {
protected static final String INP_ERR_COUNT = "Expected an input with %s value(s), received: %s";
protected static final String INP_ERR_RANGECOUNT = "Expected an input with %d to %d values, received: %s";
protected static final String INP_ERR_RANGECOUNTMIN = "Expected an input with at least %d values, received: %s";
protected static final String INP_ERR_EVENCOUNT = "Expected an input with even number of values, received: %s";
protected static final String INP_ERR_ODDCOUNT = "Expected an input with odd number of values, received: %s";
protected static final String INP_ERR_BOOLEAN = "Expected a boolean value, received: %s";
protected static final String INP_ERR_INTEGER = "Expected an integer value, received: %s";
protected static final String INP_ERR_INTEGERRANGE = "Expected an integer between %d and %d, received: %d";
protected static final String INP_ERR_DOUBLE = "Expected an numeric value, received: %s";
protected static final String INP_ERR_DOUBLERANGE = "Expected a number between %f and %f, received: %f";
protected static final String INP_ERR_TIME = "Expected a time value (hh:mm or hh:mm:ss), received: %s";
protected static final String INP_ERR_TIMEVALUE = "Expected a numeric value, 12 numeric values, or a probabilty distribution, received: %s";
protected static final String INP_ERR_BADSUM = "List must sum to %f, received:%f";
protected static final String INP_ERR_MONOTONIC = "List must %s monotonically. Values starting at index %s are %s s, %s s, ...";
protected static final String INP_ERR_BADCHOICE = "Expected one of %s, received: %s";
protected static final String INP_ERR_ELEMENT = "Error parsing element %d: %s";
protected static final String INP_ERR_ENTNAME = "Could not find an Entity named: %s";
protected static final String INP_ERR_NOUNITFOUND = "A unit is required, could not parse '%s' as a %s";
protected static final String INP_ERR_UNITNOTFOUND = "A unit of type '%s' is required";
protected static final String INP_ERR_NOTUNIQUE = "List must contain unique entries, repeated entry: %s";
protected static final String INP_ERR_NOTVALIDENTRY = "List must not contain: %s";
protected static final String INP_ERR_ENTCLASS = "Expected a %s, %s is a %s";
protected static final String INP_ERR_INTERFACE = "Expected an object implementing %s, %s does not";
protected static final String INP_ERR_UNITS = "Unit types do not match";
protected static final String INP_ERR_UNITUNSPECIFIED = "Unit type has not been specified";
protected static final String INP_ERR_NOTSUBCLASS = "Expected a subclass of %s, got %s";
protected static final String INP_ERR_BADDATE = "Expected a valid RFC8601 datetime, got: %s";
protected static final String INP_ERR_BADEXP = "Error parsing expression: %s";
protected static final String INP_VAL_LISTSET = "Values found for %s without %s being set";
protected static final String INP_VAL_LISTSIZE = "%s and %s must be of equal size";
public static final String POSITIVE_INFINITY = "Infinity";
public static final String NEGATIVE_INFINITY = "-Infinity";
public static final String SEPARATOR = " ";
private String keyword; // the preferred name for the input keyword
private final String category;
protected T defValue;
protected T value;
private boolean edited; // indicates if input has been edited for this entity
private boolean promptReqd; // indicates whether to prompt the user to save the configuration file
private boolean hidden; // Hide this input from the EditBox
protected boolean isDef; // Is this input still the default value?
protected String[] valueTokens; // value from .cfg file
private String defText; // special text to show in the default column of the Input Editor
private boolean isReqd; // indicates whether this input must be provided by the user
private boolean isValid; // if false, the input is no longer valid and must be re-entered
public static final Comparator<Object> uiSortOrder = new NaturalOrderComparator();
public Input(String key, String cat, T def) {
keyword = key;
category = cat;
setDefaultValue(def);
edited = false;
promptReqd = true;
isDef = true;
hidden = false;
valueTokens = null;
defText = null;
isReqd = false;
isValid = true;
}
public void reset() {
this.setDefaultValue( this.getDefaultValue() );
valueTokens = null;
edited = false;
isDef = true;
isValid = true;
}
/**
* Assigns the internal state for this input to the same values as the
* specified input.
* @param in - input object to be copied.
*/
public void copyFrom(Input<?> in) {
@SuppressWarnings("unchecked")
Input<T> inp = (Input<T>) in;
// Copy the internal state
value = inp.value;
valueTokens = inp.valueTokens;
isDef = false;
edited = true;
isValid = true;
}
/**
* Deletes any use of the specified entity from this input.
* @param ent - entity whose references are to be deleted
*/
public void removeReferences(Entity ent) {}
@Override
public String toString() {
return String.format("%s", value);
}
public final String getKeyword() {
return keyword;
}
public void setKeyword(String str) {
keyword = str;
}
public final String getCategory() {
return category;
}
public boolean isSynonym() {
return false;
}
public void setDefaultText(String str) {
defText = str;
}
public String getDefaultText() {
return defText;
}
public void setDefaultValue(T val) {
defValue = val;
value = val;
}
public T getDefaultValue() {
return defValue;
}
public String getDefaultString() {
if (defValue == null)
return "";
return defValue.toString();
}
public T getValue() {
return value;
}
public void setHidden(boolean hide) {
hidden = hide;
}
public boolean getHidden() {
return hidden;
}
public void setEdited(boolean bool) {
edited = bool;
}
public boolean isEdited() {
return edited;
}
public void setPromptReqd(boolean bool) {
promptReqd = bool;
}
public boolean isPromptReqd() {
return promptReqd;
}
public void setRequired(boolean bool) {
isReqd = bool;
}
public boolean isRequired() {
return isReqd;
}
public void setValid(boolean bool) {
isValid = bool;
}
public boolean isValid() {
return isValid;
}
public void validate() throws InputErrorException {
if (isReqd && isDef && !hidden)
throw new InputErrorException("An input must be provided for the keyword '%s'.", keyword);
}
public void setTokens(KeywordIndex kw) {
isDef = false;
valueTokens = kw.getArgArray();
}
/**
* Add the given tokens to the present value tokens
*/
public void addTokens(String[] args) {
// Create an array sized for the addition of new tokens
String[] newValueTokens;
if (valueTokens == null) {
newValueTokens = new String[args.length - 1];
// Copy the new tokens into the array
System.arraycopy(args, 1, newValueTokens, 0, args.length - 1);
}
else {
newValueTokens = new String[valueTokens.length + args.length - 1];
// Copy the old tokens into the array
System.arraycopy(valueTokens, 0, newValueTokens, 0, valueTokens.length);
// Copy the new tokens into the array
System.arraycopy(args, 1, newValueTokens, valueTokens.length, args.length - 1);
}
valueTokens = newValueTokens;
}
/**
* Remove the given tokens from the present value tokens
* @return - true if all the tokens were successfully removed
*/
public boolean removeTokens(String[] args) {
int newSize = valueTokens.length - (args.length - 1);
if( newSize >= 0 ) {
// Create an array sized for the removal of tokens
String[] newValueTokens = new String[newSize];
int index = 0;
// Loop through the original tokens
for (int i = 0; i < valueTokens.length; i++ ) {
// Determine if this token is to be kept
boolean keep = true;
for (int j = 1; j < args.length; j++) {
if (args[j].equals(valueTokens[i])) {
keep = false;
break;
}
}
// If the token is to be kept, add it to the array
if (keep) {
newValueTokens[index] = valueTokens[i];
index++;
}
}
// If the correct number of items were kept, reset valueTokens
if (index == newSize) {
valueTokens = newValueTokens;
return true;
}
}
return false;
}
/**
* Append the given tokens to the present value tokens
*/
public void appendTokens(String[] args) {
// Determine if braces need to be added around original tokens
boolean addBracesAroundOriginalTokens = false;
// Determine the size for an array with original and new tokens
int newSize;
int valueTokensLength = 0;
if (valueTokens == null)
newSize = args.length;
else {
valueTokensLength = valueTokens.length;
newSize = valueTokens.length + args.length;
if (! valueTokens[0].equals( "{" )) {
addBracesAroundOriginalTokens = true;
newSize += 2;
}
}
// Determine if braces need to be added around new tokens
boolean addBracesAroundNewTokens = false;
if (! args[0].equals( "{" )) {
addBracesAroundNewTokens = true;
newSize += 2;
}
// Create an array sized for the addition of new tokens
String[] newValueTokens = new String[newSize];
// Copy the old and new tokens into the array
if (addBracesAroundOriginalTokens) {
newValueTokens[0] = "{";
System.arraycopy(valueTokens, 0, newValueTokens, 1, valueTokens.length);
newValueTokens[valueTokens.length + 1] = "}";
if (addBracesAroundNewTokens) {
newValueTokens[valueTokens.length + 2] = "{";
System.arraycopy(args, 0, newValueTokens, valueTokens.length+3, args.length);
newValueTokens[newSize-1] = "}";
}
else {
System.arraycopy(args, 0, newValueTokens, valueTokens.length+2, args.length);
}
}
else {
if (valueTokens != null)
System.arraycopy(valueTokens, 0, newValueTokens, 0, valueTokens.length);
if (addBracesAroundNewTokens) {
newValueTokens[valueTokensLength] = "{";
System.arraycopy(args, 0, newValueTokens, valueTokensLength+1, args.length);
newValueTokens[newSize-1] = "}";
}
else {
System.arraycopy(args, 0, newValueTokens, valueTokensLength, args.length);
}
}
valueTokens = newValueTokens;
}
public boolean isDefault() {
return isDef;
}
public void getValueTokens(ArrayList<String> toks) {
if (valueTokens == null)
return;
for (String each : valueTokens)
toks.add(each);
}
public final String getValueString() {
if (isDefault()) return "";
ArrayList<String> tmp = new ArrayList<>();
try {
getValueTokens(tmp);
} catch (Exception e) {
InputAgent.logMessage("Error in input, value has been cleared. Keyword: %s",
this.getKeyword());
InputAgent.logStackTrace(e);
this.reset();
}
if (tmp.size() == 0) return "";
StringBuilder sb = new StringBuilder();
for (int i = 0; i < tmp.size(); i++) {
String dat = tmp.get(i);
if (dat == null) continue;
if (i > 0)
sb.append(Input.SEPARATOR);
if (Parser.needsQuoting(dat) && !dat.equals("{") && !dat.equals("}"))
sb.append("'").append(dat).append("'");
else
sb.append(dat);
}
return sb.toString();
}
public abstract void parse(KeywordIndex kw) throws InputErrorException;
public static void assertCount(DoubleVector input, int... counts)
throws InputErrorException {
// If there is no constraint on the element count, return
if (counts == null || counts.length == 0)
return;
// If there is an exact constraint, check the count
for (int each : counts) {
if (each == input.size())
return;
}
// Input size is not equal to any of the specified counts
throw new InputErrorException(INP_ERR_COUNT, Arrays.toString(counts), input.toString());
}
/**
* Verifies that the correct number of inputs have been provided.
* @param kw - object containing the inputs.
* @param counts - list of valid numbers of inputs. All other numbers are invalid.
* @throws InputErrorException
*/
public static void assertCount(KeywordIndex kw, int... counts)
throws InputErrorException {
// If there is no constraint on the element count, return
if (counts.length == 0)
return;
// If there is an exact constraint, check the count
for (int each : counts) {
if (each == kw.numArgs())
return;
}
// Input size is not equal to any of the specified counts
if (counts.length == 1)
throw new InputErrorException(INP_ERR_COUNT, counts[0], kw.argString());
else {
StringBuilder sb = new StringBuilder();
sb.append(counts[0]);
for (int i=1; i<counts.length-1; i++) {
sb.append(", ").append(counts[i]);
}
sb.append(" or ").append(counts[counts.length-1]);
throw new InputErrorException(INP_ERR_COUNT, sb.toString(), kw.argString());
}
}
/**
* Verifies that the correct number of inputs have been provided.
* @param kw - object containing the inputs.
* @param min - minimum number of inputs that are valid
* @param max - maximum number of inputs that are valid
* @throws InputErrorException
*/
public static void assertCountRange(KeywordIndex kw, int min, int max)
throws InputErrorException {
// For a range with a single value, fall back to the exact test
if (min == max) {
Input.assertCount(kw, min);
return;
}
if (kw.numArgs() < min || kw.numArgs() > max) {
if (max == Integer.MAX_VALUE)
throw new InputErrorException(INP_ERR_RANGECOUNTMIN, min, kw.argString());
throw new InputErrorException(INP_ERR_RANGECOUNT, min, max, kw.argString());
}
}
public static void assertCount(List<String> input, int... counts)
throws InputErrorException {
// If there is no constraint on the element count, return
if (counts.length == 0)
return;
// If there is an exact constraint, check the count
for (int each : counts) {
if (each == input.size())
return;
}
// Input size is not equal to any of the specified counts
throw new InputErrorException(INP_ERR_COUNT, Arrays.toString(counts), input.toString());
}
public static void assertCountRange(DoubleVector input, int min, int max)
throws InputErrorException {
// For a range with a single value, fall back to the exact test
if (min == max) {
Input.assertCount(input, min);
return;
}
if (input.size() < min || input.size() > max) {
if (max == Integer.MAX_VALUE)
throw new InputErrorException(INP_ERR_RANGECOUNTMIN, min, input.toString());
throw new InputErrorException(INP_ERR_RANGECOUNT, min, max, input.toString());
}
}
public static void assertCountRange(List<String> input, int min, int max)
throws InputErrorException {
// For a range with a single value, fall back to the exact test
if (min == max) {
Input.assertCount(input, min);
return;
}
if (input.size() < min || input.size() > max) {
if (max == Integer.MAX_VALUE)
throw new InputErrorException(INP_ERR_RANGECOUNTMIN, min, input.toString());
throw new InputErrorException(INP_ERR_RANGECOUNT, min, max, input.toString());
}
}
public static void assertCountEven(KeywordIndex kw)
throws InputErrorException {
if ((kw.numArgs() % 2) != 0)
throw new InputErrorException(INP_ERR_EVENCOUNT, kw.argString());
}
public static void assertCountOdd(KeywordIndex kw)
throws InputErrorException {
if ((kw.numArgs() % 2) == 0)
throw new InputErrorException(INP_ERR_ODDCOUNT, kw.argString());
}
public static <T extends Entity> void assertNotPresent(ArrayList<? super T> list, T ent)
throws InputErrorException {
if (list.contains(ent))
throw new InputErrorException(INP_ERR_NOTVALIDENTRY, ent.getName());
}
public static void assertSumTolerance(DoubleVector vec, double sum, double tol)
throws InputErrorException {
// Vector sum is within tolerance of given sum, no error
if (Math.abs(vec.sum() - sum) < tol)
return;
throw new InputErrorException(INP_ERR_BADSUM, sum, vec.sum());
}
public static void assertMonotonic(DoubleVector vec, int direction)
throws InputErrorException {
if (direction == 0)
return;
for (int i=1; i<vec.size(); i++) {
double diff = vec.get(i) - vec.get(i-1);
if (direction > 0 && diff < 0.0)
throw new InputErrorException(INP_ERR_MONOTONIC, "increase", i-1, vec.get(i-1), vec.get(i));
if (direction < 0 && diff > 0.0)
throw new InputErrorException(INP_ERR_MONOTONIC, "decrease", i-1, vec.get(i-1), vec.get(i));
}
}
public static <T> T parse(List<String> data, Class<T> aClass, double minValue, double maxValue, int minCount, int maxCount, Class<? extends Unit> unitType) {
if( aClass == Double.class ) {
DoubleVector tmp = Input.parseDoubles(data, minValue, maxValue, unitType);
Input.assertCount(tmp, 1);
return aClass.cast( tmp.get(0));
}
if( aClass == DoubleVector.class ) {
DoubleVector tmp = Input.parseDoubles(data, minValue, maxValue, unitType);
Input.assertCountRange(tmp, minCount, maxCount);
return aClass.cast( tmp );
}
if( Entity.class.isAssignableFrom(aClass) ) {
Class<? extends Entity> temp = aClass.asSubclass(Entity.class);
Input.assertCount(data, 1, 1);
return aClass.cast( Input.parseEntity(data.get(0), temp) );
}
if( aClass == Boolean.class ) {
Input.assertCount(data, 1);
Boolean value = Boolean.valueOf(Input.parseBoolean(data.get(0)));
return aClass.cast(value);
}
if( aClass == Integer.class ) {
Input.assertCount(data, 1);
Integer value = Input.parseInteger(data.get( 0 ), (int)minValue, (int)maxValue);
return aClass.cast(value);
}
if( aClass == SampleProvider.class ) {
// Try to parse as a constant value
try {
DoubleVector tmp = Input.parseDoubles(data, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, unitType);
Input.assertCount(tmp, 1);
return aClass.cast( new SampleConstant(unitType, tmp.get(0)) );
}
catch (InputErrorException e) {}
// If not a constant, try parsing a SampleProvider
Input.assertCount(data, 1);
Entity ent = Input.parseEntity(data.get(0), Entity.class);
SampleProvider s = Input.castImplements(ent, SampleProvider.class);
if( s.getUnitType() != UserSpecifiedUnit.class )
Input.assertUnitsMatch(unitType, s.getUnitType());
return aClass.cast(s);
}
if( aClass == IntegerVector.class ) {
IntegerVector value = Input.parseIntegerVector(data, (int)minValue, (int)maxValue);
if (value.size() < minCount || value.size() > maxCount)
throw new InputErrorException(INP_ERR_RANGECOUNT, minCount, maxCount, data);
return aClass.cast(value);
}
// TODO - parse other classes
throw new InputErrorException("%s is not supported for parsing yet", aClass);
}
/**
* Converts a file path entry in a configuration file to a URI.
* @param kw - keyword input containing the file path data
* @return the URI corresponding to the file path data.
* @throws InputErrorException
*/
public static URI parseURI(KeywordIndex kw)
throws InputErrorException {
Input.assertCount(kw, 1);
String arg = kw.getArg(0);
// Convert the file path to a URI
URI uri = null;
try {
if (kw.context != null)
uri = InputAgent.getFileURI(kw.context.context, arg, kw.context.jail);
else
uri = InputAgent.getFileURI(null, arg, null);
}
catch (URISyntaxException ex) {
throw new InputErrorException("File Entity parse error: %s", ex.getMessage());
}
if (uri == null)
throw new InputErrorException("Unable to parse the file path:\n%s", arg);
if (!uri.isOpaque() && uri.getPath() == null)
throw new InputErrorException("Unable to parse the file path:\n%s", arg);
return uri;
}
public static boolean parseBoolean(String data)
throws InputErrorException {
if ("TRUE".equals(data)) {
return true;
}
if ("FALSE".equals(data)) {
return false;
}
throw new InputErrorException(INP_ERR_BOOLEAN, data);
}
public static BooleanVector parseBooleanVector(KeywordIndex kw)
throws InputErrorException {
BooleanVector temp = new BooleanVector(kw.numArgs());
for (int i = 0; i < kw.numArgs(); i++) {
try {
boolean element = Input.parseBoolean(kw.getArg(i));
temp.add(element);
} catch (InputErrorException e) {
throw new InputErrorException(INP_ERR_ELEMENT, i+1, e.getMessage());
}
}
return temp;
}
public static BooleanVector parseBooleanVector(List<String> input)
throws InputErrorException {
BooleanVector temp = new BooleanVector(input.size());
for (int i = 0; i < input.size(); i++) {
try {
boolean element = Input.parseBoolean(input.get(i));
temp.add(element);
} catch (InputErrorException e) {
throw new InputErrorException(INP_ERR_ELEMENT, i+1, e.getMessage());
}
}
return temp;
}
public static ArrayList<Color4d> parseColorVector(KeywordIndex kw)
throws InputErrorException {
ArrayList<KeywordIndex> subArgs = kw.getSubArgs();
ArrayList<Color4d> temp = new ArrayList<>(subArgs.size());
for (int i = 0; i < subArgs.size(); i++) {
try {
Color4d element = Input.parseColour(subArgs.get(i));
temp.add(element);
} catch (InputErrorException e) {
throw new InputErrorException(INP_ERR_ELEMENT, i+1, e.getMessage());
}
}
return temp;
}
public static Class<? extends Entity> parseClass( String data ) {
Class<? extends Entity> entProto = null;
try {
Class<?> proto = Class.forName( data );
entProto = proto.asSubclass(Entity.class);
}
catch (ClassNotFoundException e) {
throw new InputErrorException( "Class not found " + data );
}
return entProto;
}
public static int parseInteger(String data)
throws InputErrorException {
return Input.parseInteger(data, Integer.MIN_VALUE, Integer.MAX_VALUE);
}
public static int parseInteger(String data, int minValue, int maxValue)
throws InputErrorException {
int temp;
try {
temp = Integer.parseInt(data);
}
catch (NumberFormatException e) {
throw new InputErrorException(INP_ERR_INTEGER, data);
}
if (temp < minValue || temp > maxValue)
throw new InputErrorException(INP_ERR_INTEGERRANGE, minValue, maxValue, temp);
return temp;
}
public static boolean isInteger(String val) {
try {
Integer.parseInt(val);
return true;
}
catch (NumberFormatException e) { return false; }
}
public static boolean isDouble(String val) {
try {
Double.parseDouble(val);
return true;
}
catch (NumberFormatException e) { return false; }
}
public static IntegerVector parseIntegerVector(List<String> input, int minValue, int maxValue)
throws InputErrorException {
IntegerVector temp = new IntegerVector(input.size());
for (int i = 0; i < input.size(); i++) {
try {
int element = Input.parseInteger(input.get(i), minValue, maxValue);
temp.add(element);
} catch (InputErrorException e) {
throw new InputErrorException(INP_ERR_ELEMENT, i+1, e.getMessage());
}
}
return temp;
}
public static IntegerVector parseIntegerVector(KeywordIndex kw, int minValue, int maxValue)
throws InputErrorException {
IntegerVector temp = new IntegerVector(kw.numArgs());
for (int i = 0; i <kw.numArgs(); i++) {
try {
int element = Input.parseInteger(kw.getArg(i), minValue, maxValue);
temp.add(element);
} catch (InputErrorException e) {
throw new InputErrorException(INP_ERR_ELEMENT, i+1, e.getMessage());
}
}
return temp;
}
public static double parseTime(String data, double minValue, double maxValue)
throws InputErrorException {
return Input.parseTime(data, minValue, maxValue, 1.0);
}
/**
* Convert the given String to a double and apply the given conversion factor
*/
public static double parseTime(String data, double minValue, double maxValue, double factor )
throws InputErrorException {
double value = 0.0d;
// check for hh:mm:ss or hh:mm
if (data.indexOf(':') > -1) {
String[] splitDouble = data.split( ":" );
if (splitDouble.length != 2 && splitDouble.length != 3)
throw new InputErrorException(INP_ERR_TIME, data);
try {
double hour = Double.valueOf(splitDouble[0]);
double min = Double.valueOf(splitDouble[1]);
double sec = 0.0d;
if (splitDouble.length == 3)
sec = Double.valueOf(splitDouble[2]);
value = hour + (min / 60.0d) + (sec / 3600.0d);
}
catch (NumberFormatException e) {
throw new InputErrorException(INP_ERR_TIME, data);
}
} else {
value = Input.parseDouble(data);
}
value = value * factor;
if (value < minValue || value > maxValue)
throw new InputErrorException(INP_ERR_DOUBLERANGE, minValue, maxValue, value);
return value;
}
private static final Pattern is8601date = Pattern.compile("\\d{4}-\\d{2}-\\d{2}");
private static final Pattern is8601time = Pattern.compile("\\d{4}-\\d{2}-\\d{2}[ T]\\d{2}:\\d{2}:\\d{2}");
private static final Pattern is8601full = Pattern.compile("\\d{4}-\\d{2}-\\d{2}[ T]\\d{2}:\\d{2}:\\d{2}\\.\\d{1,6}");
private static final Pattern isextendtime = Pattern.compile("\\d{1,}:\\d{2}:\\d{2}");
private static final Pattern isextendfull = Pattern.compile("\\d{1,}:\\d{2}:\\d{2}.\\d{1,6}");
private static final long usPerSec = 1000000;
private static final long usPerMin = 60 * usPerSec;
private static final long usPerHr = 60 * usPerMin;
private static final long usPerDay = 24 * usPerHr;
public static final long usPerYr = 365 * usPerDay;
public static boolean isRFC8601DateTime(String input) {
if (is8601time.matcher(input).matches()) return true;
if (is8601full.matcher(input).matches()) return true;
if (is8601date.matcher(input).matches()) return true;
if (isextendtime.matcher(input).matches()) return true;
if (isextendfull.matcher(input).matches()) return true;
return false;
}
/**
* Parse an RFC8601 date time and return it as an offset in microseconds from
* 0AD. This assumes a very simple concept of a 365 day year with no leap years
* and no leap seconds.
*
* An RFC8601 date time looks like YYYY-MM-DD HH:MM:SS.mmm or YYYY-MM-DDTHH:MM:SS.mmm
*
* @param input
* @param datumYear
* @return
*/
public static long parseRFC8601DateTime(String input) {
if (is8601time.matcher(input).matches()) {
int YY = Integer.parseInt(input.substring(0, 4));
int MM = Integer.parseInt(input.substring(5, 7));
int DD = Integer.parseInt(input.substring(8, 10));
int hh = Integer.parseInt(input.substring(11, 13));
int mm = Integer.parseInt(input.substring(14, 16));
int ss = Integer.parseInt(input.substring(17, 19));
return getUS(input, YY, MM, DD, hh, mm, ss, 0);
}
if (is8601full.matcher(input).matches()) {
int YY = Integer.parseInt(input.substring(0, 4));
int MM = Integer.parseInt(input.substring(5, 7));
int DD = Integer.parseInt(input.substring(8, 10));
int hh = Integer.parseInt(input.substring(11, 13));
int mm = Integer.parseInt(input.substring(14, 16));
int ss = Integer.parseInt(input.substring(17, 19));
// grab the us values and zero-pad to a full 6-digit number
String usChars = input.substring(20, input.length());
int us = 0;
switch (usChars.length()) {
case 1: us = Integer.parseInt(usChars) * 100000; break;
case 2: us = Integer.parseInt(usChars) * 10000; break;
case 3: us = Integer.parseInt(usChars) * 1000; break;
case 4: us = Integer.parseInt(usChars) * 100; break;
case 5: us = Integer.parseInt(usChars) * 10; break;
case 6: us = Integer.parseInt(usChars) * 1; break;
}
return getUS(input, YY, MM, DD, hh, mm, ss, us);
}
if (is8601date.matcher(input).matches()) {
int YY = Integer.parseInt(input.substring(0, 4));
int MM = Integer.parseInt(input.substring(5, 7));
int DD = Integer.parseInt(input.substring(8, 10));
return getUS(input, YY, MM, DD, 0, 0, 0, 0);
}
if (isextendtime.matcher(input).matches()) {
int len = input.length();
int hh = Integer.parseInt(input.substring(0, len - 6));
int mm = Integer.parseInt(input.substring(len - 5, len - 3));
int ss = Integer.parseInt(input.substring(len - 2, len));
if (mm < 0 || mm > 59 || ss < 0 || ss > 59)
throw new InputErrorException(INP_ERR_BADDATE, input);
long ret = 0;
ret += hh * usPerHr;
ret += mm * usPerMin;
ret += ss * usPerSec;
return ret;
}
if (isextendfull.matcher(input).matches()) {
int len = input.indexOf('.');
int hh = Integer.parseInt(input.substring(0, len - 6));
int mm = Integer.parseInt(input.substring(len - 5, len - 3));
int ss = Integer.parseInt(input.substring(len - 2, len));
if (mm < 0 || mm > 59 || ss < 0 || ss > 59)
throw new InputErrorException(INP_ERR_BADDATE, input);
// grab the us values and zero-pad to a full 6-digit number
String usChars = input.substring(len + 1, input.length());
int us = 0;
switch (usChars.length()) {
case 1: us = Integer.parseInt(usChars) * 100000; break;
case 2: us = Integer.parseInt(usChars) * 10000; break;
case 3: us = Integer.parseInt(usChars) * 1000; break;
case 4: us = Integer.parseInt(usChars) * 100; break;
case 5: us = Integer.parseInt(usChars) * 10; break;
case 6: us = Integer.parseInt(usChars) * 1; break;
}
long ret = 0;
ret += hh * usPerHr;
ret += mm * usPerMin;
ret += ss * usPerSec;
ret += us;
return ret;
}
throw new InputErrorException(INP_ERR_BADDATE, input);
}
private static final int[] daysInMonth;
private static final int[] firstDayOfMonth;
static {
daysInMonth = new int[12];
daysInMonth[0] = 31;
daysInMonth[1] = 28;
daysInMonth[2] = 31;
daysInMonth[3] = 30;
daysInMonth[4] = 31;
daysInMonth[5] = 30;
daysInMonth[6] = 31;
daysInMonth[7] = 31;
daysInMonth[8] = 30;
daysInMonth[9] = 31;
daysInMonth[10] = 30;
daysInMonth[11] = 31;
firstDayOfMonth = new int[12];
firstDayOfMonth[0] = 1;
for (int i = 1; i < firstDayOfMonth.length; i++) {
firstDayOfMonth[i] = firstDayOfMonth[i - 1] + daysInMonth[i - 1];
}
}
private static final long getUS(String input, int YY, int MM, int DD, int hh, int mm, int ss, int us) {
// Validate ranges
if (MM <= 0 || MM > 12)
throw new InputErrorException(INP_ERR_BADDATE, input);
if (DD <= 0 || DD > daysInMonth[MM - 1])
throw new InputErrorException(INP_ERR_BADDATE, input);
if (hh < 0 || hh > 23)
throw new InputErrorException(INP_ERR_BADDATE, input);
if (mm < 0 || mm > 59 || ss < 0 || ss > 59)
throw new InputErrorException(INP_ERR_BADDATE, input);
long ret = 0;
ret += YY * usPerYr;
ret += (firstDayOfMonth[MM - 1] - 1) * usPerDay;
ret += (DD - 1) * usPerDay;
ret += hh * usPerHr;
ret += mm * usPerMin;
ret += ss * usPerSec;
ret += us;
return ret;
}
public static double parseDouble(String data)
throws InputErrorException {
return Input.parseDouble(data, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
}
public static double parseDouble(String data, double minValue, double maxValue)
throws InputErrorException {
return Input.parseDouble(data, minValue, maxValue, 1.0);
}
/**
* Convert the given String to a double and apply the given conversion factor
*/
public static double parseDouble(String data, double minValue, double maxValue, double factor)
throws InputErrorException {
double temp;
try {
temp = Double.parseDouble(data) * factor;
}
catch (NumberFormatException e) {
throw new InputErrorException(INP_ERR_DOUBLE, data);
}
if (temp < minValue || temp > maxValue)
throw new InputErrorException(INP_ERR_DOUBLERANGE, minValue, maxValue, temp);
return temp;
}
/**
* Convert the given input to a DoubleVector and apply the given conversion factor
*/
public static DoubleVector parseDoubleVector(List<String> input, double minValue, double maxValue, double factor)
throws InputErrorException {
DoubleVector temp = new DoubleVector(input.size());
for (int i = 0; i < input.size(); i++) {
try {
double element = Input.parseDouble(input.get(i), minValue, maxValue, factor);
temp.add(element);
} catch (InputErrorException e) {
throw new InputErrorException(INP_ERR_ELEMENT, i+1, e.getMessage());
}
}
return temp;
}
/**
* Convert the given input to a DoubleVector and apply the given conversion factor
*/
public static DoubleVector parseDoubles(KeywordIndex kw, double minValue, double maxValue, Class<? extends Unit> unitType)
throws InputErrorException {
if (unitType == UserSpecifiedUnit.class)
throw new InputErrorException(INP_ERR_UNITUNSPECIFIED);
double factor = 1.0d;
int numArgs = kw.numArgs();
int numDoubles = numArgs;
boolean includeIndex = true;
// Parse the unit portion of the input
Unit unit = Input.tryParseUnit(kw.getArg(numArgs-1), unitType);
// A unit is mandatory except for dimensionless values and time values in RFC8601 date/time format
if (unit == null && unitType != DimensionlessUnit.class && unitType != TimeUnit.class)
throw new InputErrorException(INP_ERR_NOUNITFOUND, kw.getArg(numArgs-1), unitType.getSimpleName());
if (unit != null) {
factor = unit.getConversionFactorToSI();
numDoubles = numArgs - 1;
}
// Parse the numeric portion of the input
DoubleVector temp = new DoubleVector(numDoubles);
for (int i = 0; i < numDoubles; i++) {
try {
// Time input
if (unitType == TimeUnit.class) {
// RFC8601 date/time format
if (Input.isRFC8601DateTime(kw.getArg(i))) {
double element = Input.parseRFC8601DateTime(kw.getArg(i))/1e6;
if (element < minValue || element > maxValue)
throw new InputErrorException(INP_ERR_DOUBLERANGE, minValue, maxValue, temp);
temp.add(element);
}
// Normal format
else {
if (unit == null) {
includeIndex = false;
throw new InputErrorException(INP_ERR_NOUNITFOUND, kw.getArg(numArgs-1), unitType.getSimpleName());
}
double element = Input.parseDouble(kw.getArg(i), minValue, maxValue, factor);
temp.add(element);
}
}
// Non-time input
else {
double element = Input.parseDouble(kw.getArg(i), minValue, maxValue, factor);
temp.add(element);
}
} catch (InputErrorException e) {
if (includeIndex && numDoubles > 1)
throw new InputErrorException(INP_ERR_ELEMENT, i+1, e.getMessage());
else
throw e;
}
}
return temp;
}
/**
* Convert the given input to a DoubleVector and apply the given conversion factor
*/
public static DoubleVector parseDoubles(List<String> input, double minValue, double maxValue, Class<? extends Unit> unitType)
throws InputErrorException {
if (unitType == UserSpecifiedUnit.class)
throw new InputErrorException(INP_ERR_UNITUNSPECIFIED);
double factor = 1.0d;
int numDoubles = input.size();
// Parse the unit portion of the input
Unit unit = Input.tryParseUnit(input.get(numDoubles-1), unitType);
// A unit is mandatory except for dimensionless values and time values in RFC8601 date/time format
if (unit == null && unitType != DimensionlessUnit.class && unitType != TimeUnit.class)
throw new InputErrorException(INP_ERR_NOUNITFOUND, input.get(numDoubles-1), unitType.getSimpleName());
if (unit != null) {
factor = unit.getConversionFactorToSI();
numDoubles = numDoubles - 1;
}
// Parse the numeric portion of the input
DoubleVector temp = new DoubleVector(numDoubles);
for (int i = 0; i < numDoubles; i++) {
try {
// Time input
if (unitType == TimeUnit.class) {
// RFC8601 date/time format
if (Input.isRFC8601DateTime(input.get(i))) {
double element = Input.parseRFC8601DateTime(input.get(i))/1e6;
if (element < minValue || element > maxValue)
throw new InputErrorException(INP_ERR_DOUBLERANGE, minValue, maxValue, temp);
temp.add(element);
}
// Normal format
else {
if (unit == null)
throw new InputErrorException(INP_ERR_NOUNITFOUND, input.get(numDoubles-1), unitType.getSimpleName());
double element = Input.parseDouble(input.get(i), minValue, maxValue, factor);
temp.add(element);
}
}
// Non-time input
else {
double element = Input.parseDouble(input.get(i), minValue, maxValue, factor);
temp.add(element);
}
} catch (InputErrorException e) {
if (numDoubles == 1)
throw e;
else
throw new InputErrorException(INP_ERR_ELEMENT, i+1, e.getMessage());
}
}
return temp;
}
public static String parseString(String input, ArrayList<String> validList)
throws InputErrorException {
return parseString(input, validList, false);
}
public static String parseString(String input, ArrayList<String> validList, boolean caseSensitive)
throws InputErrorException {
for (String valid : validList) {
if (caseSensitive && valid.equals(input))
return valid;
if (!caseSensitive && valid.equalsIgnoreCase(input))
return valid;
}
throw new InputErrorException(INP_ERR_BADCHOICE, validList.toString(), input);
}
public static ArrayList<String> parseStrings(KeywordIndex kw, ArrayList<String> validList, boolean caseSensitive)
throws InputErrorException {
ArrayList<String> temp = new ArrayList<>(kw.numArgs());
for (int i = 0; i < kw.numArgs(); i++) {
try {
String element = Input.parseString(kw.getArg(i), validList);
temp.add(element);
} catch (InputErrorException e) {
throw new InputErrorException(INP_ERR_ELEMENT, i+1, e.getMessage());
}
}
return temp;
}
public static <T extends Enum<T>> T parseEnum(Class<T> aClass, String input) {
try {
return Enum.valueOf(aClass, input);
} catch (IllegalArgumentException e) {
throw new InputErrorException(INP_ERR_BADCHOICE, Arrays.toString(aClass.getEnumConstants()), input);
} catch (NullPointerException e) {
throw new InputErrorException(INP_ERR_BADCHOICE, Arrays.toString(aClass.getEnumConstants()), input);
}
}
public static Class<? extends Entity> parseEntityType(String input)
throws InputErrorException {
ObjectType type = Input.tryParseEntity( input, ObjectType.class );
if (type == null)
throw new InputErrorException("Entity type not found: %s", input);
Class<? extends Entity> klass = type.getJavaClass();
if (klass == null)
throw new InputErrorException("ObjectType %s does not have a java class set", input);
return klass;
}
public static void assertUnitsMatch(Class<? extends Unit> u1, Class<? extends Unit> u2)
throws InputErrorException {
if (u1 != u2)
throw new InputErrorException(INP_ERR_UNITS);
}
public static <T extends Entity> Class<? extends T> checkCast(Class<? extends Entity> klass, Class<T> parent) {
try {
return klass.asSubclass(parent);
}
catch (ClassCastException e) {
throw new InputErrorException(INP_ERR_NOTSUBCLASS, parent.getName(), klass.getName());
}
}
public static <T> T castImplements(Entity ent, Class<T> klass)
throws InputErrorException {
try {
return klass.cast(ent);
}
catch (ClassCastException e) {
throw new InputErrorException(INP_ERR_INTERFACE, klass.getName(), ent.getName());
}
}
private static <T extends Entity> T castEntity(Entity ent, Class<T> aClass)
throws InputErrorException {
try {
return aClass.cast(ent);
}
catch (ClassCastException e) {
return null;
}
}
public static <T extends Entity> T parseEntity(String choice, Class<T> aClass)
throws InputErrorException {
Entity ent = Entity.getNamedEntity(choice);
if (ent == null) {
throw new InputErrorException(INP_ERR_ENTNAME, choice);
}
T t = Input.castEntity(ent, aClass);
if (t == null) {
throw new InputErrorException(INP_ERR_ENTCLASS, aClass.getSimpleName(), choice, ent.getClass().getSimpleName());
}
return t;
}
public static <T extends Entity> T tryParseEntity(String choice, Class<T> aClass) {
return Input.castEntity(Entity.getNamedEntity(choice), aClass);
}
public static <T extends Unit> T tryParseUnit(String choice, Class<T> aClass) {
return Input.castEntity(Entity.getNamedEntity(choice), aClass);
}
public static Unit parseUnit(String str)
throws InputErrorException {
Unit u = Input.tryParseUnit(str, Unit.class);
if (u == null)
throw new InputErrorException("Could not find a unit named: %s", str);
return u;
}
public static <T extends Entity> ArrayList<T> parseEntityList(KeywordIndex kw, Class<T> aClass, boolean unique)
throws InputErrorException {
ArrayList<T> temp = new ArrayList<>(kw.numArgs());
for (int i = 0; i < kw.numArgs(); i++) {
Entity ent = Entity.getNamedEntity(kw.getArg(i));
if (ent == null) {
throw new InputErrorException(INP_ERR_ENTNAME, kw.getArg(i));
}
// If we found a group, expand the list of Entities
if (ent instanceof Group && aClass != Group.class) {
ArrayList<Entity> gList = ((Group)ent).getList();
for (int j = 0; j < gList.size(); j++) {
T t = Input.castEntity(gList.get(j), aClass);
if (t == null) {
throw new InputErrorException(INP_ERR_ENTCLASS, aClass.getSimpleName(), gList.get(j), gList.get(j).getClass().getSimpleName());
}
temp.add(t);
}
} else {
T t = Input.castEntity(ent, aClass);
if (t == null) {
throw new InputErrorException(INP_ERR_ENTCLASS, aClass.getSimpleName(), kw.getArg(i), ent.getClass().getSimpleName());
}
temp.add(t);
}
}
if (unique)
Input.assertUnique(temp);
return temp;
}
public static <T extends Entity> ArrayList<T> parseEntityList(List<String> input, Class<T> aClass, boolean unique)
throws InputErrorException {
ArrayList<T> temp = new ArrayList<>(input.size());
for (int i = 0; i < input.size(); i++) {
Entity ent = Entity.getNamedEntity(input.get(i));
if (ent == null) {
throw new InputErrorException(INP_ERR_ENTNAME, input.get(i));
}
// If we found a group, expand the list of Entities
if (ent instanceof Group && aClass != Group.class) {
ArrayList<Entity> gList = ((Group)ent).getList();
for (int j = 0; j < gList.size(); j++) {
T t = Input.castEntity(gList.get(j), aClass);
if (t == null) {
throw new InputErrorException(INP_ERR_ENTCLASS, aClass.getSimpleName(), gList.get(j), gList.get(j).getClass().getSimpleName());
}
temp.add(t);
}
} else {
T t = Input.castEntity(ent, aClass);
if (t == null) {
throw new InputErrorException(INP_ERR_ENTCLASS, aClass.getSimpleName(), input.get(i), ent.getClass().getSimpleName());
}
temp.add(t);
}
}
if (unique)
Input.assertUnique(temp);
return temp;
}
public static <T extends Entity> ArrayList<ArrayList<T>> parseListOfEntityLists(KeywordIndex kw, Class<T> aClass, boolean unique)
throws InputErrorException {
ArrayList<KeywordIndex> subArgs = kw.getSubArgs();
ArrayList<ArrayList<T>> temp = new ArrayList<>(subArgs.size());
for (int i = 0; i < subArgs.size(); i++) {
try {
ArrayList<T> element = Input.parseEntityList(subArgs.get(i), aClass, unique);
temp.add(element);
} catch (InputErrorException e) {
throw new InputErrorException(INP_ERR_ELEMENT, i+1, e.getMessage());
}
}
return temp;
}
public static <T> T parseInterfaceEntity(String choice, Class<T> aClass) {
Entity ent = Entity.getNamedEntity(choice);
if (ent == null) {
throw new InputErrorException(INP_ERR_ENTNAME, choice);
}
T temp = Input.castImplements(ent, aClass);
if (temp == null) {
throw new InputErrorException(INP_ERR_ENTCLASS, aClass.getSimpleName(), choice, ent.getClass().getSimpleName());
}
return temp;
}
public static <T> ArrayList<T> parseInterfaceEntityList(KeywordIndex kw, Class<T> aClass, boolean unique)
throws InputErrorException {
ArrayList<T> temp = new ArrayList<>(kw.numArgs());
for (int i = 0; i < kw.numArgs(); i++) {
Entity ent = Entity.getNamedEntity(kw.getArg(i));
if (ent == null) {
throw new InputErrorException(INP_ERR_ENTNAME, kw.getArg(i));
}
// If we found a group, expand the list of Entities
if (ent instanceof Group) {
ArrayList<Entity> gList = ((Group)ent).getList();
for (int j = 0; j < gList.size(); j++) {
T t = Input.castImplements(gList.get(j), aClass);
if (t == null) {
throw new InputErrorException(INP_ERR_ENTCLASS, aClass.getSimpleName(), gList.get(j), gList.get(j).getClass().getSimpleName());
}
temp.add(t);
}
} else {
T t = Input.castImplements(ent, aClass);
if (t == null) {
throw new InputErrorException(INP_ERR_ENTCLASS, aClass.getSimpleName(), kw.getArg(i), ent.getClass().getSimpleName());
}
temp.add(t);
}
}
if (unique)
Input.assertUniqueInterface(temp);
return temp;
}
public static Color4d parseColour(KeywordIndex kw) {
Input.assertCountRange(kw, 1, 4);
// Color names
if (kw.numArgs() <= 2) {
Color4d colAtt = ColourInput.getColorWithName(kw.getArg(0).toLowerCase());
if( colAtt == null )
throw new InputErrorException( "Color " + kw.getArg( 0 ) + " not found" );
if (kw.numArgs() == 1)
return colAtt;
double a = Input.parseDouble(kw.getArg(1), 0.0d, 255.0d);
if (a > 1.0f)
a /= 255.0d;
return new Color4d(colAtt.r, colAtt.b, colAtt.g, a);
}
// RGB
else {
DoubleVector dbuf = Input.parseDoubles(kw, 0.0d, 255.0d, DimensionlessUnit.class);
double r = dbuf.get(0);
double g = dbuf.get(1);
double b = dbuf.get(2);
double a = 1.0d;
if (dbuf.size() == 4)
a = dbuf.get(3);
if (r > 1.0f || g > 1.0f || b > 1.0f) {
r /= 255.0d;
g /= 255.0d;
b /= 255.0d;
}
if (a > 1.0f) {
a /= 255.0d;
}
return new Color4d(r, g, b, a);
}
}
public static StringProvider parseStringProvider(KeywordIndex kw, Entity thisEnt, Class<? extends Unit> unitType) {
// Parse the input as a StringProvExpression
if (kw.numArgs() == 1) {
try {
return new StringProvExpression(kw.getArg(0), thisEnt, unitType);
} catch (ExpError e) {}
}
// Parse the input as a SampleProvider object
SampleProvider samp = Input.parseSampleExp(kw, thisEnt, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, unitType);
return new StringProvSample(samp);
}
public static SampleProvider parseSampleExp(KeywordIndex kw,
Entity thisEnt, double minValue, double maxValue, Class<? extends Unit> unitType) {
if (unitType == UserSpecifiedUnit.class)
throw new InputErrorException(INP_ERR_UNITUNSPECIFIED);
// If there are exactly two inputs, then it must be a number and its unit
if (kw.numArgs() == 2) {
if (unitType == DimensionlessUnit.class)
throw new InputErrorException(INP_ERR_COUNT, 1, kw.argString());
DoubleVector tmp = Input.parseDoubles(kw, minValue, maxValue, unitType);
return new SampleConstant(unitType, tmp.get(0));
}
// If there is only one input, it could be a SampleProvider, a dimensionless constant, or an expression
// 1) Try parsing a SampleProvider object
SampleProvider s = null;
try {
Entity ent = Input.parseEntity(kw.getArg(0), Entity.class);
s = Input.castImplements(ent, SampleProvider.class);
}
catch (InputErrorException e) {}
if (s != null) {
Input.assertUnitsMatch(unitType, s.getUnitType());
return s;
}
// 2) Try parsing a constant value
DoubleVector tmp = null;
try {
tmp = Input.parseDoubles(kw, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, DimensionlessUnit.class);
}
catch (InputErrorException e) {}
if (tmp != null) {
if (unitType != DimensionlessUnit.class)
throw new InputErrorException(INP_ERR_UNITNOTFOUND, unitType.getSimpleName());
if (tmp.get(0) < minValue || tmp.get(0) > maxValue)
throw new InputErrorException(INP_ERR_DOUBLERANGE, minValue, maxValue, tmp.get(0));
return new SampleConstant(unitType, tmp.get(0));
}
// 3) Try parsing an expression
try {
String expString = kw.getArg(0);
return new SampleExpression(expString, thisEnt, unitType);
}
catch (ExpError e) {
throw new InputErrorException(e);
}
}
private static void assertUnique(ArrayList<? extends Entity> list) {
for (int i = 0; i < list.size(); i++) {
Entity ent = list.get(i);
for (int j = i + 1; j < list.size(); j++) {
if (ent == list.get(j)) {
throw new InputErrorException(INP_ERR_NOTUNIQUE, ent.getName());
}
}
}
}
private static void assertUniqueInterface(ArrayList<?> list) {
for (int i = 0; i < list.size(); i++) {
Entity ent = (Entity)list.get(i);
for (int j = i + 1; j < list.size(); j++) {
if (ent == list.get(j)) {
throw new InputErrorException(INP_ERR_NOTUNIQUE, ent.getName());
}
}
}
}
public static void validateIndexedLists(ListInput<?> keys, ListInput<?> vals)
throws InputErrorException {
// If no values set, no validation to be done
if (vals.getValue() == null)
return;
// values are set but indexed list has not
if (keys.getValue() == null)
throw new InputErrorException(INP_VAL_LISTSET, keys.getKeyword(), vals.getKeyword());
// Both are set, but of differing size
if (keys.getListSize() != vals.getListSize())
throw new InputErrorException(INP_VAL_LISTSIZE, keys.getKeyword(), vals.getKeyword());
}
public static void validateInputSize(ListInput<?> list1, ListInput<?> list2)
throws InputErrorException {
// One list is set but not the other
if (list1.getValue() != null && list2.getValue() == null)
throw new InputErrorException(INP_VAL_LISTSIZE, list1.getKeyword(), list2.getKeyword());
if (list1.getValue() == null && list2.getValue() != null)
throw new InputErrorException(INP_VAL_LISTSIZE, list1.getKeyword(), list2.getKeyword());
// Both are set, but of differing size
if (list1.getListSize() != list2.getListSize())
throw new InputErrorException(INP_VAL_LISTSIZE, list1.getKeyword(), list2.getKeyword() );
}
public static void validateIndexedLists(ArrayList<?> keys, DoubleVector values, String keyName, String valueName)
throws InputErrorException {
// If no values set, no validation to be done
if (values == null)
return;
// values are set but indexed list has not
if (keys == null)
throw new InputErrorException(INP_VAL_LISTSET, valueName, keyName);
// Both are set, but of differing size
if (keys.size() != values.size())
throw new InputErrorException(INP_VAL_LISTSIZE, keyName, valueName);
}
/**
* Returns a list of valid options if the input has limited number of
* choices (e.g TRUE or FALSE for BooleanInput).
* <p>
* This method must be overridden for an input to be shown with a drop-down
* menu in the Input Editor.
*/
public ArrayList<String> getValidOptions() {
if (defValue != null && defValue.getClass() == Boolean.class) {
ArrayList<String> validOptions = new ArrayList<>();
validOptions.add("TRUE");
validOptions.add("FALSE");
return validOptions;
}
else
return null;
}
public String getDefaultStringForKeyInputs(Class<? extends Unit> unitType) {
if (defValue == null)
return "";
if (defValue.getClass() == Boolean.class) {
if((Boolean)defValue)
return "TRUE";
return "FALSE";
}
StringBuilder tmp = new StringBuilder();
if (defValue.getClass() == Double.class ||
defValue.getClass() == Integer.class ||
Entity.class.isAssignableFrom(Double.class)) {
if (defValue.equals(Integer.MAX_VALUE) || defValue.equals(Double.POSITIVE_INFINITY))
return POSITIVE_INFINITY;
if (defValue.equals(Integer.MIN_VALUE) || defValue.equals(Double.NEGATIVE_INFINITY))
return NEGATIVE_INFINITY;
tmp.append(defValue);
} else if (defValue.getClass() == SampleConstant.class ||
defValue.getClass() == TimeSeriesConstantDouble.class ) {
return defValue.toString();
} else if (defValue.getClass() == DoubleVector.class) {
DoubleVector def = (DoubleVector)defValue;
if (def.size() == 0)
return "";
tmp.append(def.get(0));
for (int i = 1; i < def.size(); i++) {
tmp.append(SEPARATOR);
tmp.append(def.get(i));
}
} else if (defValue.getClass() == IntegerVector.class) {
IntegerVector def = (IntegerVector)defValue;
if (def.size() == 0)
return "";
tmp.append(def.get(0));
for (int i = 1; i < def.size(); i++) {
tmp.append(SEPARATOR);
tmp.append(def.get(i));
}
} else if ( Entity.class.isAssignableFrom( defValue.getClass() ) ) {
tmp.append(((Entity)defValue).getName());
}
else {
return "?????";
}
tmp.append(SEPARATOR);
tmp.append(Unit.getSIUnit(unitType));
return tmp.toString();
}
}