//Soluzione compito D
//Nota: soluzione adattata dal compito svolto da uno degli studenti che hanno
//preso 30 e lode. 


//File EsprBooleana.java

package esprbooleane;

public abstract class EsprBooleana implements Cloneable {  
  public boolean equals (Object o) {
    if (o!=null && getClass().equals(o.getClass())) {
      return true;
    } else return false;
  }
  public int hashCode() { //non e' usato ma in ogni caso rispettiamo il
                          //vincolo imposto da equals
    return getClass().hashCode();
  }
  public Object clone() {
    try {
      return super.clone();
    } catch (CloneNotSupportedException e) {
      throw new InternalError (e.toString());
    }
  }
}

//File Costante.java

package esprbooleane;

public class Costante extends EsprBooleana {
  private boolean valore;

  public Costante (boolean v) {
    valore = v;
  }
  public boolean getValore() {
    return valore;
  }
  public boolean equals(Object o) {
    if (super.equals(o)) {
      Costante c = (Costante)o;
      return c.valore==valore;
    } else return false;
  }
  public int hashCode() {
    if (valore==true) return 1;
    else return 0;
  }
  public Object clone() {
    //Abbiamo in questo caso che la copia profonda ha costo O(1). Si ha
    //inoltre che sono inutili operazioni aggiuntive 
    return super.clone();
  }
  public String toString() {
    return "" + valore;
  }
}


//File Variabile.java
package esprbooleane;

public class Variabile extends EsprBooleana {  
  private String nome;
  public Variabile (String n) {
    nome=n;
  }
  public String getNome() {
    return this.nome;
  }
  public boolean equals(Object o) {
    if (super.equals(o)) {
      Variabile v=(Variabile) o;
      return v.nome.equals(nome);
    } else return false;
  }
  public int hashCode() {
    return nome.hashCode();
  }
  public Object clone() {
    //Abbiamo in questo caso che la copia profonda ha costo O(1) costante.
    //Si ha inoltre che non sono necessarie operazioni aggiuntive poichè
    //String è un oggetto immutabile
    return super.clone();
  }
  public String toString() {
    return nome;
  }
}

//File And.java

package esprbooleane;

public class And extends EsprBooleana {
  private EsprBooleana op1;
  private EsprBooleana op2;
  public And (EsprBooleana op1,EsprBooleana op2) {
    this.op1=op1;
    this.op2=op2;
  }
  public EsprBooleana getOp1() {
    return op1;
  }
  public EsprBooleana getOp2() {
    return op2;
  }
  public boolean equals(Object o) {
    if (super.equals(o)) {
      And a = (And)o;
      return (a.op1.equals(op1)) && (a.op2.equals(op2));
    } 
    else return false;
  }
  public int hashCode() {
    return op1.hashCode() + 31*op2.hashCode();
  }
  public Object clone() {
    //La copia profonda ha in questo caso costo O(n) dove n è il
    //numero di è il numero di espressioni booleane. Infatti ogni espressione
    //booleana  viene "letta" una sola volta. Possiamo infatti assimilare
    //L'espressione Booleana And ad un albero che viene visitato in postOrdine,
    //cioè prima vengono visitati i rami e poi si resistituisce il risultato
    //si noti come l'albero non sia binario poichè possiamo avere come espressioni
    //booleane degli Or n-ari
    And a = (And)super.clone();
    a.op1=(EsprBooleana) op1.clone();
    a.op2=(EsprBooleana) op2.clone();
    return a;
  }
  public String toString() {
    return "(" + op1.toString() + " && " + op2.toString() +")";
  }
}


//File Or.java
package esprbooleane;

import java.util.*;
public class Or extends EsprBooleana {  
  protected List operandi; 

  public Or (List op) {
    //Controlla che gli operandi siano almeno due e che siano
    //effettivamente EsprBooleane
    if (op.size() < 2) 
      throw new RuntimeException("espressione illegale");  
    Iterator it=op.iterator();
    operandi=new LinkedList();
    while (it.hasNext()) {
      Object e =it.next();
      if (e instanceof EsprBooleana) operandi.add(e);
      else throw new RuntimeException("espressione illegale"); 
    }
  }
  
  //Era possibile anche introdurre un costruttore a zero argomenti e
  //opprotuni  metodi per aggiungere ed eliminare il numero degli operandi

  public Iterator iterator() {
    return new OrIterator(this);
  }
  public boolean equals(Object o) {
    if (super.equals(o)) {
      Or r=(Or)o;
      Iterator it1=iterator();
      Iterator it2=r.iterator();
      while (it1.hasNext() && it2.hasNext()) {
        if (!it1.next().equals(it2.next())) return false;
      }
      if (it1.hasNext() || it2.hasNext()) return false;
      else return true;
    } 
    else return false;
  }
  public int hashCode() {
    Iterator it=iterator();
    int res=0;
    while (it.hasNext()) {
      res+=31*res + it.next().hashCode();
    }
    return res;
  }
  public String toString() {
    String res="(";
    Iterator it=iterator();
    res = res + it.next().toString(); //primo operando
    while (it.hasNext()) {  //altri operandi
        res = res + " || " + it.next().toString();
    }
    return res + ")";
  }
  public Object clone() {
    //In maniera equivalente alla copia profonda della classe And il
    //costo è O(n) dove n è il numero di espressioni booleane. Si
    //possono fare le stesse considerazioni riguardo la struttura dati
    //assimilabile ad un albero n-ario in cui ciascuno dei nodi (le
    //espressioni booleane) viene visitato una sola volta
    Or r=(Or) super.clone();
    List nuova=new LinkedList();
    Iterator it =iterator();
    while (it.hasNext()) {
      nuova.add(((EsprBooleana)it.next()).clone());
    }
    r.operandi=nuova;
    return r;
  }
}

//File OrIterator.java
package esprbooleane;
import java.util.*;
class OrIterator implements Iterator{
  private Iterator it;
  
  public OrIterator (Or o) {
    it = o.operandi.iterator();
  }
  public Object next () {
    return it.next();
  }
  
  public boolean hasNext() {
    return it.hasNext();
  }
  
  public void remove () {
    throw new UnsupportedOperationException(("remove non e' supportata");
  }
}



//File ServiziEsprBooleane.java

package serviziesprbooleane;
import java.util.*;
import esprbooleane.*;

public class ServiziEsprBooleane {
  public static boolean valuta (EsprBooleana e, Map a) {
    if (e instanceof Variabile) {
      Variabile aux = (Variabile)e;
      return ((Boolean)a.get(aux.getVar())).booleanValue();
    } 
    else if (e instanceof Costante) {
      Costante aux = (Costante)e;
      return aux.getValore();
    } 
    else if (e instanceof And) {
      And aux = (And)e;
      return valuta(aux.getOp1(),a) && valuta(aux.getOp2(),a);
    } 
    else {
      Or aux = (Or) e;
      boolean res = false;
      Iterator it = aux.iterator();
      while (it.hasNext()) {
        EsprBooleana op = (EsprBooleana)it.next();
        res = res || valuta(op,a);
      }
      return res;
    }
  }
}


