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 counts; + private final String tld; + + public String getTld() { + return tld; + } + + public Long getDomainCount() { + return Optional.fromNullable(counts.get(RDE_DOMAIN_URI)).or(0L); + } + + public Long getHostCount() { + return Optional.fromNullable(counts.get(RDE_HOST_URI)).or(0L); + } + + public Long getContactCount() { + return Optional.fromNullable(counts.get(RDE_CONTACT_URI)).or(0L); + } + + public Long getRegistrarCount() { + return Optional.fromNullable(counts.get(RDE_REGISTRAR_URI)).or(0L); + } + + public Long getIdnCount() { + return Optional.fromNullable(counts.get(RDE_IDN_URI)).or(0L); + } + + public Long getNndnCount() { + return Optional.fromNullable(counts.get(RDE_NNDN_URI)).or(0L); + } + + public Long getEppParamsCount() { + return Optional.fromNullable(counts.get(RDE_EPP_PARAMS_URI)).or(0L); + } + + private RdeHeader(XjcRdeHeader header) { + this.tld = header.getTld(); + ImmutableMap.Builder builder = new ImmutableMap.Builder<>(); + for (XjcRdeHeaderCount count : header.getCounts()) { + builder = builder.put(count.getUri(), count.getValue()); + } + counts = builder.build(); + } + } + + private final XMLStreamReader reader; + private RdeHeader header; + + /** + * Creates a new instance of {@link RdeParser} + * + * @param xmlInput Contents of the escrow deposit file + */ + public RdeParser(InputStream xmlInput) throws XMLStreamException { + XMLInputFactory factory = XMLInputFactory.newInstance(); + reader = factory.createXMLStreamReader(xmlInput); + header = new RdeHeader(readHeader()); + } + + /** + * Attempts to read the RDE header as a jaxb object. + * + * @throws IllegalStateException if no RDE header is found in the file + */ + private XjcRdeHeader readHeader() { + if (!nextElement(RDE_HEADER_URI, "header")) { + throw new IllegalStateException("No RDE Header found"); + } + XjcRdeHeaderElement element = (XjcRdeHeaderElement) unmarshalElement(RDE_HEADER_URI, "header"); + return element.getValue(); + } + + /** + * Unmarshals the current element into a Jaxb element. + * + * @param uri Element URI + * @param name Element Name + * @return Jaxb Element + * @throws IllegalStateException if the parser is not at the specified element + */ + private Object unmarshalElement(String uri, String name) { + checkArgumentNotNull(name, "name cannot be null"); + checkArgumentNotNull(uri, "uri cannot be null"); + try { + if (isAtElement(uri, name)) { + TransformerFactory tf = TransformerFactory.newInstance(); + Transformer t = tf.newTransformer(); + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + t.transform(new StAXSource(reader), new StreamResult(bout)); + ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray()); + Object element = XjcXmlTransformer.unmarshal(bin); + return element; + } else { + throw new IllegalStateException(String.format("Not at element %s:%s", uri, name)); + } + } catch (IllegalStateException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Checks if the parser is at an instance of the specified element. + * + * @param uri Element URI + * @param name Element Name + * @return true if the parser is at an instance of the element, false otherwise + */ + private boolean isAtElement(String uri, String name) { + return reader.getEventType() == XMLStreamReader.START_ELEMENT + && uri.equals(reader.getNamespaceURI()) && name.equals(reader.getName().getLocalPart()); + } + + /** + * Attempts to advance to the next instance of the specified element. + * + *

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 @@ + + + + 2010-10-17T00:00:00Z + + 1.0 + urn:ietf:params:xml:ns:rdeHeader-1.0 + urn:ietf:params:xml:ns:rdeContact-1.0 + urn:ietf:params:xml:ns:rdeHost-1.0 + urn:ietf:params:xml:ns:rdeDomain-1.0 + urn:ietf:params:xml:ns:rdeRegistrar-1.0 + urn:ietf:params:xml:ns:rdeIDN-1.0 + urn:ietf:params:xml:ns:rdeNNDN-1.0 + urn:ietf:params:xml:ns:rdeEppParams-1.0 + + + + + + + test + 2 + + 1 + + 1 + + 1 + + 1 + + 1 + + 1 + + + + + + example1.test + Dexample1-TEST + + jd1234 + sh8013 + sh8013 + + ns1.example.com + ns1.example1.test + + RegistrarX + RegistrarX + 1999-04-03T22:00:00.0Z + 2015-04-03T22:00:00.0Z + + + + + example2.test + Dexample2-TEST + + + jd1234 + sh8013 + sh8013 + RegistrarX + RegistrarX + 1999-04-03T22:00:00.0Z + 2015-04-03T22:00:00.0Z + + + + + ns1.example.com + Hns1_example_com-TEST + + + 192.0.2.2 + 192.0.2.29 + 1080:0:0:0:8:800:200C:417A + + RegistrarX + RegistrarX + 1999-05-08T12:10:00.0Z + RegistrarX + 2009-10-03T09:34:00.0Z + + + + + ns1.example1.test + Hns1_example1_test-TEST + + + 192.0.2.2 + 192.0.2.29 + 1080:0:0:0:8:800:200C:417A + + RegistrarX + RegistrarX + 1999-05-08T12:10:00.0Z + RegistrarX + 2009-10-03T09:34:00.0Z + + + + + sh8013 + Csh8013-TEST + + + + John Doe + Example Inc. + + 123 Example Dr. + Suite 100 + Dulles + VA + 20166-6503 + US + + + +1.7035555555 + + +1.7035555556 + + jdoe@example.test + + RegistrarX + RegistrarX + + 2009-09-13T08:01:00.0Z + RegistrarX + + 2009-11-26T09:10:00.0Z + 2009-12-03T09:05:00.0Z + + + + + + + + + RegistrarX + Registrar X + 123 + ok + + + 123 Example Dr. + + Suite 100 + + Dulles + VA + 20166-6503 + US + + + +1.7035555555 + + +1.7035555556 + + jdoe@example.test + + http://www.example.test + + + whois.example.test + + http://whois.example.test + + + 2005-04-23T11:49:00.0Z + 2009-02-17T17:51:00.0Z + + + + + +http://www.iana.org/domains/idn-tables/tables/br_pt-br_1.0.html + + + http://registro.br/dominio/regras.html + + + + + + xn--exampl-gva.test + pt-BR + example1.test + withheld + 2005-04-23T11:49:00.0Z + + + + + 1.0 + en + + urn:ietf:params:xml:ns:domain-1.0 + + + urn:ietf:params:xml:ns:contact-1.0 + + + urn:ietf:params:xml:ns:host-1.0 + + + urn:ietf:params:xml:ns:rgp-1.0 + + urn:ietf:params:xml:ns:secDNS-1.1 + + + + + + + + + + + + + + + + + + + + + +