/***********************************************************
Copyright (C) 2004 VeriSign, Inc.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

http://www.verisign.com/nds/naming/namestore/techdocs.html
 ***********************************************************/
package com.verisign.epp.codec.gen;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import com.verisign.epp.util.EPPCatFactory;

/**
 * Represents a &lt;trID&gt; element of a response, which uniquely identifies a
 * transaction. A transaction identifier SHALL include the following child
 * elements:<br>
 * <br>
 * 
 * <ul>
 * <li>
 * An optional &lt;clTRID&gt; element that mirrors the optional &lt;clID&gt; element
 * in the command. Use <code>getClientTransId</code> and
 * <code>setClientTransId</code> to get and set the element.
 * <code>hasClientTransId</code> can be used to determine if the element exists.
 * </li>
 * <li>
 * A &lt;svTRID&gt; element that is generated by the EPP Server that uniquely
 * identifies the server transaction. Use <code>getServerTransId</code> and
 * <code>setServerTransId</code> to get and set the element.</li>
 * </ul>
 */
public class EPPTransId implements EPPCodecComponent {
	/** Minimum transaction id length. */
	public final static int MIN_TRANSID_LEN = 3;

	/** Maximum transaction id length. */
	public final static int MAX_TRANSID_LEN = 64;

	/** XML root tag name for <code>EPPTransId</code>. */
	final static String ELM_NAME = "trID";

	/** XML tag name for the clientTransId attribute. */
	private static final String ELM_CLIENT_TRANSID = "clTRID";

	/** XML tag name for the serverTransId attribute. */
	private static final String ELM_SERVER_TRANSID = "svTRID";

	/** Log4j category for logging */
	private static Logger cat = Logger.getLogger(EPPTransId.class.getName(),
			EPPCatFactory.getInstance().getFactory());

	/** Client specified transaction id */
	private String clientTransId;

	/** Server generated transaction id */
	private String serverTransId;

	/**
	 * Root mapping name such as domain or contact (e.g.
	 * <code>domain:trID</code> for domain mapping). This attribute needs to be
	 * specified before calling encode(Document) method.
	 */
	private String rootName = ELM_NAME;

	/**
	 * XML namespace URI for the root element.
	 */
	private String rootNS = EPPCodec.NS;

	/**
	 * Namespace prefix to use for root element.
	 */
	private String rootPrefix = "";

	/**
	 * Allocates a new <code>EPPTransId</code> with <code>null</code> default
	 * attribute values. The server transaction id must be set before calling
	 * <code>encode</code>.
	 */
	public EPPTransId() {
		clientTransId = null;
		serverTransId = null;
	}

	/**
	 * Allocates a new <code>EPPTransId</code> with just a server transaction id
	 * specified.
	 * 
	 * @param aServerTransId
	 *            Server generated transaction id that uniquely identifies the
	 *            transaction.
	 */
	public EPPTransId(String aServerTransId) {
		clientTransId = null;
		serverTransId = aServerTransId;
	}

	/**
	 * Allocates a new <code>EPPTransId</code> with both the client transaction
	 * id and the server transaction id specified.
	 * 
	 * @param aClientTransId
	 *            Client specified transaction id contained in an EPP command.
	 * @param aServerTransId
	 *            Server generated transaction id that uniquely identifies the
	 *            transaction.
	 */
	public EPPTransId(String aClientTransId, String aServerTransId) {
		clientTransId = aClientTransId;
		serverTransId = aServerTransId;
	}

	/**
	 * Allocates a new <code>EPPTransId</code> with both the client transaction
	 * id and the server transaction id specified along with a specific root tag
	 * name.
	 * 
	 * @param aRootNS
	 *            Root element namespace URI
	 * @param aRootName
	 *            Root element of trans id.
	 * @param aClientTransId
	 *            Client specified transaction id contained in an EPP command.
	 * @param aServerTransId
	 *            Server generated transaction id that uniquely identifies the
	 *            transaction.
	 */
	public EPPTransId(String aRootNS, String aRootName, String aClientTransId,
			String aServerTransId) {
		this.setRootName(aRootNS, aRootName);
		clientTransId = aClientTransId;
		serverTransId = aServerTransId;
	}

	/**
	 * Gets the root element XML namespace URI.
	 * 
	 * @return root element XML namespace URI
	 */
	public String getRootNS() {
		return this.rootNS;
	}

	/**
	 * Gets the root tag such as domain or host specific tags. The default value
	 * is set to <code>ELM_NAME</code>.
	 * 
	 * @return The root tag used.
	 */
	public String getRootName() {
		return rootName;
	}

	/**
	 * Sets the root tag such as domain or host specific tags.
	 * 
	 * @param aRootNS
	 *            Namespace URI for the root tag
	 * @param aRootName
	 *            Sets the root tag
	 */
	public void setRootName(String aRootNS, String aRootName) {
		this.rootNS = aRootNS;
		this.rootName = aRootName;
		this.rootPrefix = EPPUtil.getPrefix(aRootName);
		if (this.rootPrefix.length() != 0) {
			this.rootPrefix = this.rootPrefix + ":";
		}
	}

	/**
	 * Gets the client specified transaction id.
	 * 
	 * @return the client specified transaction id <code>String</code> if
	 *         defined; <code>null</code> otherwise.
	 */
	public String getClientTransId() {
		return clientTransId;
	}

	/**
	 * Sets the client specified transaction id.
	 * 
	 * @param aClientTransId
	 *            the client specified transaction id.
	 */
	public void setClientTransId(String aClientTransId) {
		clientTransId = aClientTransId;
	}

	/**
	 * Gets the server generated transaction id.
	 * 
	 * @return the server generated transaction id <code>String</code> if
	 *         defined; <code>null</code> otherwise.
	 */
	public String getServerTransId() {
		return serverTransId;
	}

	/**
	 * Sets the server generated transaction id.
	 * 
	 * @param aServerTransId
	 *            the server generated transaction id.
	 */
	public void setServerTransId(String aServerTransId) {
		serverTransId = aServerTransId;
	}

	/**
	 * encode <code>EPPTransId</code> into a DOM element tree. The &lt;trID&gt; or
	 * the element name set with <code>setRootName</code> is creates and the
	 * attribute nodes are appended as children.
	 * 
	 * @param aDocument
	 *            DOCUMENT ME!
	 * 
	 * @return &lt;trID&gt; root element tree.
	 * 
	 * @exception EPPEncodeException
	 *                Error encoding the DOM element tree.
	 */
	public Element encode(Document aDocument) throws EPPEncodeException {
		// Check pre-conditions
		if (serverTransId == null) {
			throw new EPPEncodeException(
					"EPPTransId required attribute \"server tranaction id\" is null.");
		}

		if ((serverTransId.length() < MIN_TRANSID_LEN)
				|| (serverTransId.length() > MAX_TRANSID_LEN)) {
			throw new EPPEncodeException(
					"EPPTransId server transaction id length of "
							+ serverTransId.length()
							+ "is out of range, must be between "
							+ MIN_TRANSID_LEN + " and " + MAX_TRANSID_LEN);
		}

		if ((clientTransId != null)
				&& ((clientTransId.length() < MIN_TRANSID_LEN) || (clientTransId
						.length() > MAX_TRANSID_LEN))) {
			throw new EPPEncodeException(
					"EPPTransId client transaction id length of "
							+ clientTransId.length()
							+ "is out of range, must be between "
							+ MIN_TRANSID_LEN + " and " + MAX_TRANSID_LEN);
		}

		Element root = aDocument.createElementNS(this.rootNS, this.rootName);

		// Client Transaction Id
		EPPUtil.encodeString(aDocument, root, clientTransId, EPPCodec.NS,
				ELM_CLIENT_TRANSID);

		// Server Transaction Id
		EPPUtil.encodeString(aDocument, root, serverTransId, EPPCodec.NS,
				ELM_SERVER_TRANSID);

		return root;
	}

	/**
	 * decode <code>EPPTransId</code> from a DOM element tree. The
	 * <code>aElement</code> argument needs to be the "trID" element, or an
	 * element that conforms to the XML structure of "trID".
	 * 
	 * @param aElement
	 *            The "trID" XML element.
	 * 
	 * @exception EPPDecodeException
	 *                Error decoding the DOM element tree.
	 */
	public void decode(Element aElement) throws EPPDecodeException {
		this.setRootName(aElement.getNamespaceURI(), aElement.getTagName());

		// Client Transaction Id
		clientTransId = EPPUtil.decodeString(aElement, EPPCodec.NS,
				ELM_CLIENT_TRANSID);

		// Server Transaction Id
		serverTransId = EPPUtil.decodeString(aElement, EPPCodec.NS,
				ELM_SERVER_TRANSID);
	}

	/**
	 * implements a deep <code>EPPTransId</code> compare.
	 * 
	 * @param aObject
	 *            <code>EPPTransId</code> instance to compare with
	 * 
	 * @return {@code true} if equal; {@code false} otherwise
	 */
	public boolean equals(Object aObject) {
		if (!(aObject instanceof EPPTransId)) {
			cat.error("EPPTransId.equals(): " + aObject.getClass().getName()
					+ " not EPPTransId instance");

			return false;
		}

		EPPTransId theTransId = (EPPTransId) aObject;

		// Root namespace
		if (!this.rootNS.equals(theTransId.rootNS)) {
			cat.error("EPPTransId.equals(): rootNS not equal");

			return false;
		}

		// Root name
		if (!EPPUtil.getLocalName(this.rootName).equals(
				EPPUtil.getLocalName(theTransId.rootName))) {
			cat.error("EPPTransId.equals(): rootName not equal");

			return false;
		}

		// Client Transaction Id
		if (!((clientTransId == null) ? (theTransId.clientTransId == null)
				: clientTransId.equals(theTransId.clientTransId))) {
			cat.error("EPPTransId.equals(): clientTransId not equal");

			return false;
		}

		// Server Transaction Id
		if (!((serverTransId == null) ? (theTransId.serverTransId == null)
				: serverTransId.equals(theTransId.serverTransId))) {
			cat.error("EPPTransId.equals(): serverTransId not equal");

			return false;
		}

		return true;
	}

	/**
	 * Clone <code>EPPTransId</code>.
	 * 
	 * @return Deep copy clone of <code>EPPTransId</code>
	 * 
	 * @exception CloneNotSupportedException
	 *                standard Object.clone exception
	 */
	public Object clone() throws CloneNotSupportedException {
		EPPTransId clone = null;

		clone = (EPPTransId) super.clone();

		return clone;
	}

	/**
	 * Implementation of <code>Object.toString</code>, which will result in an
	 * indented XML <code>String</code> representation of the concrete
	 * <code>EPPCodecComponent</code>.
	 * 
	 * @return Indented XML <code>String</code> if successful;
	 *         <code>ERROR</code> otherwise.
	 */
	public String toString() {
		return EPPUtil.toString(this);
	}

	/**
	 * Returns the XML namespace associated with the <code>EPPCodecComponent</code>.
	 * 
	 * @return XML namespace for the <code>EPPCodecComponent</code>. 
	 */
	@Override
	public String getNamespace() {
		return EPPCodec.NS;
	}

}
