[java-metadata-aggregator] 01/02: MDA-56 - part 1: add Container framework
Ian Young
ian at iay.org.uk
Wed Jun 28 11:55:03 EDT 2017
This is an automated email from the git hooks/post-receive script.
iay pushed a commit to branch master
in repository java-metadata-aggregator.
View the commit online:
http://git.shibboleth.net/view/?p=java-metadata-aggregator.git;a=commit;h=9e9f56838470c62cbf16159d9e93ad68eea5657d
commit 9e9f56838470c62cbf16159d9e93ad68eea5657d
Author: Ian Young <ian at iay.org.uk>
AuthorDate: Wed Jun 28 14:36:58 2017 +0100
MDA-56 - part 1: add Container framework
---
.../net/shibboleth/metadata/dom/Container.java | 293 +++++++++++++++++++++
.../net/shibboleth/metadata/dom/ElementMaker.java | 53 ++++
.../shibboleth/metadata/dom/ElementMatcher.java | 55 ++++
.../net/shibboleth/metadata/dom/BaseDOMTest.java | 41 ++-
.../net/shibboleth/metadata/dom/ContainerTest.java | 272 +++++++++++++++++++
.../shibboleth/metadata/dom/ElementMakerTest.java | 33 +++
.../metadata/dom/ElementMatcherTest.java | 32 +++
.../net/shibboleth/metadata/dom/Container-add1.xml | 4 +
.../shibboleth/metadata/dom/Container-addFirst.xml | 5 +
.../shibboleth/metadata/dom/Container-addLast.xml | 5 +
.../net/shibboleth/metadata/dom/Container-find.xml | 9 +
.../shibboleth/metadata/dom/Container-nested.xml | 7 +
12 files changed, 805 insertions(+), 4 deletions(-)
diff --git a/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/Container.java b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/Container.java
new file mode 100644
index 0000000..25f9905
--- /dev/null
+++ b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/Container.java
@@ -0,0 +1,293 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You 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 net.shibboleth.metadata.dom;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.xml.ElementSupport;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+
+/**
+ * A wrapper for a DOM {@link Element} allowing its use as a container for either
+ * a simple text value or for other {@link Element}s. In the latter case, white-space
+ * formatting is handled automatically for nested containers.
+ */
+public class Container {
+
+ /**
+ * When a container needs to add a new child {@link Element}, use a functor
+ * with this interface to create the appropriate context for the
+ * new child and to add it in place.
+ */
+ public interface ChildAddingStrategy {
+
+ /**
+ * Add the element in the appropriate position within the provided container.
+ *
+ * @param parent container into which to add the new child
+ * @param child child {@link Element} to add
+ * @return a new child {@link Container} representing the child {@link Element}
+ */
+ @Nonnull Container addChild(@Nonnull final Container parent, @Nonnull final Element child);
+ }
+
+ /**
+ * Child adding strategy which adds the new child as the first child of the
+ * container.
+ */
+ @Nonnull public static final ChildAddingStrategy FIRST_CHILD = new ChildAddingStrategy() {
+
+ @Override
+ public @Nonnull Container addChild(@Nonnull final Container parent, @Nonnull final Element child) {
+ final Element parentElement = parent.unwrap();
+ final String indent = "\n" + parent.indentInner;
+ final Node indentNode = parentElement.getOwnerDocument().createTextNode(indent);
+ parent.prime();
+ parentElement.insertBefore(child, parentElement.getFirstChild());
+ parentElement.insertBefore(indentNode, parentElement.getFirstChild());
+ return new Container(child, parent);
+ }
+
+ };
+
+ /**
+ * Child adding strategy which adds the new child as the last child of the
+ * container.
+ */
+ @Nonnull public static final ChildAddingStrategy LAST_CHILD = new ChildAddingStrategy() {
+
+ @Override
+ public @Nonnull Container addChild(@Nonnull final Container parent, @Nonnull final Element child) {
+ final Element parentElement = parent.unwrap();
+ final Document document = parentElement.getOwnerDocument();
+ parent.prime();
+
+ // The end of the parent's node list will be the indentation for the closing tag,
+ // so push in some additional indentation for the child.
+ parentElement.appendChild(document.createTextNode(parent.indentStep));
+ parentElement.appendChild(child);
+
+ // Add a newline and indentation for the closing tag again
+ parentElement.appendChild(document.createTextNode("\n" + parent.indentOuter));
+
+ return new Container(child, parent);
+ }
+
+ };
+
+ /** Default indentation of four spaces. */
+ @Nonnull private static final String DEFAULT_INDENT = " ";
+
+ /** The wrapped {@link Element}. */
+ @Nonnull private final Element element;
+
+ /** The parent {@link Container}, or <code>null</code>. */
+ @Nullable private final Container parentContainer;
+
+ /** The indentation applied to the opening and closing tags. */
+ @Nonnull private final String indentOuter;
+
+ /** The indentation applied to child containers. */
+ @Nonnull private final String indentInner;
+
+ /** The difference between outer and inner indentation. */
+ @Nonnull private final String indentStep;
+
+ /**
+ * Constructor.
+ *
+ * @param elem {@link Element} on which to base the {@link Container}
+ * @param parent {@link Container} to link as the parent, or <code>null</code>
+ * @param outer indentation to be used for this container's start and end tags
+ * @param inner indentation to be used for child containers
+ * @param step difference between inner and outer indentation
+ */
+ private Container(@Nonnull final Element elem,
+ @Nullable final Container parent,
+ @Nonnull final String outer,
+ @Nonnull final String inner,
+ @Nonnull final String step) {
+ element = Constraint.isNotNull(elem, "element must not be null");
+ parentContainer = parent;
+ indentOuter = Constraint.isNotNull(outer, "outer indent must not be null");
+ indentInner = Constraint.isNotNull(inner, "inner indent must not be null");
+ indentStep = Constraint.isNotNull(step, "indent step must not be null");
+ }
+
+ /**
+ * Constructor.
+ *
+ * This variant is used internally to construct {@link Container}s
+ * corresponding to child elements.
+ *
+ * @param child child {@link Element} for which to construct a {@link Container}
+ * @param parent parentContainer {@link Container} to link this child to
+ */
+ private Container(@Nonnull final Element child, @Nonnull final Container parent) {
+ this(child, parent, parent.indentInner, parent.indentInner + parent.indentStep, parent.indentStep);
+ }
+
+ /**
+ * Constructor.
+ *
+ * This variant is used to start a {@link Container} tree
+ * from an existing root {@link Element}. It is the only
+ * public constructor.
+ *
+ * @param root the existing {@link Element} to wrap.
+ */
+ public Container(@Nonnull final Element root) {
+ this(root, null, "", DEFAULT_INDENT, DEFAULT_INDENT);
+ }
+
+ /**
+ * Return the wrapped {@link Element}.
+ *
+ * @return the wrapped {@link Element}
+ */
+ @Nonnull
+ public Element unwrap() {
+ return element;
+ }
+
+ /**
+ * Set the text content of the wrapped {@link Element}.
+ *
+ * @param text the text content to set within the element
+ */
+ public void setText(@Nonnull final String text) {
+ Constraint.isNotNull(text, "text content must not be null");
+ element.setTextContent(text);
+ }
+
+ /**
+ * Make sure that the container is able to receive additional child
+ * containers.
+ *
+ * If the container is empty, add in text content so that its opening
+ * and closing tags are on different lines but are indented in the same
+ * way.
+ *
+ * The resulting container will have at least one child node, almost
+ * always a text node starting with "\n".
+ */
+ public void prime() {
+ if (!element.hasChildNodes()) {
+ setText("\n" + indentOuter);
+ }
+ }
+
+ /**
+ * Find an existing child matching the {@link Predicate}, if there is one.
+ *
+ * @param matcher {@link Predicate} to match against existing children
+ * @return a child {@link Container} whose {@link Element} matches
+ * the supplied {@link Predicate}.
+ */
+ @Nullable
+ public Container findChild(@Nonnull final Predicate<Element> matcher) {
+ for (final Element e : ElementSupport.getChildElements(element)) {
+ if (matcher.apply(e)) {
+ return new Container(e, this);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Find all existing children matching the {@link Predicate}.
+ *
+ * @param matcher {@link Predicate} to match against existing children
+ * @return a {@link List} of all matching children
+ */
+ @Nonnull
+ public List<Container> findChildren(@Nonnull final Predicate<Element> matcher) {
+ final List<Container> list = new ArrayList<>();
+ for (final Element e : ElementSupport.getChildElements(element)) {
+ if (matcher.apply(e)) {
+ list.add(new Container(e, this));
+ }
+ }
+ return list;
+ }
+
+ /**
+ * Add a child to the container.
+ *
+ * @param child the new child element to add
+ * @param adder strategy class to place the new child inside the container
+ * @return a container wrapping the new child element
+ */
+ @Nonnull
+ public Container addChild(@Nonnull final Element child,
+ @Nonnull final ChildAddingStrategy adder) {
+ return adder.addChild(this, child);
+ }
+
+ /**
+ * Add a child to the container.
+ *
+ * @param maker {@link Function} to create the new child element
+ * @param adder strategy class to place the new child inside the container
+ * @return a container wrapping the new child element
+ */
+ @Nonnull
+ public Container addChild(@Nonnull final Function<Container, Element> maker,
+ @Nonnull final ChildAddingStrategy adder) {
+ final Element child = maker.apply(this);
+ return addChild(child, adder);
+ }
+
+ /**
+ * Locate a child container matching the {@link Predicate}, creating one
+ * if necessary.
+ *
+ * @param matcher {@link Predicate} to match against existing children
+ * @param maker a {@link Function} to create a new child {@link Element}
+ * @param adder a {@link ChildAddingStrategy} determining where to place the new child
+ * @return a child {@link Container}, possibly just created
+ */
+ @Nonnull
+ public Container locateChild(@Nonnull final Predicate<Element> matcher,
+ @Nonnull final Function<Container, Element> maker,
+ @Nonnull final ChildAddingStrategy adder) {
+ assert matcher != null;
+ assert maker != null;
+ assert adder != null;
+
+ // Return an existing child if one exists
+ final Container existing = findChild(matcher);
+ if (existing != null) {
+ return existing;
+ }
+
+ // Construct a new child and add it in the right place
+ return addChild(maker, adder);
+ }
+}
diff --git a/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/ElementMaker.java b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/ElementMaker.java
new file mode 100644
index 0000000..233e9de
--- /dev/null
+++ b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/ElementMaker.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You 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 net.shibboleth.metadata.dom;
+
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.ThreadSafe;
+import javax.xml.namespace.QName;
+
+import net.shibboleth.utilities.java.support.xml.ElementSupport;
+
+import org.w3c.dom.Element;
+
+import com.google.common.base.Function;
+
+/**
+ * Basic maker class for {@link Element}s for use with the {@link Container} system.
+ */
+ at ThreadSafe
+public class ElementMaker implements Function<Container, Element> {
+
+ /** Qualified name for the {@link Element} to be created. */
+ @Nonnull private final QName name;
+
+ /**
+ * Constructor.
+ *
+ * @param qname qualified name for the {@link Element} to be created
+ */
+ public ElementMaker(@Nonnull final QName qname) {
+ name = qname;
+ }
+
+ @Override
+ public Element apply(@Nonnull final Container input) {
+ return ElementSupport.constructElement(input.unwrap().getOwnerDocument(), name);
+ }
+
+}
diff --git a/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/ElementMatcher.java b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/ElementMatcher.java
new file mode 100644
index 0000000..aeab7cc
--- /dev/null
+++ b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/ElementMatcher.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You 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 net.shibboleth.metadata.dom;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.ThreadSafe;
+import javax.xml.namespace.QName;
+
+import net.shibboleth.utilities.java.support.xml.ElementSupport;
+
+import org.w3c.dom.Element;
+
+import com.google.common.base.Predicate;
+
+/**
+ * Basic matcher class for {@link Element}s for use with the {@link Container} system.
+ */
+ at ThreadSafe
+public class ElementMatcher implements Predicate<Element> {
+
+ /** Element {@link QName} to match. */
+ @Nonnull private final QName qname;
+
+ /**
+ * Constructor.
+ *
+ * @param qnameToMatch qualified name ({@link QName}) to match
+ */
+ public ElementMatcher(@Nullable final QName qnameToMatch) {
+ assert qnameToMatch != null;
+ qname = qnameToMatch;
+ }
+
+ @Override
+ public boolean apply(@Nonnull final Element input) {
+ return ElementSupport.isElementNamed(input, qname);
+ }
+
+}
diff --git a/aggregator-pipeline/src/test/java/net/shibboleth/metadata/dom/BaseDOMTest.java b/aggregator-pipeline/src/test/java/net/shibboleth/metadata/dom/BaseDOMTest.java
index f37bfeb..3904a1e 100644
--- a/aggregator-pipeline/src/test/java/net/shibboleth/metadata/dom/BaseDOMTest.java
+++ b/aggregator-pipeline/src/test/java/net/shibboleth/metadata/dom/BaseDOMTest.java
@@ -18,8 +18,11 @@
package net.shibboleth.metadata.dom;
import java.io.InputStream;
+import java.io.StringReader;
import java.util.List;
+import javax.annotation.Nonnull;
+
import net.shibboleth.metadata.BaseTest;
import net.shibboleth.metadata.ErrorStatus;
import net.shibboleth.metadata.Item;
@@ -30,10 +33,12 @@ import net.shibboleth.utilities.java.support.logic.Constraint;
import net.shibboleth.utilities.java.support.primitive.StringSupport;
import net.shibboleth.utilities.java.support.xml.BasicParserPool;
import net.shibboleth.utilities.java.support.xml.ParserPool;
+import net.shibboleth.utilities.java.support.xml.SerializeSupport;
import net.shibboleth.utilities.java.support.xml.XMLParserException;
import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.XMLUnit;
+import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
@@ -48,7 +53,7 @@ public abstract class BaseDOMTest extends BaseTest {
protected BaseDOMTest(final Class<?> clazz) {
super(clazz);
}
-
+
/**
* Setup test class. Creates and initializes the parser pool.
*
@@ -70,7 +75,7 @@ public abstract class BaseDOMTest extends BaseTest {
public ParserPool getParserPool() {
return parserPool;
}
-
+
/**
* Reads in an XML file, parses it, and returns the document element. If the given path is relative (i.e., does not
* start with a '/') it is assumed to be relative to the class, or to /data if the class has not been set.
@@ -126,11 +131,39 @@ public abstract class BaseDOMTest extends BaseTest {
org.testng.Assert.fail(diff.toString());
}
}
-
+
+ /**
+ * Checks whether two nodes are equal based on {@link Node#isEqualNode(Node)}. Both nodes are serialized, re-parsed,
+ * and then compared for equality. This forces any changes made to the document that haven't yet been represented in
+ * the DOM (e.g., declaration of used namespaces) to be flushed to the DOM.
+ *
+ * @param expected the expected node against which the actual node will be tested, never null
+ * @param actual the actual node tested against the expected node, never null
+ *
+ * @throws XMLParserException thrown if there is a problem serializing and re-parsing the nodes
+ */
+ public void assertXMLEqual(@Nonnull final Node expected, @Nonnull final Node actual) throws XMLParserException {
+ Constraint.isNotNull(actual, "Actual Node may not be null");
+ final String serializedActual = SerializeSupport.nodeToString(actual);
+ Element deserializedActual = parserPool.parse(new StringReader(serializedActual)).getDocumentElement();
+
+ Constraint.isNotNull(expected, "Expected Node may not be null");
+ final String serializedExpected = SerializeSupport.nodeToString(expected);
+ Element deserializedExpected = parserPool.parse(new StringReader(serializedExpected)).getDocumentElement();
+
+ final boolean ok = deserializedExpected.isEqualNode(deserializedActual);
+ if (!ok) {
+ System.out.println("Expected:\n" + serializedExpected);
+ System.out.println("Actual:\n" + serializedActual);
+ }
+
+ Assert.assertTrue(ok, "Actual Node does not equal expected Node");
+ }
+
protected int countErrors(final Item<Element> item) {
final ClassToInstanceMultiMap<ItemMetadata> metadata = item.getItemMetadata();
final List<ErrorStatus> errors = metadata.get(ErrorStatus.class);
return errors.size();
}
-
+
}
diff --git a/aggregator-pipeline/src/test/java/net/shibboleth/metadata/dom/ContainerTest.java b/aggregator-pipeline/src/test/java/net/shibboleth/metadata/dom/ContainerTest.java
new file mode 100644
index 0000000..5fbb0b4
--- /dev/null
+++ b/aggregator-pipeline/src/test/java/net/shibboleth/metadata/dom/ContainerTest.java
@@ -0,0 +1,272 @@
+
+package net.shibboleth.metadata.dom;
+
+import java.util.List;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+
+public class ContainerTest extends BaseDOMTest {
+
+ private final Document doc;
+
+ protected ContainerTest() throws Exception {
+ super(Container.class);
+ setUp();
+ doc = getParserPool().newDocument();
+ }
+
+ @Test
+ public void prime() {
+ // simple top-level element with no indentation
+ final Element e1 = doc.createElementNS("ns", "el");
+ final Container c1 = new Container(e1);
+ c1.prime();
+ Assert.assertEquals(e1.getTextContent(), "\n");
+
+ // prime it again
+ c1.prime();
+ Assert.assertEquals(e1.getTextContent(), "\n");
+
+ // an indented container
+ final Element e2 = doc.createElementNS("ns", "el2");
+ e1.insertBefore(e2, e1.getFirstChild());
+ final Container c2 = c1.findChild(Predicates.<Element>alwaysTrue());
+ Assert.assertNotNull(c2);
+ c2.prime();
+ Assert.assertEquals(e2.getTextContent(), "\n ");
+
+ // prime it again
+ c2.prime();
+ Assert.assertEquals(e2.getTextContent(), "\n ");
+ }
+
+ @Test
+ public void addChildElementFirst() throws Exception {
+ final Element e1 = doc.createElementNS("ns", "root");
+ final Element e2 = doc.createElementNS("ns", "child");
+ e2.setTextContent("child value");
+ final Container c1 = new Container(e1);
+ c1.addChild(e2, Container.FIRST_CHILD);
+
+ final Element ok = readXMLData("add1.xml");
+ assertXMLEqual(ok, e1);
+
+ final Element e3 = doc.createElementNS("ns", "child2");
+ e3.setTextContent("child 2 value");
+ c1.addChild(e3, Container.FIRST_CHILD);
+ final Element ok2 = readXMLData("addFirst.xml");
+ assertXMLEqual(ok2, e1);
+ }
+
+ @Test
+ public void addChildElementLast() throws Exception {
+ final Element e1 = doc.createElementNS("ns", "root");
+ final Element e2 = doc.createElementNS("ns", "child");
+ e2.setTextContent("child value");
+ final Container c1 = new Container(e1);
+ c1.addChild(e2, Container.LAST_CHILD);
+
+ final Element ok = readXMLData("add1.xml");
+ assertXMLEqual(ok, e1);
+
+ final Element e3 = doc.createElementNS("ns", "child2");
+ e3.setTextContent("child 2 value");
+ c1.addChild(e3, Container.LAST_CHILD);
+ final Element ok2 = readXMLData("addLast.xml");
+ assertXMLEqual(ok2, e1);
+ }
+
+ @Test
+ public void addChildElementNested() throws Exception {
+ final Element root = doc.createElementNS("ns", "root");
+ final Element mid = doc.createElementNS("ns", "mid");
+ final Element leaf1 = doc.createElementNS("ns", "leaf");
+ leaf1.setTextContent("leaf 1");
+ final Element leaf2 = doc.createElementNS("ns", "leaf");
+ leaf2.setTextContent("leaf 2");
+ final Container rootContainer = new Container(root);
+ final Container midContainer = rootContainer.addChild(mid, Container.FIRST_CHILD);
+ midContainer.addChild(leaf1, Container.LAST_CHILD);
+ midContainer.addChild(leaf2, Container.LAST_CHILD);
+
+ final Element ok = readXMLData("nested.xml");
+ assertXMLEqual(ok, root);
+ }
+
+ @Test
+ public void addChildFunctionFirst() throws Exception {
+ final Element e1 = doc.createElementNS("ns", "root");
+ final Container c1 = new Container(e1);
+
+ c1.addChild(new Function<Container, Element>(){
+
+ @Override
+ public Element apply(Container input) {
+ final Element e2 = doc.createElementNS("ns", "child");
+ e2.setTextContent("child value");
+ return e2;
+ }
+
+ }, Container.FIRST_CHILD);
+
+ final Element ok = readXMLData("add1.xml");
+ assertXMLEqual(ok, e1);
+
+ c1.addChild(new Function<Container, Element>(){
+
+ @Override
+ public Element apply(Container input) {
+ final Element e3 = doc.createElementNS("ns", "child2");
+ e3.setTextContent("child 2 value");
+ return e3;
+ }
+
+ }, Container.FIRST_CHILD);
+
+ final Element ok2 = readXMLData("addFirst.xml");
+ assertXMLEqual(ok2, e1);
+ }
+
+ @Test
+ public void addChildFunctionLast() throws Exception {
+ final Element e1 = doc.createElementNS("ns", "root");
+ final Container c1 = new Container(e1);
+
+ c1.addChild(new Function<Container, Element>(){
+
+ @Override
+ public Element apply(Container input) {
+ final Element e2 = doc.createElementNS("ns", "child");
+ e2.setTextContent("child value");
+ return e2;
+ }
+
+ }, Container.LAST_CHILD);
+
+ final Element ok = readXMLData("add1.xml");
+ assertXMLEqual(ok, e1);
+
+ c1.addChild(new Function<Container, Element>(){
+
+ @Override
+ public Element apply(Container input) {
+ final Element e3 = doc.createElementNS("ns", "child2");
+ e3.setTextContent("child 2 value");
+ return e3;
+ }
+
+ }, Container.LAST_CHILD);
+
+ final Element ok2 = readXMLData("addLast.xml");
+ assertXMLEqual(ok2, e1);
+ }
+
+ @Test
+ public void unwrap() {
+ final Element leaf1 = doc.createElementNS("ns", "leaf");
+ leaf1.setTextContent("leaf 1");
+ final Container c = new Container(leaf1);
+ Assert.assertEquals(c.unwrap().getTextContent(), "leaf 1");
+ }
+
+ @Test
+ public void setText() {
+ final Element e = doc.createElementNS("ns", "root");
+ final Container c = new Container(e);
+ c.setText("some text");
+ Assert.assertEquals(e.getTextContent(), "some text");
+ }
+
+ @Test
+ public void findChild() throws Exception {
+ final Container root = new Container(readXMLData("find.xml"));
+ final Container child = root.findChild(new Predicate<Element>(){
+
+ @Override
+ public boolean apply(Element input) {
+ return "findme".equals(input.getLocalName());
+ }
+
+ });
+ Assert.assertNotNull(child);
+ Assert.assertEquals(child.unwrap().getTextContent(), "find me 1");
+ }
+
+ @Test
+ public void findChildren() throws Exception {
+ final Container root = new Container(readXMLData("find.xml"));
+ final List<Container> children = root.findChildren(new Predicate<Element>(){
+
+ @Override
+ public boolean apply(Element input) {
+ return "findme".equals(input.getLocalName());
+ }
+
+ });
+ Assert.assertNotNull(children);
+ Assert.assertEquals(children.size(), 3);
+ Assert.assertEquals(children.get(0).unwrap().getTextContent(), "find me 1");
+ Assert.assertEquals(children.get(1).unwrap().getTextContent(), "find me 2");
+ Assert.assertEquals(children.get(2).unwrap().getTextContent(), "find me 3");
+ }
+
+ @Test
+ public void locateChild() throws Exception {
+ final Element e1 = doc.createElementNS("ns", "root");
+ final Container c1 = new Container(e1);
+
+ c1.locateChild(new Predicate<Element>(){
+
+ @Override
+ public boolean apply(Element input) {
+ return "child".equals(input.getLocalName());
+ }
+
+
+ }, new Function<Container, Element>(){
+
+ @Override
+ public Element apply(Container input) {
+ final Element e2 = doc.createElementNS("ns", "child");
+ e2.setTextContent("child value");
+ return e2;
+ }
+
+ }, Container.LAST_CHILD);
+
+ final Element ok = readXMLData("add1.xml");
+ assertXMLEqual(ok, e1);
+
+ // same again should NOT change the result for locate
+
+ c1.locateChild(new Predicate<Element>(){
+
+ @Override
+ public boolean apply(Element input) {
+ return "child".equals(input.getLocalName());
+ }
+
+
+ }, new Function<Container, Element>(){
+
+ @Override
+ public Element apply(Container input) {
+ final Element e2 = doc.createElementNS("ns", "child");
+ e2.setTextContent("child value");
+ return e2;
+ }
+
+ }, Container.LAST_CHILD);
+
+ final Element ok2 = readXMLData("add1.xml");
+ assertXMLEqual(ok2, e1);
+
+ }
+}
diff --git a/aggregator-pipeline/src/test/java/net/shibboleth/metadata/dom/ElementMakerTest.java b/aggregator-pipeline/src/test/java/net/shibboleth/metadata/dom/ElementMakerTest.java
new file mode 100644
index 0000000..8b0f210
--- /dev/null
+++ b/aggregator-pipeline/src/test/java/net/shibboleth/metadata/dom/ElementMakerTest.java
@@ -0,0 +1,33 @@
+
+package net.shibboleth.metadata.dom;
+
+import javax.xml.namespace.QName;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import com.google.common.base.Function;
+
+public class ElementMakerTest extends BaseDOMTest {
+
+ private final Document doc;
+
+ protected ElementMakerTest() throws Exception {
+ super(ElementMaker.class);
+ setUp();
+ doc = getParserPool().newDocument();
+ }
+
+ @Test
+ public void apply() {
+ final Function<Container, Element> maker = new ElementMaker(new QName("ns", "local"));
+ final Element root = doc.createElementNS("ns", "root");
+ final Container rootContainer = new Container(root);
+ final Element newElement = maker.apply(rootContainer);
+ Assert.assertNotNull(newElement);
+ Assert.assertEquals(newElement.getLocalName(), "local");
+ Assert.assertEquals(newElement.getNamespaceURI(), "ns");
+ }
+}
diff --git a/aggregator-pipeline/src/test/java/net/shibboleth/metadata/dom/ElementMatcherTest.java b/aggregator-pipeline/src/test/java/net/shibboleth/metadata/dom/ElementMatcherTest.java
new file mode 100644
index 0000000..371c789
--- /dev/null
+++ b/aggregator-pipeline/src/test/java/net/shibboleth/metadata/dom/ElementMatcherTest.java
@@ -0,0 +1,32 @@
+
+package net.shibboleth.metadata.dom;
+
+import javax.xml.namespace.QName;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import com.google.common.base.Predicate;
+
+public class ElementMatcherTest extends BaseDOMTest {
+
+ private final Document doc;
+
+ protected ElementMatcherTest() throws Exception {
+ super(ElementMatcher.class);
+ setUp();
+ doc = getParserPool().newDocument();
+ }
+
+ @Test
+ public void matcher() throws Exception {
+ final Predicate<Element> matcher = new ElementMatcher(new QName("ns", "xxx"));
+ Assert.assertTrue(matcher.apply(doc.createElementNS("ns", "xxx")));
+ Assert.assertFalse(matcher.apply(doc.createElementNS("ns", "yyy")));
+ Assert.assertFalse(matcher.apply(doc.createElementNS("ns2", "xxx")));
+ Assert.assertFalse(matcher.apply(doc.createElementNS("ns2", "yyy")));
+ }
+
+}
diff --git a/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/dom/Container-add1.xml b/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/dom/Container-add1.xml
new file mode 100644
index 0000000..317ded9
--- /dev/null
+++ b/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/dom/Container-add1.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<root xmlns="ns">
+ <child>child value</child>
+</root>
diff --git a/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/dom/Container-addFirst.xml b/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/dom/Container-addFirst.xml
new file mode 100644
index 0000000..301b160
--- /dev/null
+++ b/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/dom/Container-addFirst.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<root xmlns="ns">
+ <child2>child 2 value</child2>
+ <child>child value</child>
+</root>
diff --git a/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/dom/Container-addLast.xml b/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/dom/Container-addLast.xml
new file mode 100644
index 0000000..0f552d6
--- /dev/null
+++ b/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/dom/Container-addLast.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<root xmlns="ns">
+ <child>child value</child>
+ <child2>child 2 value</child2>
+</root>
diff --git a/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/dom/Container-find.xml b/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/dom/Container-find.xml
new file mode 100644
index 0000000..859f20c
--- /dev/null
+++ b/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/dom/Container-find.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<root xmlns="ns">
+ <child>child value</child>
+ <findme>find me 1</findme>
+ <another>ignorable child</another>
+ <findme>find me 2</findme>
+ <another>ignore me as well</another>
+ <findme>find me 3</findme>
+</root>
diff --git a/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/dom/Container-nested.xml b/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/dom/Container-nested.xml
new file mode 100644
index 0000000..2d9c17c
--- /dev/null
+++ b/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/dom/Container-nested.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<root xmlns="ns">
+ <mid>
+ <leaf>leaf 1</leaf>
+ <leaf>leaf 2</leaf>
+ </mid>
+</root>
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list