package dynaaminen;

import java.lang.reflect.InvocationTargetException;

/**
 * Esimerkki dynaamisesta taulukosta Java 1.5:n geneerisyytt�
 * ja reflektiona k�ytt�en niin, ett� voi tallentaa vain
 * alkiota jotka toteuttavat tietyn rajapinnan ja uusi
 * alkio luodaan lis�yksen yhteydess�.
 * @author Vesa Lappalainen
 * @version 1.0, 02.03.2002
 * @version 1.1, 01.03.2005
 */

interface LukuRajapinta {
    public void set(int i);


    public int get();
}

class Luku implements LukuRajapinta {
    int luku;


    public Luku() {}


    public Luku(int i) {
        luku = i;
    }


    @Override
    public int get() {
        return luku;
    }


    @Override
    public void set(int i) {
        luku = i;
    }


    @Override
    public String toString() {
        return "" + luku;
    }
}

/**
 * Esimerkki rajoitetusta luokasta 
 * @param <TYPE> luokan yleinen tyyppi joka pit�� olla LukuRajapintaa
 */
public class TaulukkoGenExt<TYPE extends LukuRajapinta> {

    /** Luokka t�yden taulukon poikkeusta varten  */
    public static class TaulukkoTaysiException extends Exception {
        private static final long serialVersionUID = 1L;


        TaulukkoTaysiException(String viesti) {
            super(viesti);
        }
    }

    private Class<TYPE> luokka; // = TYPE.class; Tyhm��, n�in ei voi sanoa...
    private TYPE alkiot[];
    private int lkm;


    /**
     * @param luokka mit� tallennetaan
     */
    public TaulukkoGenExt(Class<TYPE> luokka) {
        this(luokka, 10);
    }


    /**
     * @param luokka mit� tallennetaan
     * @param koko taulukon aloituskoko
     */
    @SuppressWarnings("unchecked")
    public TaulukkoGenExt(Class<TYPE> luokka, int koko) {
        alkiot = (TYPE[]) new Object[koko];
        this.luokka = luokka;
    }


    /**
     * @param luku mik� alkio lis�t��n
     * @throws TaulukkoTaysiException jos taulukkoon ei mahdu
     */
    public void lisaa(int luku) throws TaulukkoTaysiException {
        try {
            TYPE uusi;
            uusi = luokka.getConstructor().newInstance();
            uusi.set(luku);
            if (lkm >= alkiot.length) throw new TaulukkoTaysiException("Tila loppu");
            alkiot[lkm++] = uusi;
        } catch (IllegalAccessException ex) {
            throw new TaulukkoTaysiException("Laiton viittaus " + ex.getMessage());
        } catch (InstantiationException ex) {
            throw new TaulukkoTaysiException("Laiton uuden luonti " + ex.getMessage());
        } catch (IllegalArgumentException ex) {
            throw new TaulukkoTaysiException("Laiton argumentti " + ex.getMessage());
        } catch (InvocationTargetException ex) {
            throw new TaulukkoTaysiException("Laiton kohde " + ex.getMessage());
        } catch (NoSuchMethodException ex) {
            throw new TaulukkoTaysiException("Ei metodia " + ex.getMessage());
        } catch (SecurityException ex) {
            throw new TaulukkoTaysiException("Suojausvirhe " + ex.getMessage());
        }
    }


    @Override
    public String toString() {
        StringBuilder s = new StringBuilder("");
        for (int i = 0; i < lkm; i++)
            s.append(" " + alkiot[i]);
        return s.toString();
    }


    /**
     * @param i mihin paikkaan laitetaan
     * @param luku mik� arvo laitetaan
     * @throws IndexOutOfBoundsException jos huono indeksi
     */
    public void set(int i, int luku) throws IndexOutOfBoundsException {
        if ((i < 0) || (lkm <= i)) throw new IndexOutOfBoundsException("i = " + i);
        alkiot[i].set(luku);
    }


    /**
     * @param i mist� paikasta haetaan
     * @return paikassa i oleva arvo
     * @throws IndexOutOfBoundsException jos huono indeksi
     */
    public int get(int i) throws IndexOutOfBoundsException {
        if ((i < 0) || (lkm <= i)) throw new IndexOutOfBoundsException("i = " + i);
        return alkiot[i].get();
    }


    /**
     * Testataan taulukkoa
     * @param args ei k�yt�ss�
     */
    public static void main(String[] args) {
        TaulukkoGenExt<Luku> luvut = new TaulukkoGenExt<Luku>(Luku.class);
        try {
            luvut.lisaa(0);
            luvut.lisaa(2);
            luvut.lisaa(99); // Tekee oikeasti luvut.lisaa(new Integer(99));
        } catch (TaulukkoTaysiException e) {
            System.out.println("Virhe: " + e.getMessage());
        }
        System.out.println(luvut);
        luvut.set(1, 4);
        System.out.println(luvut);
        int luku = luvut.get(2); // oik: luku = luvut.get(2).intValue();
        System.out.println("Paikassa 2 on " + luku);
        try {
            luvut.set(21, 4);
        } catch (IndexOutOfBoundsException e) {
            System.out.println("Virhe: " + e.getMessage());
        }
    }
}