package org.xins.common.spec;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.w3c.dom.Element;
import org.xins.common.MandatoryArgumentChecker;
import org.xins.common.xml.ElementList;
import org.xins.common.xml.ElementFormatter;
import org.xml.sax.SAXException;
public final class FunctionSpec {
private final String _functionName;
private int _cache = 0;
private String _description;
private Map<String,ParameterSpec> _inputParameters = new LinkedHashMap<String,ParameterSpec>();
private List<ParamComboSpec> _inputParamCombos = new ArrayList<ParamComboSpec>();
private Map<String,DataSectionElementSpec> _inputDataSectionElements = new LinkedHashMap<String,DataSectionElementSpec>();
private Map<String,ErrorCodeSpec> _errorCodes = new LinkedHashMap<String,ErrorCodeSpec>();
private Map<String,ParameterSpec> _outputParameters = new LinkedHashMap<String,ParameterSpec>();
private List<ParamComboSpec> _outputParamCombos = new ArrayList<ParamComboSpec>();
private Map<String,DataSectionElementSpec> _outputDataSectionElements = new LinkedHashMap<String,DataSectionElementSpec>();
FunctionSpec(String functionName, Class reference, String baseURL)
throws IllegalArgumentException, InvalidSpecificationException {
MandatoryArgumentChecker.check("functionName", functionName, "reference", reference, "baseURL", baseURL);
_functionName = functionName;
try {
Reader reader = APISpec.getReader(baseURL, functionName + ".fnc");
parseFunction(reader, reference, baseURL);
} catch (IOException ioe) {
throw new InvalidSpecificationException("[Function: " + functionName + "] Cannot read function.", ioe);
}
}
public String getName() {
return _functionName;
}
public int getCache() {
return _cache;
}
public void setCache(int cache) {
_cache = cache;
}
public String getDescription() {
return _description;
}
public ParameterSpec getInputParameter(String parameterName)
throws EntityNotFoundException, IllegalArgumentException {
MandatoryArgumentChecker.check("parameterName", parameterName);
ParameterSpec parameter = (ParameterSpec) _inputParameters.get(parameterName);
if (parameter == null) {
throw new EntityNotFoundException("Input parameter \"" + parameterName + "\" not found.");
}
return parameter;
}
public Map<String,ParameterSpec> getInputParameters() {
return Collections.unmodifiableMap(_inputParameters);
}
public ParameterSpec getOutputParameter(String parameterName)
throws IllegalArgumentException, EntityNotFoundException {
MandatoryArgumentChecker.check("parameterName", parameterName);
ParameterSpec parameter = (ParameterSpec) _outputParameters.get(parameterName);
if (parameter == null) {
throw new EntityNotFoundException("Output parameter \"" + parameterName + "\" not found.");
}
return parameter;
}
public Map<String,ParameterSpec> getOutputParameters() {
return Collections.unmodifiableMap(_outputParameters);
}
public ErrorCodeSpec getErrorCode(String errorCodeName)
throws IllegalArgumentException, EntityNotFoundException {
MandatoryArgumentChecker.check("errorCodeName", errorCodeName);
ErrorCodeSpec errorCode = _errorCodes.get(errorCodeName);
if (errorCode == null) {
throw new EntityNotFoundException("Error code \"" + errorCodeName + "\" not found.");
}
return errorCode;
}
public Map<String,ErrorCodeSpec> getErrorCodes() {
return Collections.unmodifiableMap(_errorCodes);
}
public DataSectionElementSpec getInputDataSectionElement(String elementName)
throws IllegalArgumentException, EntityNotFoundException {
MandatoryArgumentChecker.check("elementName", elementName);
DataSectionElementSpec element = _inputDataSectionElements.get(elementName);
if (element == null) {
throw new EntityNotFoundException("Input data section element \"" + elementName + "\" not found.");
}
return element;
}
public Map<String,DataSectionElementSpec> getInputDataSectionElements() {
return _inputDataSectionElements;
}
public DataSectionElementSpec getOutputDataSectionElement(String elementName)
throws IllegalArgumentException, EntityNotFoundException {
MandatoryArgumentChecker.check("elementName", elementName);
DataSectionElementSpec element = (DataSectionElementSpec) _outputDataSectionElements.get(elementName);
if (element == null) {
throw new EntityNotFoundException("Output data section element \"" + elementName + "\" not found.");
}
return element;
}
public Map<String,DataSectionElementSpec> getOutputDataSectionElements() {
return _outputDataSectionElements;
}
public List<ParamComboSpec> getInputParamCombos() {
return Collections.unmodifiableList(_inputParamCombos);
}
public List<ParamComboSpec> getOutputParamCombos() {
return Collections.unmodifiableList(_outputParamCombos);
}
private void parseFunction(Reader reader, Class reference, String baseURL)
throws IllegalArgumentException, IOException, InvalidSpecificationException {
MandatoryArgumentChecker.check("reader", reader, "reference", reference, "baseURL", baseURL);
Element function;
try {
function = ElementFormatter.parse(reader);
} catch (SAXException pe) {
throw new InvalidSpecificationException("[Function: " + _functionName + "] Cannot parse function.", pe);
}
String cacheValue = function.getAttribute("cache");
if (!cacheValue.equals("")) {
_cache = Integer.parseInt(cacheValue);
}
ElementList descriptionElementList = new ElementList(function, "description");
if (descriptionElementList.isEmpty()) {
throw new InvalidSpecificationException("[Function: " + _functionName + "] No definition specified.");
}
Element descriptionElement = descriptionElementList.get(0);
_description = descriptionElement.getTextContent();
ElementList input = new ElementList(function, "input");
if (!input.isEmpty()) {
Element inputElement = input.get(0);
_inputParameters = parseParameters(reference, inputElement);
_inputParamCombos = parseCombos(inputElement, _inputParameters, true);
ElementList dataSections = new ElementList(inputElement, "data");
if (!dataSections.isEmpty()) {
Element dataSection = dataSections.get(0);
_inputDataSectionElements = parseDataSectionElements(reference, dataSection, dataSection);
}
}
ElementList output = new ElementList(function, "output");
if (!output.isEmpty()) {
Element outputElement = output.get(0);
ElementList errorCodesList = new ElementList(outputElement, "resultcode-ref");
for (Element nextErrorCode : errorCodesList) {
String errorCodeName = nextErrorCode.getAttribute("name");
if (errorCodeName.equals("")) {
throw new InvalidSpecificationException("[Function: " + _functionName + "] Missing name attribute for a error code.");
}
if (errorCodeName.indexOf('/') != -1) {
errorCodeName = errorCodeName.substring(errorCodeName.indexOf('/') + 1);
}
ErrorCodeSpec errorCodeSpec = new ErrorCodeSpec(errorCodeName, reference, baseURL);
_errorCodes.put(errorCodeName, errorCodeSpec);
}
_outputParameters = parseParameters(reference, outputElement);
_outputParamCombos = parseCombos(outputElement, _outputParameters, true);
ElementList dataSections = new ElementList(outputElement, "data");
if (!dataSections.isEmpty()) {
Element dataSection = dataSections.get(0);
_outputDataSectionElements = parseDataSectionElements(reference, dataSection, dataSection);
}
}
}
static Map<String,DataSectionElementSpec> parseDataSectionElements(Class reference, Element topElement, Element dataSection)
throws IllegalArgumentException, InvalidSpecificationException {
MandatoryArgumentChecker.check("reference", reference, "topElement", topElement, "dataSection", dataSection);
Map<String,DataSectionElementSpec> dataSectionElements = new LinkedHashMap<String,DataSectionElementSpec>();
String dataContainsAttr = topElement.getAttribute("contains");
if (!dataContainsAttr.equals("")) {
DataSectionElementSpec dataSectionElement = getDataSectionElement(reference, dataContainsAttr, dataSection);
dataSectionElements.put(dataContainsAttr, dataSectionElement);
}
ElementList dataSectionContains = new ElementList(topElement, "contains");
if (!dataSectionContains.isEmpty()) {
Element containsElement = dataSectionContains.get(0);
ElementList contained = new ElementList(containsElement, "contained");
for (Element containedElement : contained) {
String name = containedElement.getAttribute("element");
DataSectionElementSpec dataSectionElement = getDataSectionElement(reference, name, dataSection);
dataSectionElements.put(name, dataSectionElement);
}
}
return dataSectionElements;
}
static DataSectionElementSpec getDataSectionElement(Class reference, String name, Element dataSection)
throws IllegalArgumentException, InvalidSpecificationException {
MandatoryArgumentChecker.check("reference", reference, "name", name, "dataSection", dataSection);
ElementList elements = new ElementList(dataSection, "element");
for (Element nextElement : elements) {
String nextName = nextElement.getAttribute("name");
if (name.equals(nextName)) {
String description = new ElementList(nextElement, "description").get(0).getTextContent();
Map subElements = parseDataSectionElements(reference, nextElement, dataSection);
boolean isPcdataEnable = false;
ElementList dataSectionContains = new ElementList(nextElement, "contains");
if (!dataSectionContains.isEmpty()) {
Element containsElement = dataSectionContains.get(0);
List pcdata = new ElementList(containsElement, "pcdata");
if (!pcdata.isEmpty()) {
isPcdataEnable = true;
}
}
ElementList attributesList = new ElementList(nextElement, "attribute");
Map<String, ParameterSpec> attributes = new LinkedHashMap();
for (Element nextAttribute : attributesList) {
ParameterSpec attribute = parseParameter(reference, nextAttribute);
attributes.put(attribute.getName(), attribute);
}
List attributeCombos = parseCombos(nextElement, attributes, false);
DataSectionElementSpec result = new DataSectionElementSpec(nextName,
description, isPcdataEnable, subElements, attributes, attributeCombos);
return result;
}
}
return null;
}
static ParameterSpec parseParameter(Class reference, Element paramElement)
throws IllegalArgumentException, InvalidSpecificationException {
MandatoryArgumentChecker.check("reference", reference, "paramElement", paramElement);
String parameterName = paramElement.getAttribute("name");
if (parameterName.equals("")) {
throw new InvalidSpecificationException("Missing name for a parameter.");
}
String parameterTypeName = paramElement.getAttribute("type");
boolean requiredParameter = "true".equals(paramElement.getAttribute("required"));
ElementList descriptionElementList = new ElementList(paramElement, "description");
String parameterDefaultValue = paramElement.getAttribute("default");
if (descriptionElementList.isEmpty()) {
throw new InvalidSpecificationException("No definition specified for a parameter.");
}
String parameterDescription = descriptionElementList.get(0).getTextContent();
ParameterSpec parameter = new ParameterSpec(reference ,parameterName,
parameterTypeName, requiredParameter, parameterDescription, parameterDefaultValue);
return parameter;
}
static Map<String, ParameterSpec> parseParameters(Class reference, Element topElement)
throws IllegalArgumentException, InvalidSpecificationException {
MandatoryArgumentChecker.check("reference", reference, "topElement", topElement);
ElementList parametersList = new ElementList(topElement, "param");
Map<String, ParameterSpec> parameters = new LinkedHashMap<String, ParameterSpec>();
for (Element nextParameter : parametersList) {
ParameterSpec parameter = parseParameter(reference, nextParameter);
parameters.put(parameter.getName(), parameter);
}
return parameters;
}
static List parseCombos(Element topElement, Map parameters, boolean paramCombo)
throws IllegalArgumentException, InvalidSpecificationException {
MandatoryArgumentChecker.check("topElement", topElement, "parameters", parameters);
String comboTag = paramCombo ? "param-combo" : "attribute-combo";
String referenceTag = paramCombo ? "param-ref" : "attribute-ref";
ElementList paramCombosList = new ElementList(topElement, comboTag);
List paramCombos = new ArrayList(paramCombosList.size());
for (Element nextParamCombo : paramCombosList) {
String type = nextParamCombo.getAttribute("type");
if (type.equals("")) {
throw new InvalidSpecificationException("No type defined for " + comboTag + ".");
}
ElementList paramDefs = new ElementList(nextParamCombo, referenceTag);
Map paramComboParameters = new LinkedHashMap();
for (Element paramDef : paramDefs) {
String parameterName = paramDef.getAttribute("name");
if (parameterName.equals("")) {
throw new InvalidSpecificationException("Missing name for a parameter in " + comboTag + ".");
}
ParameterSpec parameter = (ParameterSpec) parameters.get(parameterName);
if (parameter == null) {
throw new InvalidSpecificationException("Incorrect parameter name \"" +
parameterName + "\" in " + comboTag + ".");
}
paramComboParameters.put(parameterName, parameter);
}
if (paramCombo) {
ParamComboSpec paramComboSpec = new ParamComboSpec(type, paramComboParameters);
paramCombos.add(paramComboSpec);
} else {
AttributeComboSpec paramComboSpec = new AttributeComboSpec(type, paramComboParameters);
paramCombos.add(paramComboSpec);
}
}
return paramCombos;
}
}