package yacs.parser;

import yacs.domain.*;

import java.util.*;
import java.io.*;

/**
 * <p>Abstrakte Klasse für eine rekursive Struktur zur Repräsentation eines
 * algebraischen Constraint-Problems. Anm.: Diese Klasse bzw. die erbenden
 * Klassen arbeiten in Bezug auf die Variablen "stringbasiert" noch (anhand der
 * Variablennamen). Dies sollte dahingehend geändert werden, dass die
 * entsprechenden Objekte der Klasse <code>Variable</code> direkt verwendet
 * werden.</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 Expression implements Cloneable, Serializable {

  /**
   * Belegt den Wertebereich der angegebenen Variable und gibt zurück, ob dies
   * erfolgreich war (<code>true</code>) oder nicht (<code>false</code>).
   * @param variableName String
   * @param domain Domain
   * @return boolean
   */
  public abstract boolean setDomain(String variableName, Domain domain);

  /**
   * Liefert die Domäne der übergebenen Variable.
   * @param variableName String
   * @return Domain
   */
  public abstract Domain getDomain(String variableName);

  /**
   * Liefert eine Liste der Variablennamen des Constraint-Problems.
   * @return List
   */
  public abstract List getVariables();

  /**
   * Liefert eine Liste mit den Namen der noch unbelegten Variablen.
   * @return List
   */
  public abstract List getUnsetVariables();

 /**
  * Gibt zurück ob unbelegte Variablen existieren (<code>true</code>) oder nicht
  * (<code>false</code>).
  * @return boolean
  */
  public abstract boolean unsetVariablesLeft();

  /**
   * Belegt die übergebene Variable mit einem Wert. War die Wertebelegung
   * erfolgreich, wird <code>true</code> zurückgegeben, ansonsten
   * <code>false</code>.
   * @param variableName String
   * @param element DomainElement
   * @return boolean
   */
  public abstract boolean set(String variableName, DomainElement element);

  /**
   * Liefert die Wertebelegung der übergebenen Variable.
   * @param variableName String
   * @return DomainElement
   */
  public abstract DomainElement get(String variableName);

  /**
   * Prüft die Erfüllbarkeit des Constraint-Ausdrucks bzgl. der aktuellen
   * Wertebelegung.
   * @return boolean
   */
  public abstract boolean satisfiable();

  /**
   * Wertet den Constraint-Ausdruck aus. Liefert als Ergebnis die Belegung einer
   * Variable, den Wert einer Konstante oder das Ergebnis einer Berechnung.
   * @return DomainElement
   */
  public abstract DomainElement evaluate();

  /**
   * Liefert die einzelnen "Primärausdrücke" des Constraint-Problems innerhalb
   * einer Liste (enthält Instanzen des Interface <code>Expression</code>).
   * @return List
   */
  public abstract List getPrimitiveExpressions();

  /**
   * Liefert die Referenz auf die Variable mit dem übergebenen Namen.
   * @param variableName String
   * @return Variable
   */
  public abstract Variable getVariableReference(String variableName);

  /**
   * Liefert <code>true</code>, wenn der Expression die Variable mit dem
   * übergebenen Namen enthält. Ansonsten wird <code>false</code> zurückgegeben.
   * @param variableName String
   * @return boolean
   */
  public abstract boolean containsVariable(String variableName);

  /**
   * Liefert <code>true</code>, wenn die Domäne einer Variable leer ist.
   * In diesem Fall ist keine Konsistenz bzw. Lösung herstellbar.
   * Ansonsten wird <code>false</code> zurückgegeben.
   * @return boolean
   */
  public abstract boolean hasFalseDomain();

  /**
   * Liefert <code>true</code>, wenn eine "valuation domain" vorliegt, d.h. in
   * den Domänen aller Variablen ist jeweils genau ein Wert enthalten. Ansonsten
   * wird <code>false</code> zurückgegeben.
   * @return boolean
   */
  public abstract boolean hasValuationDomain();

  /**
   * Wenn eine "valuation domain" vorliegt, werden die Variablen mit dem in der
   * jeweiligen Domäne einzig vorhandenen Wert belegt. Liefert
   * <code>true</code>, wenn das Ergebnis positiv ist, d.h. die Werte einer
   * "valuation domain" konnten alle gesetzt werden. Ansonsten wird
   * <code>false</code> zurückgegeben.
   * @return boolean
   */
  public abstract boolean setValuationDomain();

   /**
    * Entfernt bei allen Variablen, die mit einem Wert belegt sind, sämtliche
    * anderen Elemente aus deren Domäne. Variablen, die nicht mit einem Wert
    * belegt sind, bleiben unverändert.
    */
   public abstract void singletons();

   /**
    * Liefert den "Vernetzungsgrad" einer Variablen innerhalb des
    * Constraint-Problems, d.h. dem Grad ihres Vorkommens in den Constraints.
    * @param variableName String
    * @return int
    */
   public abstract int count(String variableName);

  /**
   * Initialisiert die Constraint-Variablen. Es werden alle vorhandenen
   * Belegungen entfernt.
   */
  public abstract void init();

  /**
   * Konvertiert das Objekt in einen String.
   * @return String
   */
  public abstract String toString();

  /**
   * 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 Expression.clone()");
      e.printStackTrace();
    }
    catch (OutOfMemoryError e)
    {
      System.err.println("OutOfMemoryError in Expression.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 Expression
   */
  public Expression 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 (Expression)ois.readObject();
    }
    catch (Exception e)
    {
      System.err.println("Exception in Expression.cloneDeep()");
      e.printStackTrace();
    }
    return null;
  }

}
