/**
* Copyright (C) 2010-14 diirt developers. See COPYRIGHT.TXT
* All rights reserved. Use is subject to license terms. See LICENSE.TXT
*/
package org.diirt.datasource.sim;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Utility class to parse variable names and create simulated signals.
*
* @author carcassi
*/
class NameParser {
static final Pattern doubleParameter = Pattern.compile("\\s*([-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?)\\s*");
static final Pattern commaSeparatedDoubles = Pattern.compile(doubleParameter + "(," + doubleParameter + ")*");
static final Pattern functionAndParameter = Pattern.compile("(\\w+)(\\(((" + commaSeparatedDoubles + ")?)\\))?");
static final Pattern functionAndStringParameter = Pattern.compile("(\\w+)(\\((\".*\")\\))?");
/**
* Parses a comma separated list of arguments and returns them as a list.
*
* @param string a comma separated list of arguments; if null or empty returns
* the empty list
* @return the list of parsed arguments
*/
static List<Object> parseParameters(String string) {
// Argument is empty
if (string == null || "".equals(string))
return Collections.emptyList();
// Validate input
if (!commaSeparatedDoubles.matcher(string).matches()) {
throw new IllegalArgumentException("Arguments must be a comma separated list of double values (was " + string + ")");
}
// Parse parameters
Matcher matcher = doubleParameter.matcher(string);
List<Object> parameters = new ArrayList<Object>();
while (matcher.find()) {
String parameter = matcher.group();
Double value = Double.parseDouble(parameter);
parameters.add(value);
}
return parameters;
}
/**
* Parse a function with parameters and returns a list where the first
* element is the function name and the others are the parsed arguments.
*
* @param string a string representing a function
* @return the name and the parameters
*/
static List<Object> parseFunction(String string) {
Matcher matcher = functionAndParameter.matcher(string);
// Match comma separate double list
if (matcher.matches()) {
List<Object> parameters = new ArrayList<Object>();
parameters.add(matcher.group(1));
parameters.addAll(parseParameters(matcher.group(3)));
return parameters;
}
// Match string parameter
matcher = functionAndStringParameter.matcher(string);
if (matcher.matches()) {
List<Object> parameters = new ArrayList<Object>();
parameters.add(matcher.group(1));
String quotedString = matcher.group(3);
parameters.add(quotedString.substring(1, quotedString.length() - 1));
return parameters;
}
throw new IllegalArgumentException("Syntax error: function should be like xxx(num1, num2, ...) or xxx(\"string\") and was " + string);
}
/**
* Given a string representing a function call, finds the appropriate call
* matching the function name, and the appropriate constructor and instantiates
* it.
*
* @param string the function call
* @return the function
*/
static Simulation<?> createFunction(String string) {
List<Object> parameters = parseFunction(string);
StringBuilder className = new StringBuilder("org.diirt.datasource.sim.");
int firstCharPosition = className.length();
className.append((String) parameters.get(0));
className.setCharAt(firstCharPosition, Character.toUpperCase(className.charAt(firstCharPosition)));
try {
@SuppressWarnings("unchecked")
Class<SimFunction<?>> clazz = (Class<SimFunction<?>>) Class.forName(className.toString());
Object[] constructorParams = parameters.subList(1, parameters.size()).toArray();
Class[] types = new Class[constructorParams.length];
for (int i = 0; i < types.length; i++) {
types[i] = constructorParams[i].getClass();
}
return clazz.getConstructor(types).newInstance(constructorParams);
} catch (ClassNotFoundException ex) {
throw new RuntimeException("Simulation channel " + parameters.get(0) + " is not defined");
} catch (NoClassDefFoundError ex) {
if (ex.getMessage().contains("wrong name") && ex.getMessage().lastIndexOf("/") != -1) {
String suggestedName = ex.getMessage().substring(ex.getMessage().lastIndexOf("/") + 1, ex.getMessage().length() - 1);
throw new RuntimeException("Function " + parameters.get(0) + " is not defined (Looking for " + suggestedName + "?)");
}
throw new RuntimeException("Function " + parameters.get(0) + " is not defined");
} catch (NoSuchMethodException ex) {
throw new RuntimeException("Wrong parameter number for function " + parameters.get(0));
} catch (SecurityException ex) {
throw new RuntimeException("Constructor for " + parameters.get(0) + " should be at least package private");
} catch (InstantiationException ex) {
throw new RuntimeException("Constructor for " + parameters.get(0) + " failed", ex);
} catch (IllegalAccessException ex) {
throw new RuntimeException("Constructor for " + parameters.get(0) + " should be at least package private");
} catch (IllegalArgumentException ex) {
throw new RuntimeException("Wrong parameter type for function " + parameters.get(0));
} catch (InvocationTargetException ex) {
throw new RuntimeException(ex.getCause().getMessage(), ex);
}
}
}