// File InsiemeSS.java

public class InsiemeSS implements Cloneable {
  // funzioni proprie del tipo astratto
  public InsiemeSS(Class cl) {
    // costruttore, realizza la funzione InsVuoto del tipo astratto Insieme
    inizio = null;
    cardinalita = 0;
    elemClass = cl;
  }
  public boolean estVuoto() {
    return inizio == null;
  }
  public boolean membro(Object e) {
    if (!elemClass.isInstance(e)) return false;
    else return appartiene(e,inizio);
  }
  public void inserisci(Object e) {
    if (!elemClass.isInstance(e)) return;
    else if (appartiene(e,inizio)) return;
    else {
      Lista l = new Lista();
      l.info = e;
      l.next = inizio;
      inizio = l;
      cardinalita = cardinalita + 1;
    }
  }
  public void elimina(Object e) {
    if (!appartiene(e,inizio)) return;
    else {
      inizio = cancella(e,inizio);
      cardinalita = cardinalita - 1;
    }
  }
  public Object scegli() {
    if (inizio == null) return null;
    else return inizio.info;
  }
  public int cardinalita() {
    return cardinalita;
  }
  // funzioni speciali ereditate da Object
  public boolean equals(Object o) {
    if (o != null && getClass().equals(o.getClass())) {
      InsiemeSS ins = (InsiemeSS)o;
      if (!elemClass.equals(ins.elemClass)) return false;
      // ins non e' un insieme del tipo voluto
      else if (cardinalita != ins.cardinalita) return false;
      // ins non ha la cardinalita' giusta
      else {
        // verifica che gli elementi nella lista siano gli stessi
        Lista l = inizio;
        while (l != null) {
          if (!appartiene(l.info,ins.inizio))
            return false;
          l = l.next;
        }
        return true;
      }
    }
    else return false;
  }
  public Object clone() {
    try {
      InsiemeSS ins = (InsiemeSS) super.clone();
      // chiamata a clone() di Object che esegue la copia campo a campo;
      // questa copia e' sufficiente per i campi cardinalita e elemClass
      // ma non per il campo inizio del quale va fatta una copia profonda
      ins.inizio = copia(inizio);
      return ins;
    } catch(CloneNotSupportedException e) {
      // non puo' accadere perche' implementiamo l'interfaccia cloneable,
      // ma va comunque gestita
      throw new InternalError(e.toString());
    }
  }
  public String toString() {
    String s = "{";
    Lista l = inizio;
    while (l != null) {
      s = s + l.info + " ";
      l = l.next;
    }
    s = s + "}";
    return s;
  }

  // campi dati
  protected static class Lista {
    Object info;
    Lista next;
  }
  protected Lista inizio;
  protected int cardinalita;
  protected Class elemClass;

  // funzioni ausiliarie

  protected static boolean appartiene(Object e, Lista l){
    return (l != null) && (l.info.equals(e) || appartiene(e,l.next));
  }
  protected static Lista copia (Lista l) {
    if (l == null) return null;
    else {
      Lista ll = new Lista();
      ll.info = l.info;
      ll.next = copia(l.next);
      return ll;
    }
  }
  protected static Lista cancella(Object e, Lista l) {
    if (l == null) return null;
    else  if (l.info.equals(e)) return l.next;
    else {
      l.next = cancella(e,l.next);
      return l;
    }
  }
}
