//______________________________________________________________________________
//   |       |
//   | 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.4 (15127), 2011/06/03 14:52:49
//                        $Revision: 14987 $ 
//                        $Date: 2011-01-06 12:03:23 +0100 (Thu, 06 Jan 2011) $
//                        $Author: Klaus.Malorny $
//

package de.knipp.rritoolkit;

import java.util.Map;
import java.util.HashMap;
import java.util.Collection;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Date;
import java.util.IdentityHashMap;

import java.io.Serializable;

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

import de.knipp.rritoolkit.dns.DnsEntry;

import static de.knipp.rritoolkit.DenicConstants.NS_DOMAIN;
import static de.knipp.rritoolkit.DenicConstants.NS_DNS_ENTRY;
import static de.knipp.rritoolkit.DenicConstants.PREFIX_DOMAIN;

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

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



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

public class DomainData implements Serializable
{
  /** the domain name */
  
  private String domainName;
  
  
  /** the encoded domain name */
  
  private String encodedDomainName;
  
  
  /** the domain state */
  
  private DomainState domainState;
  

  /** the provider */
  
  private String provider;


  /** the list of contacts */
  
  private List<Ref> contacts;
  
  
  /** the DNS related data */
  
  private List<DnsEntry> dns;
  
  
  /** the last change date */
  
  private Date changeDate;
  
  
  /** expire date */
  
  private Date expireDate;
  

  /** flag whether domain is in dispute */
  
  private boolean inDispute;


  /** the AuthInfo text for the change provider operation */

  private String changeProvAuthInfo;


  /** the date AuthInfo 1 expires ({@code null} if not set or unknown) */

  private Date authInfo1ExpireDate;


  /** the date AuthInfo 2 expires ({@code null} if not set or unknown) */

  private Date authInfo2ExpireDate;


  /** flag whether either AuthInfo 1 or AuthInfo 2 is set */

  private boolean authInfosSet;


  /** the names of the contact sections */
  
  private static final String[] contactSectionNames = new String[]
    {
      "holder-descr",
      "holder",
      "admin-c",
      "tech-c",
      "zone-c"
    };
  

  /** the types of the contact sections; order must be synchronized with
      {@code contactSectionNames} */
    
  private static final Role[] contactSectionRoles = new Role[]
    { Role.Holder,
      Role.Holder,
      Role.Admin,
      Role.Tech,
      Role.Zone
    };

  /** the "from description" flags for the sections, order must be
      synchronized with {@code contactSectionNames} */
    
  private static final boolean[] contactSectionFromDescr = new boolean[]
    { true,
      false,
      false,
      false,
      false
    };
  
   
    
    
  /**
   *  constructor
   */
  
  public DomainData ()
  {
    contacts = new ArrayList<Ref> ();
    dns = new ArrayList<DnsEntry> ();
  }
  
  

  /**
   *  copy constructor
   *
   *  @param other               the instance to copy from
   *  @param deepCopy            if {@code true}, a deep copy is
   *                             performed, i.e. copies from referenced 
   *                             mutable objects are also created
   */
  
  public DomainData (DomainData other, boolean deepCopy)
  {
    domainName = other.domainName;
    encodedDomainName = other.encodedDomainName;
    domainState = other.domainState;
    provider = other.provider;
    
    if (deepCopy)
    {
      contacts = new ArrayList<Ref> ();
      
      Map<ContactData, ContactData> objMap = 
        new IdentityHashMap<ContactData, ContactData> ();
      
      for (Ref r: other.contacts)
      {
        ContactData contact = r.getContact ();
        ContactData newContact = objMap.get (contact);
        
        if (newContact == null)
        {
          newContact = new ContactData (contact);
          objMap.put (contact, newContact);
        }
        
        contacts.add (
          new Ref (r.getRole (), newContact, r.isFromDescription ()));
      }
 
      dns = new ArrayList<DnsEntry> ();
      for (DnsEntry entry: other.dns) dns.add (entry.clone ());
        
    } else
    {
      contacts = new ArrayList<Ref> (other.contacts);
      dns = new ArrayList<DnsEntry> (other.dns);
    }

    changeDate = other.changeDate;
    expireDate = other.expireDate;

    changeProvAuthInfo = other.changeProvAuthInfo;
    authInfo1ExpireDate = other.authInfo1ExpireDate;
    authInfo2ExpireDate = other.authInfo2ExpireDate;
    authInfosSet = other.authInfosSet;
  
    inDispute = other.inDispute;
  }
  
  
  /**
   *  convert the object to a KV list as part of a domain request
   *  (create, update, chholder, chprov, delete)
   *
   *  @param list                the list
   *  @param addDns              if {@code true}, DNS data is added
   *  @throws EncodeException    on encoding exceptions
   */
  
  public void toList (KVList list, boolean addDns)
    throws EncodeException
  {
    list.set ("domain", domainName);
    if (encodedDomainName != null) list.set ("domain-ace", encodedDomainName);
    
    for (Ref r: contacts)
    {
      Role role = r.getRole ();
      String handle = r.getContact ().getHandle ();
           
      if (handle == null)
        throw new EncodeException ("handle of a referenced contact is not set");
      
      list.add (role.getDenicToken (), handle);
    }
    
    if (addDns) for (DnsEntry entry: dns) entry.toList (list);

    if (changeProvAuthInfo != null) list.set ("authinfo", changeProvAuthInfo);
  }
  

  
  
  /**
   *  parse the given key-value list.
   *
   *  @param list                       the list to parse
   *  @throws ResponseParseException    if the parsing failed
   */
  
  public void fromList (KVMultiList list)
    throws ResponseParseException
  {
    KVList main = list.getMainSection ();
    
    domainName = main.getFirst ("domain");
    encodedDomainName = main.getFirst ("domain-ace");
    
    String temp = main.getFirst ("status");
    
    if (temp == null)
      throw new ResponseParseException ("missing status field", "status", 
        (String) null);
      
    domainState = DomainState.fromToken (temp);
    
    if (domainState == null) 
      throw new ResponseParseException ("invalid status", "status", temp);
    
    provider = main.getFirst ("regaccid");
    
    // contacts as sections
    
    Map<KVList, ContactData> parsedContacts = 
      new HashMap<KVList, ContactData> ();
    
    int count = contactSectionNames.length;
    
    for (int i = 0; i < count; i++)
    {
      for (KVList section: list.getSections (contactSectionNames[i]))
      {
        ContactData data = parsedContacts.get (section);
        
        if (data == null)
        {
          data = new ContactData ();
          data.fromList (section);
          parsedContacts.put (section, data);
        }
        
        contacts.add (new Ref (contactSectionRoles[i], data, 
          contactSectionFromDescr[i]));
      }
    }

    // DNS data
    
    String name = encodedDomainName == null ? domainName : encodedDomainName;

    dns = DnsEntry.allFromList (main, name);

    // date fields
      
    changeDate = parseDateTime (main.getFirst ("changed"));
    expireDate = parseDateTime (main.getFirst ("expire"));
    
    KVList section = list.getFirstSection ("dispute");
    
    if (section != null)
    {
      inDispute = true;
    }

    authInfo1ExpireDate = parseDateTime (main.getFirst ("authinfo1"));
    authInfo2ExpireDate = parseDateTime (main.getFirst ("authinfo2"));

    temp = main.getFirst ("authinfo");
    
    authInfosSet = temp != null && temp.equalsIgnoreCase ("true") ||
      authInfo1ExpireDate != null || authInfo2ExpireDate != null;
  }
  
  /**
   *  convert the object to XML as part of a domain request
   *  (create, update, chholder, chprov, delete)
   *
   *  @param node                the node to add the contents to
   *  @param addDns              if {@code true}, DNS data is added
   *  @throws DOMException       on DOM exceptions
   *  @throws EncodeException    on encode exceptions
   */
  
  public void toXml (Node node, boolean addDns)
    throws DOMException, EncodeException
  {
    XmlBuilder xb = new XmlBuilder (node);

    xb.elt (NS_DOMAIN, PREFIX_DOMAIN, "handle", domainName);
    
    if (encodedDomainName != null) 
      xb.elt (NS_DOMAIN, PREFIX_DOMAIN, "ace", encodedDomainName);
   
    for (Ref r: contacts)
    {
      Role role = r.getRole ();
      String handle = r.getContact ().getHandle ();

      if (handle == null)
        throw new EncodeException ("handle of a referenced contact is not set");
      
      xb.
        es (NS_DOMAIN, PREFIX_DOMAIN, "contact").
        attr ("role", role.getDenicToken ()).
        t (handle).
        ee ();
    }

    String name = encodedDomainName == null ? domainName : encodedDomainName;
    
    if (addDns)
      for (DnsEntry entry: dns)
        entry.toXml (xb.getCurrent (), name);

    if (changeProvAuthInfo != null)
      xb.elt (NS_DOMAIN, PREFIX_DOMAIN, "authInfo", changeProvAuthInfo);
  }
  
  
  /**
   *  parse the given XML subtree
   *
   *  @param node                     the node containing the contact data
   *                                  (infoDataType)
   *  @throws DOMException            on DOM exceptions
   *  @throws ResponseParseException  if the parsing failed
   */
  
  public void fromXml (Element node)
    throws DOMException, ResponseParseException
  {
    domainName = getFirstElementText (node, NS_DOMAIN, "handle");
    encodedDomainName = getFirstElementText (node, NS_DOMAIN, "ace");
    
    String temp = getFirstElementText (node, NS_DOMAIN, "status");
    
    if (temp == null)
      throw new ResponseParseException ("missing status element", node);
      
    domainState = DomainState.fromToken (temp);
    
    if (domainState == null) 
      throw new ResponseParseException ("invalid status element", 
        getFirstElement (node, NS_DOMAIN, "status"));
    
    provider = getFirstElementText (node, NS_DOMAIN, "regAccId");

    changeDate = 
      parseDateTime (getFirstElementText (node, NS_DOMAIN, "changed"));
    
    expireDate = 
      parseDateTime (getFirstElementText (node, NS_DOMAIN, "expire"));
    
    
    for (Element el: getElements (node, NS_DOMAIN, "contact"))
    {
      temp = el.getAttribute ("role");
      if (temp == null) 
        throw new ResponseParseException ("missing role attribute", el);
      
      Role role = Role.fromToken (temp);
      
      if (role == null)
        throw new ResponseParseException ("invalid value", 
          el.getAttributeNode ("role"));
      
      boolean fromDescr = asBoolean (el.getAttribute ("descr"), false);
      
      ContactData cdata = new ContactData ();
      cdata.fromXml (el);
      
      contacts.add (new Ref (role, cdata, fromDescr));
    }
    
    for (Element el: getElements (node, NS_DNS_ENTRY, "dnsentry"))
      dns.add (DnsEntry.fromXml (el));

    Element elDispute = getFirstElement (node, NS_DOMAIN, "dispute");
    
    if (elDispute != null && !isNilled (elDispute))
    {
      inDispute = true;
    }

    authInfo1ExpireDate = parseDateTime (
      getFirstElementText (node, NS_DOMAIN, "authInfo1"));

    authInfo2ExpireDate = parseDateTime (
      getFirstElementText (node, NS_DOMAIN, "authInfo2"));

    authInfosSet = authInfo1ExpireDate != null || authInfo2ExpireDate != null ||
      asBoolean (getFirstElementText (node, NS_DOMAIN, "authInfo"), false);
  }
  
  
  /**
   *  convert the object to a string (for debugging purposes)
   *
   *  @return                        the string representation
   */

  @Override
  public String toString ()
  {
    StringBuilder sb = new StringBuilder ();
    sb.append ("  domain name: ");
    sb.append (domainName);
    sb.append ("\n  encoded domain name: ");
    sb.append (encodedDomainName);
    sb.append ("\n  domain state: ");
    sb.append (domainState);
    sb.append ("\n  provider: ");
    sb.append (provider);
    sb.append ("\n  change date: ");
    sb.append (changeDate);
    sb.append ("\n  expiration date: ");
    sb.append (expireDate);
    sb.append ("\n  dispute flag: ");
    sb.append (inDispute);
    sb.append ("\n  chprov authinfo: ");
    sb.append (changeProvAuthInfo);
    sb.append ("\n  authinfo 1 expiration date: ");
    sb.append (authInfo1ExpireDate);
    sb.append ("\n  authinfo 2 expiration date: ");
    sb.append (authInfo2ExpireDate);
    sb.append ("\n  any authinfos set: ");
    sb.append (authInfosSet);
    sb.append ("\n");
    
    for (Ref r: contacts)
    {
      sb.append ("- contact \"");
      sb.append (r.getRole ());
      sb.append ("\", from description = ");
      sb.append (r.isFromDescription ());
      sb.append ("\n");
      sb.append (r.getContact ());
    }
    
    sb.append ("- DNS entries\n");
    
    for (DnsEntry e: dns)
    {
      sb.append ("  ");
      sb.append (e);
      sb.append ("\n");
    }

    return sb.toString ();
  }
  
  
  //--- access methods ---------------------------------------------------------
  
  /**
   *  return the domain name
   *
   *  @return                    the domain name
   */
  
  public String getDomainName ()
  {
    return domainName;
  }
  
  
  
  /**
   *  return the encoded domain name
   *
   *  @return                    the encoded domain name
   */
  
  public String getEncodedDomainName ()
  {
    return encodedDomainName;
  }
  
  
  
  /**
   *  set the domain name. The encoded domain name is cleared.
   *  This version should be used for non-IDNs only
   *
   *  @param theDomainName             the domain name
   */
  
  public void setDomainName (String theDomainName)
  {
    domainName = theDomainName;
    encodedDomainName = null;
  }
  
  
  
  /**
   *  set the domain name
   *
   *  @param theDomainName             the domain name
   *  @param theEncodedDomainName      the encoded domain name
   */
  
  public void setDomainName (String theDomainName, String theEncodedDomainName)
  {
    domainName = theDomainName;
    encodedDomainName = theEncodedDomainName;
  }
  
  
  
  /**
   *  return the domain state
   *
   *  @return                          the domain state
   */
  
  public DomainState getDomainState ()
  {
    return domainState;
  }
  
  
  /**
   *  return the provider
   *
   *  @return                         the provider (may be {@code null})
   */
  
  public String getProvider ()
  {
    return provider;
  }
  

  /**
   *  return the AuthInfo that shall be used for the change provider operation
   *
   *  @return               the AuthInfo or {@code null} if none is available
   */

  public String getChangeProvAuthInfo ()
  {
    return changeProvAuthInfo;
  }

  

  /**
   *  set the AuthInfo that shall be used for the change provider
   *  operation
   *
   *  @param authInfo              the AuthInfo or {@code null} if none
   *                               is available
   */

  public void setChangeProvAuthInfo (String authInfo)
  {
    changeProvAuthInfo = authInfo;
  }





  /**
   *  return the expire date for the AuthInfo 1
   *
   *  @return               the expire date or {@code null} if it is not set
   *                        or not provided by the registry, i.e. if the
   *                        domain is not managed by the registrar and the
   *                        "with provider" flag has not been set in the request
   */

  public Date getAuthInfo1ExpireDate ()
  {
    return authInfo1ExpireDate;
  }
  
  
  
  /**
   *  return the expire date for the AuthInfo 2
   *
   *  @return               the expire date or {@code null} if it is not set
   *                        or not provided by the registry, i.e. if the
   *                        domain is not managed by the registrar and the
   *                        "with provider" flag has not been set in the request
   */

  public Date getAuthInfo2ExpireDate ()
  {
    return authInfo2ExpireDate;
  }


  /**
   *  return whether any of the two AutInfos are valid; if the extended
   *  info response (own domain or "with provider" flag) is retrieved,
   *  which does not contain the flag, the value is set to {@code true}
   *  if at least one of the two expire dates is given.
   *
   *  @return               {@code true} if the domain has valid AuthInfos
   */

  public boolean hasAnyAuthInfos ()
  {
    return authInfosSet;
  }



  /**
   *  returns the list of contact references
   *
   *  @return                    the list
   */
  
  public List<Ref> getContactRefs ()
  {
    return new ArrayList<Ref> (contacts);
  }
  
  
  
  /**
   *  return a list of contacts with the given role
   *
   *  @param role                the role
   *  @return                    the list
   */
  
  public List<ContactData> getContacts (Role role)
  {
    List<ContactData> list = new ArrayList<ContactData> ();
    
    for (Ref r: contacts)
      if (r.getRole () == role) list.add (r.getContact ());
    
    return list;
  }
  
  
  
  /**
   *  return the first contact of the given role
   *
   *  @param role                the role
   *  @return                    the first contact or {@code null} if
   *                             there are no contacts of that role
   */
  
  public ContactData getFirstContact (Role role)
  {
    for (Ref r: contacts)
      if (r.getRole () == role) return r.getContact ();
    
    return null;
  }
  
  
  /**
   *  clear all contact references
   */
  
  public void clearContacts ()
  {
    contacts.clear ();
  }
  
  
  
  /**
   *  clear all contact references with the given role
   *
   *  @param role                the role to clear
   */
  
  public void clearContacts (Role role)
  {
    Iterator<Ref> i = contacts.iterator ();
    
    while (i.hasNext ())
    { 
      if (i.next ().getRole () == role) i.remove ();
    }
  }
  
  
  
  /**
   *  add a single contact
   *
   *  @param role                the role
   *  @param contact             the contact
   */
  
  public void addContact (Role role, ContactData contact)
  {
    contacts.add (new Ref (role, contact));
  }
  
  
  /**
   *  add all given contacts
   *
   *  @param role                the role
   *  @param theContacts         the contacts
   */
  
  public void addContacts (Role role, ContactData... theContacts)
  {
    for (ContactData contact: theContacts)
      contacts.add (new Ref (role, contact));
  }
  
  
  
  /**
   *  add a single contact
   *
   *  @param ref                 the reference
   */
  
  public void addContact (Ref ref)
  {
    contacts.add (ref);
  }
  
  
  /**
   *  add all given contacts
   *
   *  @param refs                the references
   */
  
  public void addContacts (Ref... refs)
  {
    for (Ref r: refs) contacts.add (r);
  }
  
  
  
  /**
   *  add all given contacts
   *
   *  @param refs                the references
   */
  
  public void addContacts (Collection<Ref> refs)
  {
    contacts.addAll (refs);
  }
  
  
  
  
  /**
   *  return the DNS entries
   *
   *  @return                    the entries
   */
  
  public List<DnsEntry> getDnsEntries ()
  {
    return new ArrayList<DnsEntry> (dns);
  }
  
  
  
  /**
   *  clear all DNS entries
   */
  
  public void clearDnsEntries ()
  {
    dns.clear ();
  }

  
  
  /**
   *  add an entry to the DNS configuration
   *
   *  @param entry                     the entry to add
   */
  
  public void addDnsEntry (DnsEntry entry)
  {
    dns.add (entry);
  }
  
  
  
  /**
   *  add all entries to the DNS configuration
   *
   *  @param entries                   the entries to add
   */
  
  public void addDnsEntries (DnsEntry... entries)
  {
    for (DnsEntry entry: entries) dns.add (entry);
  }
  
  
  /**
   *  add all entries to the DNS configuration
   *
   *  @param entries                   the entries
   */
  
  public void addDnsEntries (Collection<? extends DnsEntry> entries)
  {
    dns.addAll (entries);
  }
  

 
  /**
   *  return the expiration date
   *
   *  @return               the expiration date (for non-delegated domains) or 
   *                        {@code null} if the domain does not expire
   */
  
  public Date getExpireDate ()
  {
    return expireDate;
  }
  
  
  
  
  /**
   *  return the change date
   *
   *  @return                      the change date, may be {@code null}
   */
  
  public Date getChangeDate ()
  {
    return changeDate;
  }
  
  
  
  
  
  
  /**
   *  enumeration for the different states of the domain
   *
   *  @version  $Revision: 14987 $
   *  @author   Klaus Malorny
   */
  
  public static enum DomainState
  {
    /** free */

    Free ("free"),


    /** connected, i.e. normally registered */

    Connect ("connect"),


    /** registered, but on hold due to failed DNS checks */

    Failed ("failed"),


    /** invalid */

    Invalid ("invalid");

    
    /** the DENIC token */
    
    private String denicToken;
    
    
    /** map from the token to the type */
    
    private static final Map<String, DomainState> tokenMap;
    
    
    static
    {
      tokenMap = new HashMap<String, DomainState> ();
      
      for (DomainState ds: values ()) 
        tokenMap.put (ds.denicToken.toLowerCase (), ds);
    }
    
    /**
     *  constructor
     *
     *  @param token               the token that is used in DENIC messages
     *                             for representing the level
     */
    
    private DomainState (String token)
    {
      denicToken = token;
    }
 
    
    /**
     *  return the DENIC token
     *
     *  @return                     the token
     */
    
    public String getDenicToken ()
    {
      return denicToken;
    }
    
    
    
    /**
     *  return the state for the given token
     *
     *  @param denicToken           the token
     *  @return                     the state, or {@code null} if the
     *                              token is not found
     */
    
    public static DomainState fromToken (String denicToken)
    {
      return tokenMap.get (denicToken.toLowerCase ());
    }
  }
  
  
  
  /**
   *  enumeration for the different contact roles within the domain
   *
   *  @version  $Revision: 14987 $
   *  @author   Klaus Malorny
   */
  
  public static enum Role
  {
    /** holder */

    Holder ("holder"),


    /** administrative contact */

    Admin ("admin-c"),


    /** technical contact */
    
    Tech ("tech-c"),


    /** zone contact */

    Zone ("zone-c");



    /** the DENIC token */
    
    private String denicToken;
    
    
    /** map from the token to the type */
    
    private static final Map<String, Role> tokenMap;
    
    
    static
    {
      tokenMap = new HashMap<String, Role> ();
      
      for (Role r: values ()) 
        tokenMap.put (r.denicToken.toLowerCase (), r);
    }
    
    /**
     *  constructor
     *
     *  @param token               the token that is used in DENIC messages
     *                             for representing the level
     */
    
    private Role (String token)
    {
      denicToken = token;
    }
 
    
    /**
     *  return the DENIC token
     *
     *  @return                     the token
     */
    
    public String getDenicToken ()
    {
      return denicToken;
    }
    
    
    
    /**
     *  return the role for the given token
     *
     *  @param denicToken           the token
     *  @return                     the role, or {@code null} if the
     *                              token is not found
     */
    
    public static Role fromToken (String denicToken)
    {
      return tokenMap.get (denicToken.toLowerCase ());
    }
  }
  
  
  
  /**
   *  class that represents a reference to a contact.
   *
   *  @version  $Revision: 14987 $
   *  @author   Klaus Malorny
   */
  
  public static class Ref implements Serializable
  {
    /** the role */
    
    private Role role;
    
    
    /** the contact itself */
    
    private ContactData contact;
  
    
    /** flag whether the contact was derived from a description field */
    
    private boolean fromDescription;
    
    
    /**
     *  constructor
     *
     *  @param theRole                 the role
     *  @param theContact              the contact
     *  @param isFromDescr             whether the contact is derived from
     *                                 a domain description field
     */
    
    public Ref (Role theRole, ContactData theContact, boolean isFromDescr)
    {
      role = theRole;
      contact = theContact;
      fromDescription = isFromDescr;
    }

    
    
    /**
     *  constructor
     *
     *  @param theRole                 the role
     *  @param theContact              the contact
     */
    
    public Ref (Role theRole, ContactData theContact)
    {
      this (theRole, theContact, false);
    }
    
    
    /**
     *  return the role
     *
     *  @return                         the role
     */
    
    public Role getRole ()
    {
      return role;
    }
    
    
    
    /**
     *  return the contact
     *
     *  @return                       the contact
     */
    
    public ContactData getContact ()
    {
      return contact;
    }
    
    
    
    /**
     *  return whether the contact data has been derived from a
     *  domain description field
     * 
     *  @return                  {@code true} if so
     */
    
    public boolean isFromDescription ()
    {
      return fromDescription;
    }
    
    
  }
  
 
}


