/*
* Copyright 2014, The Sporting Exchange Limited
*
* 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.betfair.cougar.transformations.manglers;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* BSIDL 3.1 supports defining a response as both a "response" and a "simpleResponse". This mangler
* simply converts "response" elements to "simpleResponse" elements so that we can continue to
* work with the same old ftls. It throws an IllegalArgumentException if the idd specifies both - this is
* allowed by the xsd, but is complete nonsense.
* <p/>
* If at some point support for "simpleResponse" is dropped from BSIDL then this mangler can be removed and
* the ftls updated to check for "response" instead.
*/
public class ResponseToSimpleResponseMangler extends AbstractMangler {
@Override
public String getName() {
return "ResponseToSimpleResponseMangler";
}
@Override
public void mangleDocument(Node doc) {
final XPathFactory factory = XPathFactory.newInstance();
try {
//Get all parameters nodes, and recurse through to find any that have a "response" element defined
NodeList nodes = (NodeList)factory.newXPath().evaluate("//parameters", doc, XPathConstants.NODESET);
if (nodes != null) {
for (int i = 0; i < nodes.getLength(); i++) {
Node parametersNode = nodes.item(i);
Node toBeReplaced = getNodeToBeReplaced(parametersNode);
if (toBeReplaced != null) {
//There is no way to simply change the qualifiedName or localName of the element, so we need to
//go through all this palarva of making a copy and replacing it
String qualifiedName = toBeReplaced.getPrefix() != null ? toBeReplaced.getPrefix() + ".simpleResponse" : "simpleResponse";
Element replacementNode = parametersNode.getOwnerDocument().createElementNS(toBeReplaced.getNamespaceURI(), qualifiedName);
//Copy all the child nodes (which surprisingly does not include attributes, even though they are nodes)
NodeList replacementChildNodes = toBeReplaced.getChildNodes();
if (replacementChildNodes != null) {
for (int j = 0; j < replacementChildNodes.getLength(); j++) {
replacementNode.appendChild(replacementChildNodes.item(j).cloneNode(true));
}
}
//Copy all the attributes
NamedNodeMap attributes = toBeReplaced.getAttributes();
if (attributes != null) {
for (int j = 0; j < attributes.getLength(); j++) {
Attr attribute = (Attr)attributes.item(j);
//Strangely, it is possible to get a null attribute from this collection... check for it
if (attribute != null) {
replacementNode.setAttributeNode((Attr)attribute.cloneNode(true));
}
}
}
//Replace the existing node with a new
parametersNode.replaceChild(replacementNode, toBeReplaced);
}
}
}
} catch (XPathExpressionException e) {
throw new IllegalArgumentException("XPath failed to get parameters", e);
}
}
//Check for a "response" element, which will need to be converted to a "simpleResponse" element
//Also ensures only one of the two has been defined
private Node getNodeToBeReplaced(Node parametersNode) {
Node toBeReplaced = null;
int responseCount = 0;
NodeList childNodes = parametersNode.getChildNodes();
if (childNodes != null) {
for (int j = 0; j < childNodes.getLength(); j++) {
Node childNode = childNodes.item(j);
String name=childNode.getLocalName();
if ("simpleResponse".equals(name) || "response".equals(name)) {
responseCount++;
if("response".equals(name)){
//This is the node that will need to be replaced with a copy with a localName of "simpleResponse"
toBeReplaced=childNode;
}
}
//If responseCount>1 This implies that both a simpleResponse and response have
//been defined - this is allowed by the xsd, but makes no sense
if (responseCount > 1) {
throw new IllegalArgumentException("Only one of either simpleResponse or response should define the response type");
}
}
}
return toBeReplaced;
}
}