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