// 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 () {}
    /** konstruktor z parametrem (data) */
    public Ziarno (MyGregCalendar d) { data = d; }

    /** 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ść nieograniczana */
    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
{
    public void propertyChange (PropertyChangeEvent ev)
    {
        System.out.println("zmiana właściwości "+ev.getPropertyName()+": "+ev.getNewValue());
    }
}

/** test */
public class TestJavaBeans
{
    public static void main (String[] args)
    {
        // utworzenie ziarna
        Ziarno z = new Ziarno(new MyGregCalendar(2014,2,14));
        DatownikDolny dolny = new DatownikDolny(new MyGregCalendar(2000,1,1));
        DatownikGorny gorny = new DatownikGorny(new MyGregCalendar(2014,12,31));
        Komentator koment = new Komentator();
        z.addVetoableChangeListener(dolny);
        z.addVetoableChangeListener(gorny);
        z.addPropertyChangeListener(koment);

        // zmiana właściwości powiązanej
        System.out.println(z);
        z.setTekst("dziś mamy ");
        System.out.println(z);

        // zmiana właściwości ograniczonej
        try { z.setData(new MyGregCalendar(2014,1,22)); }
        catch (Exception ex) { System.err.println(ex); }
        finally { System.out.println(z); }

        // zmiana właściwości ograniczonej - za wczwśnie
        try { z.setData(new MyGregCalendar(1998,2,5)); }
        catch (Exception ex) { System.err.println(ex); }
        finally { System.out.println(z); }

        // zmiana właściwości ograniczonej - za późno
        try { z.setData(new MyGregCalendar(2019,9,15)); }
        catch (Exception ex) { System.err.println(ex); }
        finally { System.out.println(z); }
    }
}

