/*
* Copyright (c) 2014 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.hds.xmlgen;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.milyn.payload.JavaResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.hds.HDSException;
import com.emc.storageos.hds.util.SmooksUtil;
import com.emc.storageos.hds.xmlgen.beans.Operation;
import com.emc.storageos.hds.xmlgen.beans.XmlElementSequenceAttribute;
public class InputXMLGenerationClient {
private static final Logger log = LoggerFactory
.getLogger(InputXMLGenerationClient.class);
public static String getInputXMLString(String requestedOperationName,
Map<String, Object> attributeMap, String xmlInputContextFile,
String smooksConfigFile) {
String operationInputXMLString = null;
InputStream stream = null;
try {
List<Operation> availableOperations = new ArrayList<Operation>();
log.debug("XML Context File name to be loaded: {}", xmlInputContextFile);
stream = InputXMLGenerationClient.class.getClass().getResourceAsStream(
xmlInputContextFile);
JavaResult javaResult = SmooksUtil.getParsedXMLJavaResult(stream,
smooksConfigFile);
@SuppressWarnings("unchecked")
List<Operation> operationList = (List<Operation>) javaResult
.getBean(XMLConstants.OPERATION_LIST_BEAN_NAME);
if (null != operationList && !operationList.isEmpty()) {
log.debug("{} operations found in configuration file",
operationList.size());
for (Operation operation : operationList) {
if (operation.getName().equalsIgnoreCase(requestedOperationName)) {
log.debug("Found matching operation {}", operation.getName());
availableOperations.add(operation);
}
}
Operation operation = getModelSupportedOperation(attributeMap,
availableOperations);
if (null != operation) {
operationInputXMLString = generateXMLString(operation, attributeMap);
} else {
log.error("No Operation found with the given model");
HDSException.exceptions
.unableToGenerateInputXmlDueToUnSupportedModelFound();
}
} else {
log.error("No operation list found to generate input xml.");
HDSException.exceptions.unableToGenerateInputXmlDueToNoOperations();
}
} catch (Exception ex) {
HDSException.exceptions.unableToGenerateInputXmlForGivenRequest(ex
.getLocalizedMessage());
}
return operationInputXMLString;
}
/**
* This method will make sure that if a new model is used to test then the
* operation with model "default" will be used to execute commands.
*
* @param attributeMap
* @param availableOperations
* @return
*/
private static Operation getModelSupportedOperation(Map<String, Object> attributeMap,
List<Operation> availableOperations) {
String requestedSystemModel = getModelFromMap(attributeMap);
Operation operationToUse = null;
if (null != availableOperations && !availableOperations.isEmpty()) {
for (Operation operation : availableOperations) {
List<String> supportedModelsFromOperation = Arrays.asList(operation
.getModels().split(XMLConstants.COMMA_OPERATOR));
if (supportedModelsFromOperation.contains(requestedSystemModel)) {
operationToUse = operation;
break;
} else if (supportedModelsFromOperation
.contains(XMLConstants.DEFAULT_MODEL)) {
operationToUse = operation;
}
}
}
return operationToUse;
}
/**
* Return the model if it is present in the attributeMap else return any.
*
* @param attributeMap
* @return
*/
private static String getModelFromMap(Map<String, Object> attributeMap) {
if (!attributeMap.containsKey(XMLConstants.MODEL)) {
return XMLConstants.DEFAULT_MODEL;
}
return attributeMap.get(XMLConstants.MODEL).toString();
}
/**
* Generates the XML String for the given operation & attributemap.
*
* @param operation
* @param attributeMap
* @return
* @throws IllegalAccessException
* @throws IllegalArgumentException
* @throws InvocationTargetException
* @throws ClassNotFoundException
* @throws NoSuchMethodException
* @throws SecurityException
*/
private static String generateXMLString(Operation operation,
Map<String, Object> attributeMap) throws IllegalAccessException,
IllegalArgumentException, InvocationTargetException, ClassNotFoundException,
NoSuchMethodException, SecurityException {
StringBuilder xmlNodeWithAttributes = new StringBuilder(XMLConstants.XMl_START_ELEMENT);
List<String> sequenceList = Arrays.asList(operation.getXmlElementSequence().split(
XMLConstants.COMMA_OPERATOR));
Class noparams[] = {};
LinkedList<String> xmlNodesToClose = new LinkedList<String>();
List<String> preClosedXMLNodes = new ArrayList<String>();
Map<String, XmlElementSequenceAttribute> elementAttributes = getElementProperites(operation
.getXmlElementSequenceAttributeList());
Iterator<String> sequenceItr = sequenceList.iterator();
while (sequenceItr.hasNext()) {
String elementName = sequenceItr.next().toString();
if (elementAttributes.containsKey(elementName)) {
XmlElementSequenceAttribute sequenceAttrs = elementAttributes
.get(elementName);
if (null != sequenceAttrs.getModel()) {
String type = sequenceAttrs.getType();
String model = sequenceAttrs.getModel();
String elementKey = (null == type) ? elementName : elementName
+ XMLConstants.UNDERSCORE_OP + type;
if (attributeMap.containsKey(elementKey)) {
log.debug("Found key {} in map", elementKey);
Object modelValue = attributeMap.get(elementKey);
if (modelValue instanceof List) {
List<Object> elementList = (List<Object>) modelValue;
if (null != elementList && !elementList.isEmpty()) {
for (Object object : elementList) {
log.debug("list instance : {} object: {}", elementName,
object);
xmlNodeWithAttributes.append(XMLConstants.LESS_THAN_OP)
.append(elementName);
Class cls = Class.forName(model);
Method method = cls.getDeclaredMethod(
XMLConstants.METHOD_TO_INVOKE, noparams);
Object output = method.invoke(object, null);
xmlNodeWithAttributes.append(XMLConstants.SPACE)
.append(output.toString())
.append(XMLConstants.GREATER_THAN_OP);
log.debug("getChildExists: {}", sequenceAttrs.getChildExists());
if (null != sequenceAttrs.getChildExists() && sequenceAttrs.getChildExists()) {
String childNodeStr = processIfChildNodesExists(
cls, object, noparams, elementName);
if (null != childNodeStr && !childNodeStr.isEmpty()) {
xmlNodeWithAttributes.append(childNodeStr);
}
}
xmlNodeWithAttributes.append(XMLConstants.XML_CLOSING_START_TAG)
.append(elementName).append(XMLConstants.GREATER_THAN_OP);
}
}
} else {
log.debug("no list: {}", elementName);
xmlNodeWithAttributes.append(XMLConstants.LESS_THAN_OP)
.append(elementName);
Class cls = Class.forName(model);
Method method = cls.getDeclaredMethod(
XMLConstants.METHOD_TO_INVOKE, noparams);
Object output = method.invoke(modelValue, null);
log.debug("object : {}", output);
xmlNodeWithAttributes.append(XMLConstants.SPACE)
.append(output.toString())
.append(XMLConstants.GREATER_THAN_OP);
xmlNodesToClose.addFirst(elementName);
}
}
}
if (null != sequenceAttrs.getProperties()
&& !sequenceAttrs.getProperties().isEmpty()) {
log.debug("Found properties for element name: {}", elementName);
xmlNodeWithAttributes.append(XMLConstants.LESS_THAN_OP).append(
elementName);
String attributeNames[] = sequenceAttrs.getProperties().split(
XMLConstants.COMMA_OPERATOR);
int count = 0;
for (String attributeName : attributeNames) {
String attributeValue = null;
if (null != sequenceAttrs.getValues()) {
String defaultValuesFromXML[] = sequenceAttrs.getValues()
.split(XMLConstants.COMMA_OPERATOR);
if (null != defaultValuesFromXML[count]) {
attributeValue = defaultValuesFromXML[count];
count++;
}
}
if (null == attributeValue) {
attributeValue = attributeMap.get(
elementName + XMLConstants.UNDERSCORE_OP
+ attributeName).toString();
}
if (null != attributeValue && attributeValue.length() > 0) {
xmlNodeWithAttributes.append(" ").append(
attributeName + XMLConstants.EQUAL_OP + "\""
+ attributeValue + "\" ");
}
}
}
if (operation.getXmlElementsToClose().contains(elementName)) {
preClosedXMLNodes.add(elementName);
xmlNodeWithAttributes.append(XMLConstants.XML_CLOSING_TAG);
}
} else {
if (operation.getXmlElementsToClose().contains(elementName)) {
log.debug("pre closed element: {}", elementName);
xmlNodeWithAttributes.append(XMLConstants.LESS_THAN_OP)
.append(elementName).append(XMLConstants.XML_CLOSING_TAG);
preClosedXMLNodes.add(elementName);
} else {
log.debug("No sequence element found. hence adding: {}", elementName);
xmlNodeWithAttributes.append(XMLConstants.LESS_THAN_OP)
.append(elementName).append(XMLConstants.GREATER_THAN_OP);
xmlNodesToClose.addFirst(elementName);
}
}
}
if (null != xmlNodesToClose && !xmlNodesToClose.isEmpty()) {
for (String xmlNodeToClose : xmlNodesToClose) {
if (!preClosedXMLNodes.contains(xmlNodeToClose)) {
log.debug("Closing {} element tag", xmlNodeToClose);
xmlNodeWithAttributes.append("</").append(xmlNodeToClose)
.append(XMLConstants.GREATER_THAN_OP);
}
}
}
return xmlNodeWithAttributes.toString();
}
/**
* Process the child nodes if they are present in the object which is passed in.
*
* @param cls : class to invoke
* @param object : object in which method exists.
* @param noparams : method params.
* @return
*/
private static String processIfChildNodesExists(Class cls, Object object,
Class[] noparams, String elementName) throws NoSuchMethodException,
IllegalAccessException, InvocationTargetException {
String childNodeOutput = null;
Method method = cls.getDeclaredMethod(
XMLConstants.CHILD_NODE_METHOD, noparams);
Object output = method.invoke(object, null);
if (null != output && !output.toString().isEmpty()) {
log.debug("Processing child nodes of node {}", elementName);
childNodeOutput = output.toString();
}
return childNodeOutput;
}
private static Map<String, XmlElementSequenceAttribute> getElementProperites(
List<XmlElementSequenceAttribute> xmlElementSequenceAttributeList) {
Map<String, XmlElementSequenceAttribute> elementAttributes = new HashMap<String, XmlElementSequenceAttribute>();
if (null != xmlElementSequenceAttributeList
&& !xmlElementSequenceAttributeList.isEmpty()) {
for (XmlElementSequenceAttribute xmlElementAttribute : xmlElementSequenceAttributeList) {
elementAttributes.put(xmlElementAttribute.getName(), xmlElementAttribute);
}
}
return elementAttributes;
}
}