// 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();
}
}