//______________________________________________________________________________
//   |       |
//   | knipp |           Knipp Medien und Kommunikation GmbH
//    -------
//
//
//     Knipp RRI Toolkit
//
//
//     Copyright (C) 2005 by Knipp Medien und Kommunikation GmbH
//
//     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 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 
//
//
//     initial author:    Klaus Malorny
//
//     version:           1.3 (15112), 2011/05/27 16:00:14
//                        $Revision: 14987 $ 
//                        $Date: 2011-01-06 12:03:23 +0100 (Thu, 06 Jan 2011) $
//                        $Author: Klaus.Malorny $
//

package de.knipp.rritoolkit;

import java.util.Collection;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Set;
import java.util.EnumSet;
import java.util.Date;
import java.util.Collections;
import java.util.StringTokenizer;

import java.io.Serializable;

import org.w3c.dom.Node;
import org.w3c.dom.Element;
import org.w3c.dom.DOMException;

import static de.knipp.rritoolkit.DenicConstants.NS_CONTACT;
import static de.knipp.rritoolkit.DenicConstants.PREFIX_CONTACT;

import static de.knipp.rritoolkit.Utils.parseDateTime;

import static de.knipp.rritoolkit.XmlHelper.getElements;
import static de.knipp.rritoolkit.XmlHelper.getFirstElement;
import static de.knipp.rritoolkit.XmlHelper.getFirstElementText;
import static de.knipp.rritoolkit.XmlHelper.isNilled;
import static de.knipp.rritoolkit.XmlHelper.asBoolean;

/**
 *  class that holds contact data, both for requests and responses
 *
 *  @version  $Revision: 14987 $
 *  @author   Klaus Malorny
 */

public class ContactData implements Serializable
{
  /** the handle */
  
  private String handle;
  
  
  /** the type */
  
  private Type type;
  
  
  /** the name */
  
  private String name;
  
  
  /** the organisation */
  
  private List<String> organisation;
  
  
  /** the address */
  
  private List<String> address;
  
  
  /** the postal code */
  
  private String postalCode;
  
  
  /** the city */
  
  private String city;
  
  
  /** the country code */
  
  private String countryCode;
  
  
  /** the phone numbers */
  
  private List<String> phoneNumbers;
  
  
  /** the fax numbers */
  
  private List<String> faxNumbers;
  
  
  /** the e-mail addresses */
  
  private List<String> emailAddresses;
  
  
  /** the sip addresses */
  
  private List<String> sipAddresses;
  
  
  /** the disclosed fields */
  
  private Set<Field> disclose;
  
  
  /** the remarks */
  
  private List<String> remarks;
  
  
  /** the change date */
  
  private Date changed;


  /** flag whether contact is holder of a domain in dispute */

  private boolean disputeReference;
  
  
  /**
   *  constructor
   */
  
  public ContactData ()
  {
    organisation = new ArrayList<String> ();
    address = new ArrayList<String> ();
    phoneNumbers = new ArrayList<String> ();
    faxNumbers = new ArrayList<String> ();
    emailAddresses = new ArrayList<String> ();
    sipAddresses = new ArrayList<String> ();
    disclose = EnumSet.noneOf (Field.class); 
    remarks = new ArrayList<String> ();
  }
  
  
  
  /**
   *  copy constructor
   *
   *  @param other                the other contact to copy from
   */
  
  public ContactData (ContactData other)
  {
    handle = other.handle;
    type = other.type;
    name = other.name;
    organisation = new ArrayList<String> (other.organisation);
    address = new ArrayList<String> (other.address);
    postalCode = other.postalCode;
    city = other.city;
    countryCode = other.countryCode;
    phoneNumbers = new ArrayList<String> (other.phoneNumbers);
    faxNumbers = new ArrayList<String> (other.faxNumbers);
    emailAddresses = new ArrayList<String> (other.emailAddresses);
    sipAddresses = new ArrayList<String> (other.sipAddresses);
    disclose = EnumSet.copyOf (other.disclose);
    remarks = new ArrayList<String> (other.remarks);
    changed = other.changed;   // date considered as immutable
    disputeReference = other.disputeReference;
  }
  
  
  
  
  
  /**
   *  convert the object to a KV list as part of a contact request
   *
   *  @param list                the list
   *  @throws EncodeException    on encoding exceptions
   */
  
  public void toList (KVList list)
    throws EncodeException
  {
    if (handle != null) list.add ("handle", handle);
    if (type != null) list.add ("type", type.getDenicToken ());
    if (name != null) list.add ("name", name);
    list.add ("organisation", organisation);
    list.add ("address", address);
    if (postalCode != null) list.add ("postalcode", postalCode);
    if (city != null) list.add ("city", city);
    if (countryCode != null) list.add ("countrycode", countryCode);
    list.add ("phone", phoneNumbers);
    list.add ("fax", faxNumbers);
    list.add ("email", emailAddresses);
    list.add ("sip", sipAddresses);
    
    if (disclose.size () > 0)
    {
      StringBuilder sb = new StringBuilder ();
      boolean first = true;
      
      for (Field f: disclose)
      {
        if (first) 
          first = false; 
         else 
          sb.append (", ");
        
        sb.append (f.getDenicToken ());
      }
      
      list.add ("disclose", sb.toString ());
    }
    
    list.add ("remarks", remarks);
  }

  
  
  /**
   *  parse the given key-value list.
   *
   *  @param list                       the list to parse
   *  @throws ResponseParseException    if the parsing failed
   */
  
  public void fromList (KVList list)
    throws ResponseParseException
  {
    handle = list.getFirst ("handle");
    String temp = list.getFirst ("type");
    
    if (temp != null)
    {
      type = Type.fromToken (temp);
      
      if (type == null) 
        throw new ResponseParseException ("invalid type value",
          "type", temp);
    } else
    {
      type = null;
    }
    
    name = list.getFirst ("name");
    organisation.clear ();
    organisation.addAll (list.getList ("organisation"));
    address.clear ();
    address.addAll (list.getList ("address"));
    postalCode = list.getFirst ("postalcode");
    city = list.getFirst ("city");
    countryCode = list.getFirst ("countrycode");
    phoneNumbers.clear ();
    phoneNumbers.addAll (list.getList ("phone"));
    faxNumbers.clear ();
    faxNumbers.addAll (list.getList ("fax"));
    emailAddresses.clear ();
    emailAddresses.addAll (list.getList ("email"));
    sipAddresses.clear ();
    sipAddresses.addAll (list.getList ("sip"));
    remarks.clear ();
    remarks.addAll (list.getList ("remarks"));
    
    disclose.clear ();
    temp = list.getFirst ("disclose");
    
    if (temp != null)
    {
      StringTokenizer st = new StringTokenizer (temp, " \t,");
      
      while (st.hasMoreTokens ())
      {
        String token = st.nextToken ();
        Field f = Field.fromToken (token);
        
        if (f == null) throw new ResponseParseException (
          "unknown disclose field \"" + token + "\"", "disclose", temp);
        
        disclose.add (f);
      }
    }

    temp = list.getFirst ("disputereference");
    disputeReference = temp != null && temp.equalsIgnoreCase ("true");
    
    changed = parseDateTime (list.getFirst ("changed"));
  }
  


  /**
   *  return whether the element has the disclose attribute set
   *
   *  @param el                       the element, may be {@code null}
   *  @return                         {@code true} if disclosed;
   *                                  {@code false} if {@code el} is
   *                                  {@code null}
   *  @throws DOMException            on DOM exceptions
   */
  
  private boolean isDisclosed (Element el)
    throws DOMException
  {
    return el != null && asBoolean (el.getAttribute ("disclose"), true);
  }
  
  
  /**
   *  parse a single field
   * 
   *  @param node                     the parent node
   *  @param localName                the local name
   *  @param field                    the disclose field
   *  @return                         the value
   *  @throws DOMException            on DOM exceptions
   */
  
  private String  parseField (Node node, String localName, Field field)
    throws DOMException
  {
    Element el = getFirstElement (node, NS_CONTACT, localName);
    if (isDisclosed (el)) disclose.add (field);
    return el == null || isNilled (el) ? null : el.getTextContent ();
  }

  
  /**
   *  parse a field consisting of multiple entries
   *
   *  @param node                     the parent node
   *  @param localName                the local name
   *  @param field                    the disclose field
   *  @param list                     the list to fill
   *  @throws DOMException            on DOM exceptions
   */
  
  private void parseField (Node node, String localName, Field field,
    List<String> list)
    throws DOMException
  {
    list.clear ();
    boolean undisclosed = false;

    for (Element elTemp: getElements (node, NS_CONTACT, localName))
    {
      if (!isNilled (elTemp)) list.add (elTemp.getTextContent ());
      if (!isDisclosed (elTemp)) undisclosed = true;
    }
    
    if (!undisclosed) disclose.add (field);
  }
    
    
    
  /**
   *  parse the given XML subtree
   *
   *  @param node                     the node containing the contact data
   *  @throws DOMException            on DOM exceptions
   *  @throws ResponseParseException  if the parsing failed
   */
  
  public void fromXml (Element node)
    throws DOMException, ResponseParseException
  {
    disclose.clear ();
    
    Element el = getFirstElement (node, NS_CONTACT, "handle");
    handle = el == null || isNilled (el) ? null : el.getTextContent ();
    
    el = getFirstElement (node, NS_CONTACT, "type");
    
    if (el != null && !isNilled (el))
    {
      String temp = el.getTextContent ().trim ();
      type = Type.fromToken (temp);
      if (type == null) throw new ResponseParseException ("invalid type", el);
      
    } else
    {
      type = null;
    }
    
    if (isDisclosed (el)) disclose.add (Field.Type);

    name = parseField (node, "name", Field.Name);
    parseField (node, "organisation", Field.Organisation, organisation);
    el = getFirstElement (node, NS_CONTACT, "postal");
    
    if (el != null)
    { 
      parseField (el, "address", Field.Address, address);
      postalCode = parseField (el, "postalCode", Field.PostalCode);
      city = parseField (el, "city", Field.City);
      countryCode = parseField (el, "countryCode", Field.CountryCode);
    
    } else
    {
      address.clear ();
      postalCode = city = countryCode = null;
    }
    
    parseField (node, "phone", Field.Phone, phoneNumbers);
    parseField (node, "fax", Field.Fax, faxNumbers);
    parseField (node, "email", Field.EMail, emailAddresses);
    parseField (node, "sip", Field.Sip, sipAddresses);
    parseField (node, "remarks", Field.Remarks, remarks);

    disputeReference = asBoolean (
      getFirstElementText (node, NS_CONTACT, "disputeReference"), false);

    changed = parseDateTime (getFirstElementText (node, NS_CONTACT, "changed"));
  }

  
  /**
   *  add an element to the builder
   *
   *  @param xb                the builder
   *  @param localName         the local name
   *  @param value             the value (may be {@code null})
   *  @param field             the field
   *  @throws DOMException     on DOM exceptions
   */
  
  private void add (XmlBuilder xb, String localName, String value, Field field)
    throws DOMException
  {
    if (value == null) return;
    boolean d = disclose != null && disclose.contains (field);
    
    xb.
      es (NS_CONTACT, PREFIX_CONTACT, localName).
      attf ("disclose", d).
      t (value).
      ee ();
  }
  

  /**
   *  add elements for a list to the builder
   *
   *  @param xb                the builder
   *  @param localName         the local name
   *  @param values            the value (may be {@code null})
   *  @param field             the field
   *  @throws DOMException     on DOM exceptions
   */
  
  private void add (XmlBuilder xb, String localName, 
    List<String> values, Field field)
    throws DOMException
  {
    if (values == null || values.isEmpty ()) return;
    boolean d = disclose != null && disclose.contains (field);
    
    for (String value: values)
      xb.
        es (NS_CONTACT, PREFIX_CONTACT, localName).
        attf ("disclose", d).
        t (value).
        ee ();
  }
  
  
  
  /**
   *  convert the object to XML as part of a contact request
   *
   *  @param node              the node to add the contents to
   *  @throws DOMException     on DOM exceptions
   *  @throws EncodeException  on encode exceptions
   */
  
  public void toXml (Node node)
    throws DOMException, EncodeException
  {
    XmlBuilder xb = new XmlBuilder (node);
    xb.elt (NS_CONTACT, PREFIX_CONTACT, "handle", handle);
    add (xb, "type", type == null ? null : type.getDenicToken (), Field.Type);
    add (xb, "name", name, Field.Name);
    add (xb, "organisation", organisation, Field.Organisation);
    xb.es (NS_CONTACT, PREFIX_CONTACT, "postal");
    add (xb, "address", address, Field.Address);
    add (xb, "postalCode", postalCode, Field.PostalCode);
    add (xb, "city", city, Field.City);
    add (xb, "countryCode", countryCode, Field.CountryCode);
    xb.ee ();
    add (xb, "phone", phoneNumbers, Field.Phone);
    add (xb, "fax", faxNumbers, Field.Fax);
    add (xb, "email", emailAddresses, Field.EMail);
    add (xb, "sip", sipAddresses, Field.Sip);
    add (xb, "remarks", remarks, Field.Remarks);
  }
  
  
  
  
  /**
   *  convert the contact to a sting for debugging purposes
   *
   *  @return              a string representation
   */

  @Override
  public String toString ()
  {
    StringBuilder sb = new StringBuilder ();
    sb.append (super.toString ());
    sb.append ("\n    handle: ");
    sb.append (handle);
    sb.append ("\n    type: ");
    sb.append (type);
    sb.append ("\n    name: ");
    sb.append (name);
    
    for (String text: organisation)
    {
      sb.append ("\n    organisation: ");
      sb.append (text);
    }

    for (String text: address)
    {
      sb.append ("\n    address: ");
      sb.append (text);
    }
  
    sb.append ("\n    postal code: ");
    sb.append (postalCode);
    sb.append ("\n    city: ");
    sb.append (city);
    sb.append ("\n    country code: ");
    sb.append (countryCode);

    for (String text: phoneNumbers)
    {
      sb.append ("\n    phone: ");
      sb.append (text);
    }

    for (String text: faxNumbers)
    {
      sb.append ("\n    fax: ");
      sb.append (text);
    }
    
    for (String text: emailAddresses)
    {
      sb.append ("\n    e-mail: ");
      sb.append (text);
    }
    
    for (String text: sipAddresses)
    {
      sb.append ("\n    sip: ");
      sb.append (text);
    }
    
    for (String text: remarks)
    {
      sb.append ("\n    remarks: ");
      sb.append (text);
    }
    
    sb.append ("\n    disclose: ");
    
    boolean first = true;
    
    for (Field field: disclose)
    {
      if (first)
        first = false;
       else
        sb.append (", ");
      
      sb.append (field);
    }
    
    sb.append ("\n    disputeReference: ");
    sb.append (disputeReference);
    sb.append ("\n    changed: ");
    sb.append (changed);
    sb.append ("\n");
    
    return sb.toString ();
  }
  
  
  //--- access methods ---------------------------------------------------------
  
  
  
  /**
   *  return the handle
   *
   *  @return                the handle or {@code null} if not set
   */
  
  public String getHandle ()
  {
    return handle;
  }
  
  
  
  /**
   *  set the handle
   *
   *  @param theHandle       the handle, may be {@code null}
   */
  
  public void setHandle (String theHandle)
  {
    handle = theHandle;
  }
  
  
  
  /**
   *  return the contact type 
   *
   *  @return                the contact type or {@code null} if not set
   */
  
  public Type getType ()
  {
    return type;
  }
  
  
  
  /**
   *  set the type
   *
   *  @param theType         the type, may be {@code null}
   */
  
  public void setType (Type theType)
  {
    type = theType;
  }
  
  
  /**
   *  return the name
   *
   *  @return                the name
   */
  
  public String getName ()
  {
    return name;
  }
  
  
  /**
   *  set the name
   *
   *  @param theName         the name
   */
  
  public void setName (String theName)
  {
    name = theName;
  }
  
  
  
  /**
   *  return the organisation as a list of strings
   *
   *  @return                the list (read-only)
   */
  
  public List<String> getOrganisation ()
  {
    return Collections.unmodifiableList (organisation);
  }
  
  
  
  /**
   *  return the organisation as an array
   *
   *  @return               the array
   */
  
  public String[] getOrganisationAsArray ()
  {
    String[] array = new String[organisation.size ()];
    return organisation.toArray (array);
  }
  
  
  /**
   *  set the organisation
   *
   *  @param list               the list of strings to initialize with,
   *                            may be {@code null}
   */
  
  public void setOrganisation (List<String> list)
  {
    organisation.clear ();
    if (list != null) organisation.addAll (list);
  }
  
  
  /**
   *  set the organisation
   *
   *  @param lines       the lines of the organisation, may be {@code null}
   */
  
  public void setOrganisation (String... lines)
  {
    organisation.clear ();

    if (lines != null)
      for (String line: lines) organisation.add (line);
  }  
  

  
  
  
  /**
   *  return the address as a list of strings
   *
   *  @return                the list (read-only)
   */
  
  public List<String> getAddress ()
  {
    return Collections.unmodifiableList (address);
  }
  
  
  
  /**
   *  return the address as an array
   *
   *  @return               the array or {@code null} if not set
   */
  
  public String[] getAddressAsArray ()
  {
    String[] array = new String[address.size ()];
    return address.toArray (array);
  }
  
  
  /**
   *  set the address
   *
   *  @param list               the list of strings to initialize with,
   *                            may be {@code null}
   */
  
  public void setAddress (List<String> list)
  {
    address.clear ();
    if (list != null) address.addAll (list);
  }
  
  
  /**
   *  set the address
   *
   *  @param lines       the lines of the address, may be {@code null}
   */
  
  public void setAddress (String... lines)
  {
    address.clear ();
    if (lines != null)
      for (String line: lines) address.add (line);
  }  
  
  
  
  /**
   *  return the postal code
   *
   *  @return                   the postal code, may be {@code null}
   */
  
  public String getPostalCode ()
  {
    return postalCode;
  }
  
  
  /**
   *  set the postal code
   *
   *  @param code              the postal code, may be {@code null}
   */
  
  public void setPostalCode (String code)
  {
    postalCode = code;
  }
  
  
  
  /**
   *  return the city
   *
   *  @return                the city, may be {@code null}
   */
  
  public String getCity ()
  {
    return city;
  }
  
  
  
  /**
   *  set the city
   *
   *  @param theCity         the city, may be {@code null}
   */
  
  public void setCity (String theCity)
  {
    city = theCity;
  }
  
  
  
  /**
   *  return the country code
   *
   *  @return                the country code, may be {@code null}
   */
  
  public String getCountryCode ()
  {
    return countryCode;
  }
  
  
  /**
   *  set the country code
   *
   *  @param code            the country code, may be {@code null}
   */
  
  public void setCountryCode (String code)
  {
    countryCode = code;
  }
  
  
  

  /**
   *  return the phone numbers as a list of strings
   *
   *  @return                the list (read-only)
   */

  public List<String> getPhoneNumbers ()
  {
    return Collections.unmodifiableList (phoneNumbers);
  }



  /**
   *  return the phone numbers as an array
   *
   *  @return               the array
   */

  public String[] getPhoneNumbersAsArray ()
  {
    String[] array = new String[phoneNumbers.size ()];
    return phoneNumbers.toArray (array);
  }


  /**
   *  set the phone numbers
   *
   *  @param list               the list of strings to initialize with,
   *                            may be {@code null}
   */

  public void setPhoneNumbers (List<String> list)
  {
    phoneNumbers.clear ();
    if (list != null)
      phoneNumbers.addAll (list);
  }


  /**
   *  set the phone numbers
   *
   *  @param numbers       the phone numbers, may be {@code null}
   */

  public void setPhoneNumbers (String... numbers)
  {
    phoneNumbers.clear ();

    if (numbers != null)
      for (String number: numbers) phoneNumbers.add (number);
  }

  
  

  /**
   *  return the fax numbers as a list of strings
   *
   *  @return                the list (read-only)
   */

  public List<String> getFaxNumbers ()
  {
    return faxNumbers == null ?
      null : Collections.unmodifiableList (faxNumbers);
  }



  /**
   *  return the fax numbers as an array
   *
   *  @return               the array
   */

  public String[] getFaxNumbersAsArray ()
  {
    String[] array = new String[faxNumbers.size ()];
    return faxNumbers.toArray (array);
  }


  /**
   *  set the fax numbers
   *
   *  @param list               the list of strings to initialize with,
   *                            may be {@code null}
   */

  public void setFaxNumbers (List<String> list)
  {
    faxNumbers.clear ();
    if (list != null) faxNumbers.addAll (list);
  }


  /**
   *  set the fax numbers
   *
   *  @param numbers       the fax numbers, may be {@code null}
   */

  public void setFaxNumbers (String... numbers)
  {
    faxNumbers.clear ();
    
    if (numbers != null) 
      for (String number: numbers) faxNumbers.add (number);
    
  }

  

  /**
   *  return the e-mail addresses as a list of strings
   *
   *  @return                the list (read-only)
   */

  public List<String> getEMailAddresses ()
  {
    return Collections.unmodifiableList (emailAddresses);
  }



  /**
   *  return the e-mail addresses as an array
   *
   *  @return               the array
   */

  public String[] getEMailAddressesAsArray ()
  {
    String[] array = new String[emailAddresses.size ()];
    return emailAddresses.toArray (array);
  }


  /**
   *  set the e-mail addresses
   *
   *  @param list               the list of strings to initialize with,
   *                            may be {@code null}
   */

  public void setEMailAddresses (List<String> list)
  {
    emailAddresses.clear ();
    if (list != null) emailAddresses.addAll (list);
  }


  /**
   *  set the e-mail addresses
   *
   *  @param addrs       the e-mail addresses, may be {@code null}
   */

  public void setEMailAddresses (String... addrs)
  {
    emailAddresses.clear ();
    
    if (addrs != null)
      for (String addr: addrs) emailAddresses.add (addr);
  }

  

  /**
   *  return the SIP addresses as a list of strings
   *
   *  @return                the list (read-only)
   */

  public List<String> getSipAddresses ()
  {
    return Collections.unmodifiableList (sipAddresses);
  }



  /**
   *  return the SIP addresses as an array
   *
   *  @return               the array
   */

  public String[] getSipAddressesAsArray ()
  {
    String[] array = new String[sipAddresses.size ()];
    return sipAddresses.toArray (array);
  }


  /**
   *  set the SIP addresses
   *
   *  @param list               the list of strings to initialize with,
   *                            may be {@code null}
   */

  public void setSipAddresses (List<String> list)
  {
    sipAddresses.clear ();
    if (list != null) sipAddresses.addAll (list);
  }


  /**
   *  set the SIP addresses
   *
   *  @param addrs       the SIP addresses, may be {@code null}
   */

  public void setSipAddresses (String... addrs)
  {
    sipAddresses.clear ();
    
    if (addrs != null)
      for (String addr: addrs) sipAddresses.add (addr);
  }

  
  
  /**
   *  return the disclosed fields
   *
   *  @return                the disclosed fields (read-only)
   */
  
  public Set<Field> getDisclosedFields ()
  {
    return Collections.unmodifiableSet (disclose);
  }
  
  
  /**
   *  set the disclosed fields
   *
   *  @param collection    the set of disclosed fields, may be {@code null}
   */
  
  public void setDisclosedFields (Collection<Field> collection)
  {
    disclose = collection == null ? 
      EnumSet.noneOf (Field.class) : EnumSet.copyOf (collection);
  }
  
  

  /**
   *  return the remarks as a list of strings
   *
   *  @return                the list (read-only)
   */

  public List<String> getRemarks ()
  {
    return Collections.unmodifiableList (remarks);
  }



  /**
   *  return the remarks as an array
   *
   *  @return               the array
   */

  public String[] getRemarksAsArray ()
  {
    String[] array = new String[remarks.size ()];
    return remarks.toArray (array);
  }


  /**
   *  set the remarks
   *
   *  @param list               the list of strings to initialize with,
   *                            may be {@code null}
   */

  public void setRemarks (List<String> list)
  {
    remarks.clear ();
    if (list != null) remarks.addAll (list);
  }


  /**
   *  set the remarks
   *
   *  @param lines       the remarks, may be {@code null}
   */

  public void setRemarks (String... lines)
  {
    remarks.clear ();
    
    if (lines != null)
      for (String line: lines) remarks.add (line);
  }


  /**
   *  return whether the contact is referenced as a holder contact by
   *  a domain that is in the dispute state
   *
   *  @return               {@code true} if so
   */

  public boolean getDisputeReference ()
  {
    return disputeReference;
  }
  
  
  /**
   *  return the change date
   *
   *  @return                      the change date, may be {@code null}
   */
  
  public Date getChangeDate ()
  {
    return changed;
  }
  
  
  
  /**
   *  enumeration for the different contact types
   *
   *  @version  $Revision: 14987 $
   *  @author   Klaus Malorny
   */
  
  public static enum Type
  {
    /** person */

    Person ("PERSON"),


    /** role */

    Role ("ROLE"),


    /** organisation */

    Organisation ("ORG");



    /** the DENIC token */
    
    private String denicToken;
    
    
    /** map from the token to the type */
    
    private static final Map<String, Type> tokenMap;
    
    
    static
    {
      tokenMap = new HashMap<String, Type> ();
      
      for (Type t: values ()) 
        tokenMap.put (t.denicToken.toLowerCase (), t);
    }
    
    /**
     *  constructor
     *
     *  @param token               the token that is used in DENIC messages
     *                             for representing the level
     */
    
    private Type (String token)
    {
      denicToken = token;
    }
 
    
    /**
     *  return the DENIC token
     *
     *  @return                     the token
     */
    
    public String getDenicToken ()
    {
      return denicToken;
    }
    
    
    
    /**
     *  return the type for the given token
     *
     *  @param denicToken           the token
     *  @return                     the type, or {@code null} if the
     *                              token is not found
     */
    
    public static Type fromToken (String denicToken)
    {
      return tokenMap.get (denicToken.toLowerCase ());
    }
  }
  
  
  
  

  
  /**
   *  enumeration for the different disclose fields
   *
   *  @version  $Revision: 14987 $
   *  @author   Klaus Malorny
   */
  
  public static enum Field
  {
    /** the contact type */

    Type ("type"),


    /** the name */

    Name ("name"),


    /** the organisation */

    Organisation ("organisation"),


    /** the address */

    Address ("address"),


    /** the city */

    City ("city"),


    /** the postal code */

    PostalCode ("postalcode"),
    
    
    /** the country code */
    
    CountryCode ("countrycode"),


    /** the phone numbers */

    Phone ("phone"),


    /** the fax numbers */

    Fax ("fax"),


    /** the e-mail addresses */

    EMail ("email"),


    /** the SIP addresses */

    Sip ("sip"),


    /** the remarks */

    Remarks ("remarks");

    /** the DENIC token */
    
    private String denicToken;
    
    
    /** map from the token to the type */
    
    private static final Map<String, Field> tokenMap;
    
    
    static
    {
      tokenMap = new HashMap<String, Field> ();
      
      for (Field f: values ()) 
        tokenMap.put (f.denicToken.toLowerCase (), f);
    }
    
    /**
     *  constructor
     *
     *  @param token               the token that is used in DENIC messages
     *                             for representing the level
     */
    
    private Field (String token)
    {
      denicToken = token;
    }
 
    
    /**
     *  return the DENIC token
     *
     *  @return                     the token
     */
    
    public String getDenicToken ()
    {
      return denicToken;
    }
    
    
    
    /**
     *  return the field for the given token
     *
     *  @param denicToken           the token
     *  @return                     the field, or {@code null} if the
     *                              token is not found
     */
    
    public static Field fromToken (String denicToken)
    {
      return tokenMap.get (denicToken.toLowerCase ());
    }
  }

}




