package com.temenos.useragent.generic.mediatype; /* * #%L * useragent-generic-java * %% * Copyright (C) 2012 - 2016 Temenos Holdings N.V. * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ import static com.temenos.useragent.generic.mediatype.AtomUtil.*; import static com.temenos.useragent.generic.mediatype.PropertyNameUtil.*; import java.util.ArrayList; import java.util.List; import org.apache.abdera.model.Entry; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * Handles the Atom XML content part of the {@link Entry entry} for read and * update. * * @author ssethupathi * */ public class AtomXmlContentHandler { private Document document; public AtomXmlContentHandler(Document document) { if (document == null) { throw new IllegalArgumentException( "Invalid content document 'null'"); } this.document = document; } public Document getDocument() { return document; } /** * Returns the count of the given element's existence in the document. * * @param fqPropertyName * @return count * @throws IllegalArgumentException * if the fully qualified property is invalid */ public int getCount(String fqPropertyName) { String[] pathParts = flattenPropertyName(fqPropertyName); Node parent = identifyParentElement(fqPropertyName, document, pathParts); if (parent == null) { return 0; } validateElementName(pathParts[pathParts.length - 1], fqPropertyName); String elementName = buildElementName(pathParts[pathParts.length - 1]); Element element = identifyChildElement(parent, elementName, 0); return countElementSiblings(element); } /** * Retruns the value for the given element's text node in the document. * * @param fqPropertyName * @return value * @throws IllegalArgumentException * if the fully qualified property is invalid */ public String getValue(String fqPropertyName) { String[] pathParts = flattenPropertyName(fqPropertyName); Node parent = identifyParentElement(fqPropertyName, document, pathParts); if (parent == null) { return null; } validateElementName(pathParts[pathParts.length - 1], fqPropertyName); String elementName = buildElementName(pathParts[pathParts.length - 1]); Element element = getFirstChildElement(parent, elementName); if (element != null) { return element.getTextContent(); } else { return null; } } /** * Sets the given value to the element of the fully qualified property name. * * @param fqPropertyName * @param value * @throws IllegalArgumentException * if the fully qualified property is invalid */ public void setValue(String fqPropertyName, String value) { String[] pathParts = flattenPropertyName(fqPropertyName); Node parent = checkAndCreateParent(fqPropertyName, document, pathParts); validateElementName(pathParts[pathParts.length - 1], fqPropertyName); String elementName = buildElementName(pathParts[pathParts.length - 1]); Element element = getFirstChildElement(parent, elementName); if (element != null) { element.setTextContent(value); } else { appendNewChildElement(parent, elementName, value); } } public void remove(String fqPropertyName) { String[] pathParts = flattenPropertyName(fqPropertyName); Node parent = identifyParentElement(fqPropertyName, document, pathParts); if (parent == null) { return; } int expectedIndex = extractIndex(pathParts[pathParts.length - 1]); String elementName = buildElementName(pathParts[pathParts.length - 1]); if (isPropertyNameWithIndex(pathParts[pathParts.length - 1])) { parent = identifyChildElement(parent, elementName, 0); elementName = "d:element"; } Element element = identifyChildElement(parent, elementName, expectedIndex); parent.removeChild(element); } private int countElementSiblings(Element parent) { if (parent == null) { return 0; } List<Element> elementChildren = getChildElements(parent, "d:element"); if (elementChildren.isEmpty()) { return 1; } return elementChildren.size(); } private Element cloneExistingElement(Node parent, String fqElementName) { Element cloneableElement = getFirstChildElement(parent, fqElementName); if (cloneableElement == null) { throw new IllegalStateException("Invalid element name '" + fqElementName + "' to set value to"); } Node clonedNode = null; List<Element> childElements = getChildElements(cloneableElement); if (childElements.isEmpty()) { clonedNode = cloneableElement.cloneNode(false); } else { clonedNode = cloneableElement.cloneNode(true); removeNonContainerChildElements(clonedNode); } parent.appendChild(clonedNode); return (Element) clonedNode; } private void removeNonContainerChildElements(Node parent) { NodeList children = parent.getChildNodes(); for (int idx = 0; idx < children.getLength(); idx++) { Node currentNode = children.item(idx); if (!getChildElements(currentNode).isEmpty()) { removeNonContainerChildElements(currentNode); } else if (Node.ELEMENT_NODE == currentNode.getNodeType()) { parent.removeChild(currentNode); } } } private void appendNewChildElement(Node parent, String fqPropertyName, String value) { Element newElement = parent.getOwnerDocument().createElementNS( NS_ODATA, fqPropertyName); newElement.setTextContent(value); parent.appendChild(newElement); } private Node identifyParentElement(String fqPropertyName, Document document, String... pathParts) { Node parent = document.getFirstChild(); int pathIndex = 0; while (pathIndex < (pathParts.length - 1)) { String pathPart = pathParts[pathIndex]; validateElementNameWithIndex(pathPart, fqPropertyName); parent = identifyChildElement(parent, buildElementName(pathPart), 0); parent = identifyChildElement(parent, buildElementName("element"), extractIndex(pathPart)); pathIndex++; } return parent; } private Node checkAndCreateParent(String fqPropertyName, Document document, String... pathParts) { Node parent = document.getFirstChild(); int pathIndex = 0; while (pathIndex < (pathParts.length - 1)) { String pathPart = pathParts[pathIndex]; validateElementNameWithIndex(pathPart, fqPropertyName); parent = checkAndCreateChild(parent, buildElementName(pathPart), 0); parent = checkAndCreateChild(parent, buildElementName("element"), extractIndex(pathPart)); pathIndex++; } return parent; } private String buildElementName(String path) { String elementName = path; if (isPropertyNameWithIndex(path)) { elementName = path.substring(0, path.indexOf("(")); } return "d:" + elementName; } private Element checkAndCreateChild(Node parent, String childName, int expectedIndex) { if (parent == null) { return null; } List<Element> childElements = getChildElements(parent, childName); if (expectedIndex < childElements.size()) { return identifyChildElement(parent, childName, expectedIndex); } else if (expectedIndex == childElements.size()) { return (Element) cloneExistingElement(parent, childName); } else { throw new IllegalStateException( "Invalid index '" + expectedIndex + "' to set value to. Only supported indexs are between '0' and '" + childElements.size() + "'"); } } private void validateElementNameWithIndex(String elementName, String fqPropertyName) { if (!isPropertyNameWithIndex(elementName)) { throw new IllegalArgumentException("Invalid part '" + elementName + "' in fully qualified property name '" + fqPropertyName + "'"); } } private void validateElementName(String elementName, String fqPropertyName) { if (elementName.contains("(")) { throw new IllegalArgumentException("Invalid part '" + elementName + "' in fully qualified property name '" + fqPropertyName + "'"); } } private Element identifyChildElement(Node parent, String childName, int index) { if (parent == null) { return null; } return getSpecificElement(parent.getChildNodes(), childName, index); } private Element getFirstChildElement(Node node, String childName) { return getSpecificElement(node.getChildNodes(), childName, 0); } private Element getSpecificElement(NodeList nodes, String childName, int expectedIndex) { int matchingElementIdx = 0; for (int idx = 0; idx < nodes.getLength(); idx++) { Node node = nodes.item(idx); if (Node.ELEMENT_NODE == node.getNodeType() && node.getNodeName().equals(childName)) { if (matchingElementIdx == expectedIndex) { return (Element) node; } matchingElementIdx++; } } return null; } private List<Element> getChildElements(Node parent) { List<Element> elements = new ArrayList<Element>(); NodeList children = parent.getChildNodes(); for (int idx = 0; idx < children.getLength(); idx++) { Node child = children.item(idx); if (Node.ELEMENT_NODE == child.getNodeType()) { elements.add((Element) child); } } return elements; } private List<Element> getChildElements(Node parent, String childName) { List<Element> elements = new ArrayList<Element>(); NodeList children = parent.getChildNodes(); for (int idx = 0; idx < children.getLength(); idx++) { Node child = children.item(idx); if (Node.ELEMENT_NODE == child.getNodeType() && child.getNodeName().equals(childName)) { elements.add((Element) child); } } return elements; } }