1   package kerho;
2   
3   import java.io.BufferedReader;
4   import java.io.IOException;
5   import java.io.PrintWriter;
6   import java.io.File;
7   import java.util.Iterator;
8   import java.util.NoSuchElementException;
9   
10  import fi.jyu.mit.ohj2.Tiedosto;
11  
12  
13  /**
14   * Kerhon jäsenistö joka osaa mm. lisätä uuden jäsenen
15   *
16   * @author Vesa Lappalainen
17   * @version 1.0, 22.02.2003
18   */
19  public class Jasenet implements Iterable<Jasen> {
20    private boolean muutettu = false;
21    private String tiedostonPerusNimi = "";
22    private String kokoNimi = "";
23   
24    private static final int MAX_JASENIA = 5;
25    private int lkm = 0;
26  
27    /**
28     * Taulukko jäsenistä 
29     */
30    private Jasen alkiot[] = new Jasen[MAX_JASENIA];
31  
32  
33    /**
34     * Oletusmuodostaja
35     */
36    public Jasenet() {
37        // Attribuuttien oma alustus riittää
38    }
39    
40  
41    /**
42     * Lisää uuden jäsenen tietorakenteeseen.  Ottaa jäsenen omistukseensa.
43     * @param jasen lisätäävän jäsenen viite.  Huom tietorakenne muuttuu omistajaksi
44     * @throws SailoException jos tietorakenne on jo täynnä
45     * @example
46     * <pre name="test">
47     * #THROWS SailoException 
48     * #PACKAGEIMPORT
49     * Jasenet jasenet = new Jasenet();
50     * Jasen aku1 = new Jasen(), aku2 = new Jasen();
51     * jasenet.getLkm() === 0;
52     * jasenet.lisaa(aku1); jasenet.getLkm() === 1;
53     * jasenet.lisaa(aku2); jasenet.getLkm() === 2;
54     * jasenet.lisaa(aku1); jasenet.getLkm() === 3;
55     * jasenet.anna(0) === aku1;
56     * jasenet.anna(1) === aku2;
57     * jasenet.anna(2) === aku1;
58     * jasenet.anna(1) == aku1 === false;
59     * jasenet.anna(1) == aku2 === true;
60     * jasenet.anna(3) === aku1; #THROWS IndexOutOfBoundsException 
61     * jasenet.lisaa(aku1); jasenet.getLkm() === 4;
62     * jasenet.lisaa(aku1); jasenet.getLkm() === 5;
63     * jasenet.lisaa(aku1);  #THROWS SailoException
64     * </pre>
65     */
66    public void lisaa(Jasen jasen) throws SailoException {
67      muutettu = true;
68      if ( lkm >= alkiot.length ) throw new SailoException("Liikaa alkioita");
69      alkiot[lkm] = jasen;
70      lkm++;
71    }
72  
73  
74    /**
75     * Palauttaa viitteen i:teen jäseneen.
76     * @param i monennenko jäsenen viite halutaan
77     * @return viite jäseneen, jonka indeksi on i
78     * @throws IndexOutOfBoundsException jos i ei ole sallitulla alueella  
79     */
80    public Jasen anna(int i) throws IndexOutOfBoundsException  {
81      if ( i < 0 || lkm <= i ) throw new IndexOutOfBoundsException("Laiton indeksi: " + i);
82      return alkiot[i];
83    }
84  
85  
86    /**
87     * Lukee jäsenistön tiedostosta.  Kesken.
88     * @param tied tiedoston nimen alkuosa
89     * @throws SailoException jos lukeminen epäonnistuu
90     * 
91     * @example
92     * <pre name="test">
93     * #THROWS SailoException 
94     * #import java.io.File;
95     * 
96     *  Jasenet jasenet = new Jasenet();
97     *  Jasen aku1 = new Jasen(), aku2 = new Jasen();
98     *  aku1.vastaaAkuAnkka();
99     *  aku2.vastaaAkuAnkka();
100    *  String tiedNimi = "testikelmit";
101    *  File ftied = new File(tiedNimi+".dat");
102    *  ftied.delete();
103    *  jasenet.lueTiedostosta(tiedNimi); #THROWS SailoException
104    *  jasenet.lisaa(aku1);
105    *  jasenet.lisaa(aku2);
106    *  jasenet.talleta();
107    *  jasenet = new Jasenet();           // Poistetaan vanhat luomalla uusi
108    *  jasenet.lueTiedostosta(tiedNimi);  // johon ladataan tiedot tiedostosta.
109    *  Iterator<Jasen> i = jasenet.iterator();
110    *  i.next().toString() === aku1.toString();
111    *  i.next().toString() === aku2.toString();
112    *  i.hasNext() === false;
113    *  jasenet.lisaa(aku2);
114    *  jasenet.talleta();
115    *  ftied.delete() === true;
116    *  File fbak = new File(tiedNimi+".bak");
117    *  fbak.delete() === true;
118    * </pre>
119    */
120   public void lueTiedostosta(String tied) throws SailoException {
121     setTiedostonPerusNimi(tied);
122     BufferedReader fi = Tiedosto.avaa_lukemista_varten(getTiedostonNimi());
123     if ( fi == null ) throw new SailoException("Tiedosto " + getTiedostonNimi() + " ei aukea");
124 
125     try {
126       kokoNimi = fi.readLine();
127       if ( kokoNimi == null ) throw new SailoException("Kerhon nimi puuttuu");
128       String rivi = fi.readLine();
129       if ( rivi == null ) throw new SailoException("Maksimikoko puuttuu");
130       // int maxKoko = Mjonot.erotaInt(rivi,10); // tehdään jotakin
131 
132       while ( ( rivi = fi.readLine() ) != null ) {
133         rivi = rivi.trim();
134         if ( "".equals(rivi) || rivi.charAt(0) == ';') continue;
135         Jasen jasen = new Jasen(); // NOPMD, pakko luoda täällä
136         jasen.parse(rivi);  // voisi olla virhekäsittely
137         lisaa(jasen);
138       }
139       muutettu = false;
140 
141 
142     } catch ( IOException e ) {
143       throw new SailoException("Ongelmia tiedoston kanssa: " + e.getMessage()); // NOPMD
144     } finally {
145       try {
146         fi.close();
147       } catch (IOException e) {
148         throw new SailoException("Tiedoston sulkeminen ei onnistu: " + e.getMessage()); // NOPMD
149       }
150     }
151   }
152 
153   
154   /**
155    * Tallentaa jäsenistön tiedostoon.  
156    * Tiedoston muoto:
157    * <pre>
158    * Kelmien kerho
159    * 20
160    * ; kommenttirivi
161    * 2|Ankka Aku|121103-706Y|Ankkakuja 6|12345|ANKKALINNA|12-1234|||1996|50.0|30.0|Velkaa Roopelle
162    * 3|Ankka Tupu|121153-706Y|Ankkakuja 6|12345|ANKKALINNA|12-1234|||1996|50.0|30.0|Velkaa Roopelle
163    * </pre>
164    * @throws SailoException jos talletus epäonnistuu
165    */
166   public void talleta() throws SailoException {
167     if ( !muutettu ) return;
168 
169     File fbak = new File(getBakNimi());
170     File ftied = new File(getTiedostonNimi());
171     fbak.delete();                  // if .. System.err.println("Ei voi tuhota");
172     ftied.renameTo(fbak);           // if .. System.err.println("Ei voi nimetä");
173 
174     PrintWriter fo = Tiedosto.avaa_kirjoittamista_varten(ftied.getName());
175     if ( fo == null ) throw new SailoException("Tiedosto " + ftied.getName() + "ei aukea");
176     try {
177       fo.println(getKokoNimi());
178       fo.println(alkiot.length);
179       for (Jasen jasen:this) {
180         fo.println(jasen.toString());
181       }
182     //} catch ( IOException e ) { // ei heitä poikkeusta
183     //  throw new SailoException("Tallettamisessa ongelmia: " + e.getMessage()); // NOPMD
184     } finally {
185       fo.close();
186     }
187 
188     muutettu = false;
189   }
190   
191   /**
192    * Palauttaa Kerhon koko nimen
193    * @return Kerhon koko nimi merkkijononna
194    */
195   public String getKokoNimi()           { return kokoNimi;                   }
196 
197   
198   /**
199    * Palauttaa kerhon jäsenten lukumäärän
200    * @return jäsenten lukumäärä
201    */
202   public int getLkm()                    { return lkm;                         }
203 
204   
205   /**
206    * Asettaa tiedoston perusnimen ilan tarkenninta
207    * @param tied tallennustiedoston perusnimi
208    */
209   public void setTiedostonPerusNimi(String tied)  { tiedostonPerusNimi = tied;     }
210 
211   
212   /**
213    * Palauttaa tiedoston nimen, jota käytetään tallennukseen
214    * @return tallennustiedoston nimi
215    */
216   public String getTiedostonPerusNimi()  { return tiedostonPerusNimi;              }
217 
218   
219   /**
220    * Palauttaa tiedoston nimen, jota käytetään tallennukseen
221    * @return tallennustiedoston nimi
222    */
223   public String getTiedostonNimi()       { return tiedostonPerusNimi + ".dat";     }
224 
225   
226   /**
227    * Palauttaa varakopiotiedoston nimen
228    * @return varakopiotiedoston nimi
229    */
230   public String getBakNimi()             { return tiedostonPerusNimi + ".bak";     }
231 
232   
233 
234   /**
235    * Luokka jäsenten iteroimiseksi.
236    * @example
237    * <pre name="test">
238    * #THROWS SailoException 
239    * #PACKAGEIMPORT
240    * #import java.util.*;
241    * 
242    * Jasenet jasenet = new Jasenet();
243    * Jasen aku1 = new Jasen(), aku2 = new Jasen();
244    * aku1.rekisteroi(); aku2.rekisteroi();
245    *
246    * jasenet.lisaa(aku1); 
247    * jasenet.lisaa(aku2); 
248    * jasenet.lisaa(aku1); 
249    * 
250    * StringBuffer ids = new StringBuffer(30);
251    * for (Jasen jasen:jasenet)   // Kokeillaan for-silmukan toimintaa
252    *   ids.append(" "+jasen.getTunnusnro());           // NOPMD
253    * 
254    * String tulos = " " + aku1.getTunnusnro() + " " + aku2.getTunnusnro() + " " + aku1.getTunnusnro();
255    * 
256    * ids.toString() === tulos; 
257    * 
258    * ids = new StringBuffer(30);
259    * for (Iterator<Jasen>  i=jasenet.iterator(); i.hasNext(); ) { // ja iteraattorin toimintaa
260    *   Jasen jasen = i.next();
261    *   ids.append(" "+jasen.getTunnusnro());           // NOPMD
262    * }
263    * 
264    * ids.toString() === tulos;
265    * 
266    * Iterator<Jasen>  i=jasenet.iterator();
267    * i.next() == aku1  === true;
268    * i.next() == aku2  === true;
269    * i.next() == aku1  === true;
270    * 
271    * i.next();  #THROWS NoSuchElementException
272    *  
273    * </pre>
274    */
275   public class JasenetIterator implements Iterator<Jasen> {
276       private int kohdalla = -1;
277 
278       /**
279        * Onko olemassa vielä seuraavaa jäsentä
280        * @see java.util.Iterator#hasNext()
281        * @return true jos on vielä jäseniä
282        */
283       public boolean hasNext() {
284 //       if ( kohdalla + 1 >= lkm )  return false;
285 //       return true;
286         return kohdalla + 1 < lkm;
287       }
288 
289       
290     /**
291      * Annetaan seuraava jäsen
292      * @return seuraava jäsen
293      * @throws NoSuchElementException jos seuraava alkiota ei enää ole
294      * @see java.util.Iterator#next()
295      */
296     public Jasen next() throws NoSuchElementException {
297         if ( !hasNext() ) throw new NoSuchElementException("Ei oo");
298         kohdalla++;
299         return alkiot[kohdalla];
300       }
301 
302     
303       
304     /**
305      * Tuhoamista ei ole toteutettu
306      * @throws UnsupportedOperationException aina
307      * @see java.util.Iterator#remove()
308      */
309     public void remove() throws UnsupportedOperationException {
310         throw new UnsupportedOperationException("Me ei poisteta");
311       }
312     }
313 
314     /**
315      * Palautetaan iteraattori jäsenistöön.
316      * @return jäsen iteraattori
317      */
318     public Iterator<Jasen> iterator() {
319 //      return alkiot.iterator();
320       return new JasenetIterator();
321     }
322   
323   
324   /**
325    * Testiohjelma jäsenistölle
326    * @param args ei käytössä
327    */
328   public static void main(String args[]) {
329     Jasenet jasenet = new Jasenet();
330 
331     Jasen aku = new Jasen(), aku2 = new Jasen();
332     aku.rekisteroi();    aku.vastaaAkuAnkka();
333     aku2.rekisteroi();   aku2.vastaaAkuAnkka();
334 
335     try {
336       jasenet.lisaa(aku);
337       jasenet.lisaa(aku2);
338   
339       System.out.println("============= Jäsenet testi =================");
340 
341       for (Iterator<Jasen>  i=jasenet.iterator(); i.hasNext(); ) {
342 //        Jasen jasen = jasenet.anna(i);
343 //        System.out.println("Jäsen nro: " + i);
344         Jasen jasen = i.next();
345         jasen.tulosta(System.out);
346       }
347       jasenet.anna(100);
348       
349     } catch ( IndexOutOfBoundsException ex ) {
350         System.out.println(ex.getMessage());
351     } catch ( SailoException ex ) {
352       System.out.println(ex.getMessage());
353     }
354   }
355 
356 }
357 
358 
359