diff --git a/java/google/registry/rde/RdeParser.java b/java/google/registry/rde/RdeParser.java new file mode 100644 index 000000000..faf993c27 --- /dev/null +++ b/java/google/registry/rde/RdeParser.java @@ -0,0 +1,568 @@ +// Copyright 2016 The Domain Registry Authors. All Rights Reserved. +// +// 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 google.registry.rde; + +import static com.google.common.base.Preconditions.checkArgument; +import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; + +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableMap; + +import google.registry.xjc.XjcXmlTransformer; +import google.registry.xjc.rdecontact.XjcRdeContact; +import google.registry.xjc.rdecontact.XjcRdeContactElement; +import google.registry.xjc.rdedomain.XjcRdeDomain; +import google.registry.xjc.rdedomain.XjcRdeDomainElement; +import google.registry.xjc.rdeeppparams.XjcRdeEppParams; +import google.registry.xjc.rdeeppparams.XjcRdeEppParamsElement; +import google.registry.xjc.rdeheader.XjcRdeHeader; +import google.registry.xjc.rdeheader.XjcRdeHeaderCount; +import google.registry.xjc.rdeheader.XjcRdeHeaderElement; +import google.registry.xjc.rdehost.XjcRdeHost; +import google.registry.xjc.rdehost.XjcRdeHostElement; +import google.registry.xjc.rdeidn.XjcRdeIdn; +import google.registry.xjc.rdeidn.XjcRdeIdnElement; +import google.registry.xjc.rdenndn.XjcRdeNndn; +import google.registry.xjc.rdenndn.XjcRdeNndnElement; +import google.registry.xjc.rderegistrar.XjcRdeRegistrar; +import google.registry.xjc.rderegistrar.XjcRdeRegistrarElement; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; + +import javax.annotation.concurrent.NotThreadSafe; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.stax.StAXSource; +import javax.xml.transform.stream.StreamResult; + +/** + * RDE escrow deposit file parser + * + *
{@link RdeParser} parses escrow deposit files as a sequence of elements. The parser will first + * parse and cache the RDE header before any other elements, so all calls to {@link #getHeader} will + * return the header even if the parser has advanced beyond it. + * + *
{@link RdeParser} currently supports parsing the following rde elements as jaxb objects: + *
Any calls to {@link #nextDomain}, {@link #nextHost}, etc. will advance the parser to the next
+ * element in the file, if any additional elements of that type exist. Since the order of these
+ * elements is not known at the time the file is read, client code should only try to parse one type
+ * of element at a time. Parsing of additional element types should be performed by creating a new
+ * parser.
+ */
+@NotThreadSafe
+public class RdeParser {
+
+ private static final String RDE_DOMAIN_URI = "urn:ietf:params:xml:ns:rdeDomain-1.0";
+ private static final String RDE_HOST_URI = "urn:ietf:params:xml:ns:rdeHost-1.0";
+ private static final String RDE_CONTACT_URI = "urn:ietf:params:xml:ns:rdeContact-1.0";
+ private static final String RDE_REGISTRAR_URI = "urn:ietf:params:xml:ns:rdeRegistrar-1.0";
+ private static final String RDE_IDN_URI = "urn:ietf:params:xml:ns:rdeIDN-1.0";
+ private static final String RDE_NNDN_URI = "urn:ietf:params:xml:ns:rdeNNDN-1.0";
+ private static final String RDE_EPP_PARAMS_URI = "urn:ietf:params:xml:ns:rdeEppParams-1.0";
+ private static final String RDE_HEADER_URI = "urn:ietf:params:xml:ns:rdeHeader-1.0";
+
+ /**
+ * Convenient immutable java representation of an RDE header
+ */
+ public static class RdeHeader {
+
+ private final ImmutableMap The parser may skip over other types of elements while advancing to the next instance of the
+ * specified element.
+ *
+ * @param uri Element URI
+ * @param name Element Name
+ * @return true if the parser advanced to the element, false otherwise.
+ */
+ private boolean nextElement(String uri, String name) {
+ try {
+ while (reader.hasNext()) {
+ reader.next();
+ if (isAtElement(uri, name)) {
+ return true;
+ }
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return false;
+ }
+
+ public RdeHeader getHeader() {
+ return header;
+ }
+
+ /**
+ * Attempts to skip over a number of specified elements.
+ *
+ * If the parser is not currently at one of the specified elements, it will advance to the next
+ * instance before skipping any.
+ *
+ * In the process of skipping over a specific type of element, other elements may be skipped as
+ * well. Elements of types other than that specified by the uri and name will not be counted.
+ *
+ * @param numberOfElements Number of elements to skip
+ * @param uri Element URI
+ * @param name Element Name
+ * @return Number of elements that were skipped
+ */
+ private int skipElements(int numberOfElements, String uri, String name) {
+ checkArgument(
+ numberOfElements >= 0, "number of elements must be greater than or equal to zero");
+ // don't do any skipping if numberOfElements is 0
+ if (numberOfElements == 0) {
+ return 0;
+ }
+ // unless the parser is at one of the specified elements,
+ // the first call to nextElement() will advance to the first
+ // element to be skipped
+ int skipped = 0;
+ if (isAtElement(uri, name)) {
+ skipped = 1;
+ }
+ while (nextElement(uri, name) && skipped < numberOfElements) {
+ skipped++;
+ }
+ return skipped;
+ }
+
+ /**
+ * Advances parser to the next contact element.
+ *
+ * The parser may skip over other types of elements while advancing to the next contact
+ * element.
+ *
+ * @return true if the parser advanced to a contact element, false otherwise
+ */
+ public boolean nextContact() {
+ return nextElement(RDE_CONTACT_URI, "contact");
+ }
+
+ /**
+ * Checks if the parser is at a contact element.
+ *
+ * @return true if the parser is at a contact element, false otherwise
+ */
+ public boolean isAtContact() {
+ return isAtElement(RDE_CONTACT_URI, "contact");
+ }
+
+ /**
+ * Attempts to skip over a number of contacts.
+ *
+ * If the parser is not currently at a contact element, it will advance to the next instance
+ * before skipping any.
+ *
+ * In the process of skipping over a contact element, other elements may be skipped as well.
+ * Elements of types other than contact elements will not be counted.
+ *
+ * @return Number of contact elements that were skipped
+ */
+ public int skipContacts(int numberOfContacts) {
+ return skipElements(numberOfContacts, RDE_CONTACT_URI, "contact");
+ }
+
+ /**
+ * Gets the current contact.
+ *
+ * The parser must be at a contact element before this method is called, or an
+ * {@link IllegalStateException} will be thrown. Check the return value of {@link #isAtContact} or
+ * {@link #nextContact} to determine if the parser is at a contact element.
+ *
+ * @return Jaxb Contact
+ * @throws IllegalStateException if the parser is not at a contact element
+ */
+ public XjcRdeContact getContact() {
+ XjcRdeContactElement element =
+ (XjcRdeContactElement) unmarshalElement(RDE_CONTACT_URI, "contact");
+ return element.getValue();
+ }
+
+ /**
+ * Advances parser to the next domain element.
+ *
+ * The parser may skip over other types of elements while advancing to the next domain element.
+ *
+ * @return true if the parser advanced to a domain element, false otherwise
+ */
+ public boolean nextDomain() {
+ return nextElement(RDE_DOMAIN_URI, "domain");
+ }
+
+ /**
+ * Checks if the parser is at a domain element.
+ *
+ * @return true if the parser is at a domain element, false otherwise
+ */
+ public boolean isAtDomain() {
+ return isAtElement(RDE_DOMAIN_URI, "domain");
+ }
+
+ /**
+ * Attempts to skip over a number of domains.
+ *
+ * If the parser is not currently at a domain element, it will advance to the next instance
+ * before skipping any.
+ *
+ * In the process of skipping over a domain element, other elements may be skipped as well.
+ * Elements of types other than domain elements will not be counted.
+ *
+ * @return Number of domain elements that were skipped
+ */
+ public int skipDomains(int numberOfDomains) {
+ return skipElements(numberOfDomains, RDE_DOMAIN_URI, "domain");
+ }
+
+ /**
+ * Gets the current domain.
+ *
+ * The parser must be at a domain element before this method is called, or an
+ * {@link IllegalStateException} will be thrown. Check the return value of {@link #isAtDomain} or
+ * {@link #nextDomain} to determine if the parser is at a domain element.
+ *
+ * @return Jaxb Domain
+ * @throws IllegalStateException if the parser is not at a domain element
+ */
+ public XjcRdeDomain getDomain() {
+ XjcRdeDomainElement element = (XjcRdeDomainElement) unmarshalElement(RDE_DOMAIN_URI, "domain");
+ return element.getValue();
+ }
+
+ /**
+ * Advances parser to the next host element.
+ *
+ * The parser may skip over other types of elements while advancing to the next host element.
+ *
+ * @return true if the parser advanced to a host element, false otherwise
+ */
+ public boolean nextHost() {
+ return nextElement(RDE_HOST_URI, "host");
+ }
+
+ /**
+ * Checks if the parser is at a host element.
+ *
+ * @return true if the parser is at a host element, false otherwise
+ */
+ public boolean isAtHost() {
+ return isAtElement(RDE_HOST_URI, "host");
+ }
+
+ /**
+ * Attempts to skip over a number of hosts.
+ *
+ * If the parser is not currently at a host element, it will advance to the next instance
+ * before skipping any.
+ *
+ * In the process of skipping over a host element, other elements may be skipped as well.
+ * Elements of types other than host elements will not be counted.
+ *
+ * @return Number of host elements that were skipped
+ */
+ public int skipHosts(int numberOfHosts) {
+ return skipElements(numberOfHosts, RDE_HOST_URI, "host");
+ }
+
+ /**
+ * Gets the current host.
+ *
+ * The parser must be at a host element before this method is called, or an
+ * {@link IllegalStateException} will be thrown. Check the return value of {@link #isAtHost} or
+ * {@link #nextHost} to determine if the parser is at a host element.
+ *
+ * @return Jaxb Host
+ * @throws IllegalStateException if the parser is not at a host element
+ */
+ public XjcRdeHost getHost() {
+ XjcRdeHostElement element = (XjcRdeHostElement) unmarshalElement(RDE_HOST_URI, "host");
+ return element.getValue();
+ }
+
+ /**
+ * Advances parser to the next registrar element.
+ *
+ * The parser may skip over other types of elements while advancing to the next registrar
+ * element.
+ *
+ * @return true if the parser advanced to a registrar element, false otherwise
+ */
+ public boolean nextRegistrar() {
+ return nextElement(RDE_REGISTRAR_URI, "registrar");
+ }
+
+ /**
+ * Checks if the parser is at a registrar element.
+ *
+ * @return true if the parser is at a registrar element, false otherwise
+ */
+ public boolean isAtRegistrar() {
+ return isAtElement(RDE_REGISTRAR_URI, "registrar");
+ }
+
+ /**
+ * Gets the current registrar.
+ *
+ * The parser must be at a registrar element before this method is called, or an
+ * {@link IllegalStateException} will be thrown. Check the return value of {@link #isAtRegistrar}
+ * or {@link #nextRegistrar} to determine if the parser is at a registrar element.
+ *
+ * @return Jaxb Registrar
+ * @throws IllegalStateException if the parser is not at a registrar element
+ */
+ public XjcRdeRegistrar getRegistrar() {
+ XjcRdeRegistrarElement element =
+ (XjcRdeRegistrarElement) unmarshalElement(RDE_REGISTRAR_URI, "registrar");
+ return element.getValue();
+ }
+
+ /**
+ * Advances parser to the next IDN element.
+ *
+ * The parser may skip over other types of elements while advancing to the next IDN element.
+ *
+ * @return true if the parser advanced to a IDN element, false otherwise
+ */
+ public boolean nextIdn() {
+ return nextElement(RDE_IDN_URI, "idnTableRef");
+ }
+
+ /**
+ * Checks if the parser is at a IDN element.
+ *
+ * @return true if the parser is at a IDN element, false otherwise
+ */
+ public boolean isAtIdn() {
+ return isAtElement(RDE_IDN_URI, "idnTableRef");
+ }
+
+ /**
+ * Gets the current IDN.
+ *
+ * The parser must be at a IDN element before this method is called, or an
+ * {@link IllegalStateException} will be thrown. Check the return value of {@link #isAtIdn} or
+ * {@link #nextIdn} to determine if the parser is at a IDN element.
+ *
+ * @return Jaxb IDN
+ * @throws IllegalStateException if the parser is not at a IDN element
+ */
+ public XjcRdeIdn getIdn() {
+ XjcRdeIdnElement element = (XjcRdeIdnElement) unmarshalElement(RDE_IDN_URI, "idnTableRef");
+ return element.getValue();
+ }
+
+ /**
+ * Advances parser to the next NNDN element.
+ *
+ * The parser may skip over other types of elements while advancing to the next NNDN element.
+ *
+ * @return true if the parser advanced to a NNDN element, false otherwise
+ */
+ public boolean nextNndn() {
+ return nextElement(RDE_NNDN_URI, "NNDN");
+ }
+
+ /**
+ * Checks if the parser is at a NNDN element.
+ *
+ * @return true if the parser is at a NNDN element, false otherwise
+ */
+ public boolean isAtNndn() {
+ return isAtElement(RDE_NNDN_URI, "NNDN");
+ }
+
+ /**
+ * Gets the current NNDN.
+ *
+ * The parser must be at a NNDN element before this method is called, or an
+ * {@link IllegalStateException} will be thrown. Check the return value of {@link #isAtNndn} or
+ * {@link #nextNndn} to determine if the parser is at a NNDN element.
+ *
+ * @return Jaxb NNDN
+ * @throws IllegalStateException if the parser is not at a NNDN element
+ */
+ public XjcRdeNndn getNndn() {
+ XjcRdeNndnElement element = (XjcRdeNndnElement) unmarshalElement(RDE_NNDN_URI, "NNDN");
+ return element.getValue();
+ }
+
+ /**
+ * Advances parser to the next eppParams element.
+ *
+ * The parser may skip over other types of elements while advancing to the next eppParams
+ * element.
+ *
+ * @return true if the parser advanced to a eppParams element, false otherwise
+ */
+ public boolean nextEppParams() {
+ return nextElement(RDE_EPP_PARAMS_URI, "eppParams");
+ }
+
+ /**
+ * Checks if the parser is at a eppParams element.
+ *
+ * @return true if the parser is at a eppParams element, false otherwise
+ */
+ public boolean isAtEppParams() {
+ return isAtElement(RDE_EPP_PARAMS_URI, "eppParams");
+ }
+
+ /**
+ * Gets the current eppParams.
+ *
+ * The parser must be at a eppParams element before this method is called, or an
+ * {@link IllegalStateException} will be thrown. Check the return value of {@link #isAtEppParams}
+ * or {@link #nextEppParams} to determine if the parser is at a eppParams element.
+ *
+ * @return Jaxb EppParams
+ * @throws IllegalStateException if the parser is not at a eppParams element
+ */
+ public XjcRdeEppParams getEppParams() {
+ XjcRdeEppParamsElement element =
+ (XjcRdeEppParamsElement) unmarshalElement(RDE_EPP_PARAMS_URI, "eppParams");
+ return element.getValue();
+ }
+}
diff --git a/java/google/registry/repositories.bzl b/java/google/registry/repositories.bzl
index 6c2028bb1..5940115f9 100644
--- a/java/google/registry/repositories.bzl
+++ b/java/google/registry/repositories.bzl
@@ -455,3 +455,9 @@ def domain_registry_repositories():
artifact = "com.google.truth:truth:0.28",
sha1 = "0a388c7877c845ff4b8e19689dda5ac9d34622c4",
)
+
+ native.maven_jar(
+ name = "stax2_api",
+ artifact = "org.codehaus.woodstox:stax2-api:3.1.4",
+ sha1 = "ac19014b1e6a7c08aad07fe114af792676b685b7",
+ )
diff --git a/javatests/google/registry/rde/RdeParserTest.java b/javatests/google/registry/rde/RdeParserTest.java
new file mode 100644
index 000000000..09f26ea6f
--- /dev/null
+++ b/javatests/google/registry/rde/RdeParserTest.java
@@ -0,0 +1,482 @@
+// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
+//
+// 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 google.registry.rde;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.io.ByteSource;
+
+import google.registry.rde.RdeParser.RdeHeader;
+import google.registry.testing.ExceptionRule;
+import google.registry.xjc.rdecontact.XjcRdeContact;
+import google.registry.xjc.rdedomain.XjcRdeDomain;
+import google.registry.xjc.rdeeppparams.XjcRdeEppParams;
+import google.registry.xjc.rdehost.XjcRdeHost;
+import google.registry.xjc.rdeidn.XjcRdeIdn;
+import google.registry.xjc.rdenndn.XjcRdeNndn;
+import google.registry.xjc.rderegistrar.XjcRdeRegistrar;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/** Unit tests for {@link RdeParser}. */
+@RunWith(JUnit4.class)
+public class RdeParserTest {
+
+ private static final ByteSource DEPOSIT_XML = RdeTestData.get("deposit_full_parser.xml");
+
+ private InputStream xml;
+
+ @Rule
+ public final ExceptionRule thrown = new ExceptionRule();
+
+ private void checkHeader(RdeHeader header) {
+ assertThat(header.getTld()).isEqualTo("test");
+ assertThat(header.getContactCount()).isEqualTo(1L);
+ assertThat(header.getDomainCount()).isEqualTo(2L);
+ assertThat(header.getEppParamsCount()).isEqualTo(1L);
+ assertThat(header.getHostCount()).isEqualTo(1L);
+ assertThat(header.getIdnCount()).isEqualTo(1L);
+ assertThat(header.getNndnCount()).isEqualTo(1L);
+ assertThat(header.getRegistrarCount()).isEqualTo(1L);
+ }
+
+ @Before
+ public void before() throws IOException {
+ xml = new ByteArrayInputStream(DEPOSIT_XML.read());
+ }
+
+ @After
+ public void after() throws IOException {
+ xml.close();
+ }
+
+ @Test
+ public void testGetHeader_returnsHeader() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ checkHeader(parser.getHeader());
+ }
+
+ @Test
+ public void testGetContactNotAtElement_throwsIllegalStateException() throws Exception {
+ thrown.expect(IllegalStateException.class,
+ "Not at element urn:ietf:params:xml:ns:rdeContact-1.0:contact");
+ RdeParser parser = new RdeParser(xml);
+ parser.getContact();
+ }
+
+ @Test
+ public void testGetContactAtElement_returnsContact() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ parser.nextContact();
+ XjcRdeContact contact = parser.getContact();
+ assertThat(contact.getId()).isEqualTo("sh8013");
+ assertThat(contact.getClID()).isEqualTo("RegistrarX");
+ }
+
+ @Test
+ public void testNextContact_advancesParser() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ assertThat(parser.isAtContact()).isFalse();
+ // there is only one contact in the escrow file
+ assertThat(parser.nextContact()).isTrue();
+ assertThat(parser.isAtContact()).isTrue();
+ assertThat(parser.nextContact()).isFalse();
+ assertThat(parser.isAtContact()).isFalse();
+ }
+
+ @Test
+ public void testSkipZeroContacts_skipsZero() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ assertThat(parser.skipContacts(0)).isEqualTo(0);
+ assertThat(parser.nextContact()).isTrue();
+ }
+
+ @Test
+ public void testSkipOneContactFromBeginning_skipsOne() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ assertThat(parser.skipContacts(1)).isEqualTo(1);
+ assertThat(parser.isAtContact()).isFalse();
+ }
+
+ @Test
+ public void testSkipOneContactFromFirstContact_skipsOne() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ parser.nextContact();
+ assertThat(parser.skipContacts(1)).isEqualTo(1);
+ assertThat(parser.isAtContact()).isFalse();
+ }
+
+ @Test
+ public void testSkip9999Contacts_skipsOne() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ assertThat(parser.skipContacts(9999)).isEqualTo(1);
+ assertThat(parser.isAtContact()).isFalse();
+ }
+
+ @Test
+ public void testSkipContactsFromEnd_skipsZero() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ parser.nextContact();
+ parser.nextContact();
+ assertThat(parser.skipContacts(1)).isEqualTo(0);
+ assertThat(parser.isAtContact()).isFalse();
+ }
+
+ @Test
+ public void testGetHeaderAfterNextContact_returnsHeader() throws Exception {
+ // verify that the header is still available after advancing to next contact
+ RdeParser parser = new RdeParser(xml);
+ parser.nextContact();
+ checkHeader(parser.getHeader());
+ }
+
+ @Test
+ public void testGetDomainNotAtElement_throwsIllegalStateException() throws Exception {
+ thrown.expect(IllegalStateException.class,
+ "Not at element urn:ietf:params:xml:ns:rdeDomain-1.0:domain");
+ RdeParser parser = new RdeParser(xml);
+ parser.getDomain();
+ }
+
+ @Test
+ public void testGetDomainAtElement_returnsDomain() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ parser.nextDomain();
+ XjcRdeDomain domain = parser.getDomain();
+ assertThat(domain.getName()).isEqualTo("example1.test");
+ }
+
+ @Test
+ public void testNextDomain_advancesParser() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ // there are 2 domains in the escrow file
+ assertThat(parser.isAtDomain()).isFalse();
+ assertThat(parser.nextDomain()).isTrue();
+ assertThat(parser.isAtDomain()).isTrue();
+ assertThat(parser.nextDomain()).isTrue();
+ assertThat(parser.isAtDomain()).isTrue();
+ assertThat(parser.nextDomain()).isFalse();
+ assertThat(parser.isAtDomain()).isFalse();
+ }
+
+ @Test
+ public void testSkipZeroDomains_skipsZero() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ assertThat(parser.skipDomains(0)).isEqualTo(0);
+ assertThat(parser.nextDomain()).isTrue();
+ }
+
+ @Test
+ public void testSkipOneDomainFromBeginning_skipsOne() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ assertThat(parser.skipDomains(1)).isEqualTo(1);
+ // there are two domains
+ assertThat(parser.isAtDomain()).isTrue();
+ // prove that the parser advanced to the second domain
+ assertThat(parser.nextDomain()).isFalse();
+ assertThat(parser.isAtDomain()).isFalse();
+ }
+
+ @Test
+ public void testSkipTwoDomainsFromBeginning_skipsTwo() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ assertThat(parser.skipDomains(2)).isEqualTo(2);
+ // there are two domains
+ assertThat(parser.isAtDomain()).isFalse();
+ }
+
+ @Test
+ public void testSkipOneDomainFromFirstDomain_skipsOne() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ parser.nextDomain();
+ assertThat(parser.skipDomains(1)).isEqualTo(1);
+ // there are two domains
+ assertThat(parser.isAtDomain()).isTrue();
+ // prove that the parser advanced to the second domain
+ assertThat(parser.nextDomain()).isFalse();
+ assertThat(parser.isAtDomain()).isFalse();
+ }
+
+ @Test
+ public void testSkipTwoDomainsFromFirstDomain_skipsTwo() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ parser.nextDomain();
+ assertThat(parser.skipDomains(2)).isEqualTo(2);
+ // there are two domains
+ assertThat(parser.isAtDomain()).isFalse();
+ }
+
+ @Test
+ public void testSkip9999Domains_skipsTwo() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ assertThat(parser.skipDomains(9999)).isEqualTo(2);
+ assertThat(parser.isAtDomain()).isFalse();
+ }
+
+ @Test
+ public void testSkipDomainsFromEnd_skipsZero() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ parser.nextDomain();
+ parser.nextDomain();
+ parser.nextDomain();
+ assertThat(parser.skipDomains(1)).isEqualTo(0);
+ }
+
+ @Test
+ public void testGetHeaderAfterNextDomain_returnsHeader() throws Exception {
+ // verify that the header is still available after advancing to next domain
+ RdeParser parser = new RdeParser(xml);
+ parser.nextDomain();
+ checkHeader(parser.getHeader());
+ }
+
+ @Test
+ public void testGetHostNotAtElement_throwsIllegalStateException() throws Exception {
+ thrown.expect(IllegalStateException.class,
+ "Not at element urn:ietf:params:xml:ns:rdeHost-1.0:host");
+ RdeParser parser = new RdeParser(xml);
+ parser.getHost();
+ }
+
+ @Test
+ public void testGetHostAtElement_returnsHost() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ parser.nextHost();
+ XjcRdeHost host = parser.getHost();
+ assertThat(host.getName()).isEqualTo("ns1.example.com");
+ }
+
+ @Test
+ public void testNextHost_advancesParser() throws Exception {
+ // the header lies, there are 2 hosts in the file
+ RdeParser parser = new RdeParser(xml);
+ assertThat(parser.isAtHost()).isFalse();
+ assertThat(parser.nextHost()).isTrue();
+ assertThat(parser.isAtHost()).isTrue();
+ assertThat(parser.nextHost()).isTrue();
+ assertThat(parser.isAtHost()).isTrue();
+ assertThat(parser.nextHost()).isFalse();
+ assertThat(parser.isAtHost()).isFalse();
+ }
+
+ @Test
+ public void testSkipZeroHosts_skipsZero() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ assertThat(parser.skipHosts(0)).isEqualTo(0);
+ assertThat(parser.nextHost()).isTrue();
+ }
+
+ @Test
+ public void testSkipOneHostFromBeginning_skipsOne() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ assertThat(parser.skipHosts(1)).isEqualTo(1);
+ // there are two hosts
+ assertThat(parser.isAtHost()).isTrue();
+ // prove that the parser advanced to the second host
+ assertThat(parser.nextHost()).isFalse();
+ assertThat(parser.isAtHost()).isFalse();
+ }
+
+ @Test
+ public void testSkipTwoHostsFromBeginning_skipsTwo() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ assertThat(parser.skipHosts(2)).isEqualTo(2);
+ // there are two hosts
+ assertThat(parser.isAtHost()).isFalse();
+ }
+
+ @Test
+ public void testSkipOneHostFromFirstHost_skipsOne() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ parser.nextHost();
+ assertThat(parser.skipHosts(1)).isEqualTo(1);
+ // there are two hosts
+ assertThat(parser.isAtHost()).isTrue();
+ // prove that the parser advanced to the second host
+ assertThat(parser.nextHost()).isFalse();
+ assertThat(parser.isAtHost()).isFalse();
+ }
+
+ @Test
+ public void testSkipTwoHostsFromFirstHost_skipsTwo() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ parser.nextHost();
+ assertThat(parser.skipHosts(2)).isEqualTo(2);
+ // there are two hosts
+ assertThat(parser.isAtHost()).isFalse();
+ }
+
+ @Test
+ public void testSkip9999Hosts_skipsTwo() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ assertThat(parser.skipHosts(9999)).isEqualTo(2);
+ // there are two hosts
+ assertThat(parser.isAtHost()).isFalse();
+ }
+
+ @Test
+ public void testSkipHostFromEnd_skipsZero() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ parser.nextHost();
+ parser.nextHost();
+ parser.nextHost();
+ assertThat(parser.skipHosts(1)).isEqualTo(0);
+ }
+
+ @Test
+ public void testGetHeaderAfterNextHost_returnsHeader() throws Exception {
+ // verify that the header is still available after advancing to next host
+ RdeParser parser = new RdeParser(xml);
+ parser.nextHost();
+ checkHeader(parser.getHeader());
+ }
+
+ @Test
+ public void testGetRegistrarNotAtElement_throwsIllegalStateException() throws Exception {
+ thrown.expect(IllegalStateException.class,
+ "Not at element urn:ietf:params:xml:ns:rdeRegistrar-1.0:registrar");
+ RdeParser parser = new RdeParser(xml);
+ parser.getRegistrar();
+ }
+
+ @Test
+ public void testGetRegistrarAtElement_returnsRegistrar() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ parser.nextRegistrar();
+ XjcRdeRegistrar registrar = parser.getRegistrar();
+ assertThat(registrar.getId()).isEqualTo("RegistrarX");
+ assertThat(registrar.getName()).isEqualTo("Registrar X");
+ }
+
+ @Test
+ public void testNextRegistrar_advancesParser() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ assertThat(parser.isAtRegistrar()).isFalse();
+ assertThat(parser.nextRegistrar()).isTrue();
+ assertThat(parser.isAtRegistrar()).isTrue();
+ assertThat(parser.nextRegistrar()).isFalse();
+ assertThat(parser.isAtRegistrar()).isFalse();
+ }
+
+ @Test
+ public void testGetHeaderAfterNextRegistrar_returnsHeader() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ parser.nextRegistrar();
+ checkHeader(parser.getHeader());
+ }
+
+ @Test
+ public void testGetNndnNotAtElement_throwsIllegalStateException() throws Exception {
+ thrown.expect(IllegalStateException.class,
+ "Not at element urn:ietf:params:xml:ns:rdeNNDN-1.0:NNDN");
+ RdeParser parser = new RdeParser(xml);
+ parser.getNndn();
+ }
+
+ @Test
+ public void testGetNndnAtElement_returnsNndn() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ parser.nextNndn();
+ XjcRdeNndn nndn = parser.getNndn();
+ assertThat(nndn.getAName()).isEqualTo("xn--exampl-gva.test");
+ }
+
+ @Test
+ public void testNextNndn_advancesParser() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ assertThat(parser.isAtNndn()).isFalse();
+ assertThat(parser.nextNndn()).isTrue();
+ assertThat(parser.isAtNndn()).isTrue();
+ assertThat(parser.nextNndn()).isFalse();
+ assertThat(parser.isAtNndn()).isFalse();
+ }
+
+ @Test
+ public void testGetHeaderAfterNextNndn_returnsHeader() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ parser.nextNndn();
+ checkHeader(parser.getHeader());
+ }
+
+ @Test
+ public void testGetIdnNotAtElement_throwsIllegalStateException() throws Exception {
+ thrown.expect(IllegalStateException.class,
+ "Not at element urn:ietf:params:xml:ns:rdeIDN-1.0:idnTableRef");
+ RdeParser parser = new RdeParser(xml);
+ parser.getIdn();
+ }
+
+ @Test
+ public void testGetIdnAtElement_returnsIdn() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ parser.nextIdn();
+ XjcRdeIdn idn = parser.getIdn();
+ // url contains whitespace
+ assertThat(idn.getUrl().trim())
+ .isEqualTo("http://www.iana.org/domains/idn-tables/tables/br_pt-br_1.0.html");
+ }
+
+ @Test
+ public void testNextIdn_advancesParser() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ assertThat(parser.isAtIdn()).isFalse();
+ assertThat(parser.nextIdn()).isTrue();
+ assertThat(parser.isAtIdn()).isTrue();
+ assertThat(parser.nextIdn()).isFalse();
+ assertThat(parser.isAtIdn()).isFalse();
+ }
+
+ @Test
+ public void testGetHeaderAfterNextIdn_returnsHeader() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ parser.nextIdn();
+ checkHeader(parser.getHeader());
+ }
+
+ @Test
+ public void testGetEppParamsNotAtElement_throwsIllegalStateException() throws Exception {
+ thrown.expect(IllegalStateException.class,
+ "Not at element urn:ietf:params:xml:ns:rdeEppParams-1.0:eppParams");
+ RdeParser parser = new RdeParser(xml);
+ parser.getEppParams();
+ }
+
+ @Test
+ public void testGetEppParamsAtElement_returnsEppParams() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ parser.nextEppParams();
+ XjcRdeEppParams eppParams = parser.getEppParams();
+ assertThat(eppParams.getVersions()).containsExactly("1.0");
+ }
+
+ @Test
+ public void testNextEppParamsAdvancesParser() throws Exception {
+ RdeParser parser = new RdeParser(xml);
+ assertThat(parser.isAtEppParams()).isFalse();
+ assertThat(parser.nextEppParams()).isTrue();
+ assertThat(parser.isAtEppParams()).isTrue();
+ assertThat(parser.nextEppParams()).isFalse();
+ assertThat(parser.isAtEppParams()).isFalse();
+ }
+}
diff --git a/javatests/google/registry/rde/testdata/deposit_full_parser.xml b/javatests/google/registry/rde/testdata/deposit_full_parser.xml
new file mode 100644
index 000000000..70f607571
--- /dev/null
+++ b/javatests/google/registry/rde/testdata/deposit_full_parser.xml
@@ -0,0 +1,259 @@
+
+