package javabeansexample;

import java.io.*;
import java.util.*;
import java.beans.*;

class MyGregCalendar extends GregorianCalendar {
    public MyGregCalendar() {
    }

    public MyGregCalendar(int y, int m, int d) {
        super(y, m - 1, d);
    }

    public String toString() {
        return get(Calendar.DAY_OF_MONTH) + "-" + (get(Calendar.MONTH) + 1) + "-" + get(Calendar.YEAR);
    }
}

/**
 * klasa ziarna którą można serializować
 */
class Ziarno implements Serializable {
    /**
     * wsparcie dla nasłuchu zmian
     */
    private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
    /**
     * wsparcie dla wetowania zmian
     */
    private VetoableChangeSupport vcs = new VetoableChangeSupport(this);

    /**
     * konstruktor bezparametrowy
     */
    public Ziarno() {
    }

    /**
     * przyłączanie i odłączanie słuchaczy zdarzeń związanych ze zmianami właściwości
     */
    public synchronized void addPropertyChangeListener(PropertyChangeListener lst) {
        pcs.addPropertyChangeListener(lst);
    }

    public synchronized void removePropertyChangeListener(PropertyChangeListener lst) {
        pcs.removePropertyChangeListener(lst);
    }

    /**
     * przyłączanie i odłączanie słuchaczy zdarzeń związanych z wetowaniem zmian właściwości
     */
    public synchronized void addVetoableChangeListener(VetoableChangeListener lst) {
        vcs.addVetoableChangeListener(lst);
    }

    public synchronized void removeVetoableChangeListener(VetoableChangeListener lst) {
        vcs.removeVetoableChangeListener(lst);
    }

    /**
     * właściwość powiązana
     */
    protected String tekst = "teraz jest ";

    /**
     * getter dla właściwości tekst
     */
    public synchronized String getTekst() {
        return tekst;
    }

    /**
     * setter dla właściwości tekst
     */
    public synchronized void setTekst(String nowyTekst) {
        String staryTekst = tekst;
        tekst = nowyTekst;
        pcs.firePropertyChange(new PropertyChangeEvent(this, "tekst", staryTekst, nowyTekst));
    }

    /**
     * właściwość ograniczana
     */
    protected MyGregCalendar data = new MyGregCalendar();

    /**
     * getter dla właściwości data
     */
    public synchronized MyGregCalendar getData() {
        return data;
    }

    /**
     * setter dla właściwości data
     */
    public synchronized void setData(MyGregCalendar nowaData) throws PropertyVetoException {
        MyGregCalendar staraData = data;
        vcs.fireVetoableChange("data", staraData, nowaData);
        data = nowaData;
        pcs.firePropertyChange("data", staraData, nowaData);
    }

    /**
     * prezentacja obiektu ziarna
     */
    public String toString() {
        return tekst + data;
    }
}

/**
 * decydent w sprawach zmiany daty w obiekcie Ziarno
 */
class DatownikDolny implements VetoableChangeListener, Serializable {
    protected MyGregCalendar granica;

    public DatownikDolny() {
        granica = new MyGregCalendar();
    }

    public DatownikDolny(MyGregCalendar d) {
        if (d == null) throw new NullPointerException();
        granica = d;
    }

    public void vetoableChange(PropertyChangeEvent ev) throws PropertyVetoException {
        if (!ev.getPropertyName().equals("data")) return;
        MyGregCalendar d = (MyGregCalendar) ev.getNewValue();
        if (d.compareTo(granica) < 0) {
            System.out.println("za wcześnie");
            throw new PropertyVetoException("za wcześnie", ev);
        }
    }
}

/**
 * decydent w sprawach zmiany daty w obiekcie Ziarno
 */
class DatownikGorny implements VetoableChangeListener, Serializable {
    protected MyGregCalendar granica;

    public DatownikGorny() {
        granica = new MyGregCalendar();
    }

    public DatownikGorny(MyGregCalendar d) {
        if (d == null) throw new NullPointerException();
        granica = d;
    }

    public void vetoableChange(PropertyChangeEvent ev) throws PropertyVetoException {
        if (!ev.getPropertyName().equals("data")) return;
        MyGregCalendar d = (MyGregCalendar) ev.getNewValue();
        if (d.compareTo(granica) > 0) {
            System.out.println("za późno");
            throw new PropertyVetoException("za późno", ev);
        }
    }
}

/**
 * obserwator zmian tekstu i daty w obiekcie Ziarno
 */
class Komentator implements PropertyChangeListener, Serializable {
    protected int granica = 15;

    public void propertyChange(PropertyChangeEvent ev) {
        System.out.println("zmiana właściwości " + ev.getPropertyName() + ": " + ev.getNewValue());
    }
}

public class TestJavaBeans extends Object {
    public static void main(String[] args) {
        Ziarno z = new Ziarno();
        DatownikDolny dolny = new DatownikDolny(new MyGregCalendar(2000, 1, 1));
        DatownikGorny gorny = new DatownikGorny(new MyGregCalendar(2022, 12, 31));
        Komentator koment = new Komentator();
        z.addVetoableChangeListener(dolny);
        z.addVetoableChangeListener(gorny);
        z.addPropertyChangeListener(koment);

        System.out.println(z);
        z.setTekst("dziś mamy ");
        try {
            MyGregCalendar d = new MyGregCalendar(2022, 3, 4);
            System.out.println("próba 1: " + d);
            z.setData(d);
        } catch (Exception ex) {
            System.out.println("zawetowanie daty 1");
        }
        System.out.println(z);
        try {
            MyGregCalendar d = new MyGregCalendar(1990, 12, 6);
            System.out.println("próba 2: " + d);
            z.setData(d);
        } catch (Exception ex) {
            System.out.println("zawetowanie daty 2");
        }
        System.out.println(z);
        try {
            MyGregCalendar d = new MyGregCalendar(2024, 6, 30);
            System.out.println("próba 3: " + d);
            z.setData(d);
        } catch (Exception ex) {
            System.out.println("zawetowanie daty 3");
        }
        System.out.println(z);
    }
}
