package yacs.domain;

import java.io.*;

/**
 * <p>Abstrakte Klasse zur Repräsentation von einzelnen Werten einer Domäne
 * einer Constraint-Variable. Implementierungen dieser Klasse können z.B.
 * Elemente für numerische finite Domänen, symbolische finite Domänen oder
 * reellwertige Intervall-Domänen sein.</p>
 *
 * <p>Copyright (C) 2005  Wolfgang Runte
 * <br><br>
 * 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.
 * <br><br>
 * 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.
 * <br><br>
 * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
 * USA</p>
 *
 * @author Wolfgang Runte (woru@tzi.org)
 * @version YACS 0.1.1
 */
public abstract class DomainElement implements Comparable, Cloneable, Serializable {

// ********** private attributs **********

// ********** public attributs **********

// ********** private methods **********

// ********** public methods **********

  /**
   * Liefert den Wert des Elements.
   * @return Object
   */
  public abstract Object getValue();

  /**
   * Methode zur Addition zweier Elemente.
   * @param param DomainElement
   * @return DomainElement
   */
  public abstract DomainElement plus(DomainElement param);

  /**
   * Methode zur Subtraktion zweier Elemente.
   * @param param DomainElement
   * @return DomainElement
   */
  public abstract DomainElement minus(DomainElement param);

  /**
   * Methode zur Multiplikation zweier Elemente.
   * @param param DomainElement
   * @return DomainElement
   */
  public abstract DomainElement times(DomainElement param);

  /**
   * Methode zur Division zweier Elemente.
   * @param param DomainElement
   * @return DomainElement
   */
  public abstract DomainElement divide(DomainElement param);

  /**
   * Liefert das Element mit negativem Vorzeichen.
   * @return DomainElement
   */
  public abstract DomainElement uminus();

  /**
   * Liefert <code>true</code>, wenn das Element gleich dem übergebenen Element
   * ist, <code>false</code> wenn nicht.
   * @param param DomainElement
   * @return boolean
   */
   public abstract boolean equals(DomainElement param);

  /**
   * Liefert <code>true</code>, wenn das Element ungleich dem übergebenen
   * Element ist, <code>false</code> wenn nicht.
   * @param param DomainElement
   * @return boolean
   */
  public abstract boolean notEquals(DomainElement param);

  /**
   * Liefert <code>true</code>, wenn das Element größer als das übergebenen
   * Element ist, <code>false</code> wenn nicht.
   * @param param DomainElement
   * @return boolean
   */
  public abstract boolean greaterAs(DomainElement param);

  /**
   * Liefert <code>true</code>, wenn das Element kleiner als das übergebenen
   * Element ist, <code>false</code> wenn nicht.
   * @param param DomainElement
   * @return boolean
   */
  public abstract boolean lowerAs(DomainElement param);

  /**
   * Liefert <code>true</code>, wenn das Element größer/gleich dem übergebenen
   * Element ist, <code>false</code> wenn nicht.
   * @param param DomainElement
   * @return boolean
   */
  public abstract boolean greaterEqualAs(DomainElement param);

  /**
   * Liefert <code>true</code>, wenn das Element kleiner/gleich dem übergebenen
   * Element ist, <code>false</code> wenn nicht.
   * @param param DomainElement
   * @return boolean
   */
  public abstract boolean lowerEqualAs(DomainElement param);

  /**
   * Liefert die Belegung dieses Elements als <code>String</code>.
   * @return String
   */
  public abstract String toString();

  /**
   * Bietet die Möglichkeit Instanzen dieser Klasse innerhalb einer
   * <code>TreeMap</code> sortieren zu lassen. Hierfür ist außerdem
   * erforderlich, dass diese Klasse das Interface <code>Comparable</code>
   * implementiert.
   * @param param Object
   * @return int
   */
  public int compareTo(Object param) {
    if (this.greaterAs((DomainElement)param)) {
      return 1;
    } else if (this.lowerAs((DomainElement)param)) {
      return -1;
    }
    return 0;
  }

  /**
   * Liefert eine Kopie dieses Objekts. Allerdings nur eine "shallow copy", d.h.
   * die Felder des Objektes werden nicht geklont.
   * @return Object
   */
  public Object clone() {
    try {
      return super.clone();
    }
    catch (CloneNotSupportedException e)
    {
      System.err.println("CloneNotSupportedException in DomainElement.clone()");
      e.printStackTrace();
    }
    catch (OutOfMemoryError e)
    {
      System.err.println("OutOfMemoryError in DomainElement.clone()");
      e.printStackTrace();
    }
    return null;
  }

  /**
   * Klont <code>this</code> und rekursiv alle darin referenzierten Objekte mit
   * (die Selbheit indirekt mehrfach referenzierter Objekte bleibt erhalten).
   * @return DomainElement
   */
  public DomainElement cloneDeep() {
   try {
     ByteArrayOutputStream baos = new ByteArrayOutputStream();
     ObjectOutputStream oos = new ObjectOutputStream(baos);
     oos.writeObject(this); oos.flush();
     ObjectInputStream ois = new ObjectInputStream(
       new ByteArrayInputStream(baos.toByteArray())
     );
     // Klont durch Deserialisierung:
     return (DomainElement)ois.readObject();
   }
   catch (Exception e)
   {
     System.err.println("Exception in DomainElement.cloneDeep()");
     e.printStackTrace();
   }
   return null;
 }

}
