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.Iterator;
11  import java.util.List;
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 = 50;
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     * Muodostaja jolla maxkoko voidaan asettaa
48     * @param koko jäsenistön maxkoko 
49     * 
50     */
51    public Jasenet(int koko) {
52        alkiot = new Jasen[koko];
53    }
54    
55  
56    /**
57     * Lisää uuden jäsenen tietorakenteeseen.  Ottaa jäsenen omistukseensa.
58     * @param jasen lisätäävän jäsenen viite.  Huom tietorakenne muuttuu omistajaksi
59     * @throws SailoException jos tietorakenne on jo täynnä
60     * @example
61     * <pre name="test">
62     * #THROWS SailoException 
63     * #PACKAGEIMPORT
64     * Jasenet jasenet = new Jasenet(5);
65     * Jasen aku1 = new Jasen(), aku2 = new Jasen();
66     * jasenet.getLkm() === 0;
67     * jasenet.lisaa(aku1); jasenet.getLkm() === 1;
68     * jasenet.lisaa(aku2); jasenet.getLkm() === 2;
69     * jasenet.lisaa(aku1); jasenet.getLkm() === 3;
70     * jasenet.anna(0) === aku1;
71     * jasenet.anna(1) === aku2;
72     * jasenet.anna(2) === aku1;
73     * jasenet.anna(1) == aku1 === false;
74     * jasenet.anna(1) == aku2 === true;
75     * jasenet.anna(3) === aku1; #THROWS IndexOutOfBoundsException 
76     * jasenet.lisaa(aku1); jasenet.getLkm() === 4;
77     * jasenet.lisaa(aku1); jasenet.getLkm() === 5;
78     * jasenet.lisaa(aku1);  #THROWS SailoException
79     * </pre>
80     */
81    public void lisaa(Jasen jasen) throws SailoException {
82      muutettu = true;
83      if ( lkm >= alkiot.length ) throw new SailoException("Liikaa alkioita");
84      alkiot[lkm] = jasen;
85      lkm++;
86    }
87  
88  
89    /**
90     * Korvaa jäsenen tietorakenteessa.  Ottaa jäsenen omistukseensa.
91     * Etsitään samalla tunnusnumerolla oleva jäsen.  Jos ei läydy,
92     * niin lisätään uutena jäsenenä.
93     * @param jasen lisätäävän jäsenen viite.  Huom tietorakenne muuttuu omistajaksi
94     * @throws SailoException jos tietorakennen on jo täynnä
95     */
96    public void korvaa(Jasen jasen) throws SailoException {
97      int id = jasen.getTunnusnro();
98      for (int i = 0; i < lkm; i++ ) {
99        if ( alkiot[i].getTunnusnro() == id ) {
100         alkiot[i] = jasen;
101         muutettu = true;
102         return;
103       }
104     }
105     lisaa(jasen);
106   }
107 
108   
109   /**
110    * Palauttaa viitteen i:teen jäseneen.
111    * @param i monennenko jäsenen viite halutaan
112    * @return viite jäseneen, jonka indeksi on i
113    * @throws IndexOutOfBoundsException jos i ei ole sallitulla alueella  
114    */
115   public Jasen anna(int i) throws IndexOutOfBoundsException  {
116     if ( i < 0 || lkm <= i ) throw new IndexOutOfBoundsException("Laiton indeksi: " + i);
117     return alkiot[i];
118   }
119 
120   /**
121    * Palauttaa viitteen i:teen jäseneen.
122    * @param i monennenko jäsenen viite halutaan
123    * @return viite jäseneen, jonka indeksi on i
124    * @throws IndexOutOfBoundsException jos i ei ole sallitulla alueella  
125    */
126   public Object annaObj(int i) throws IndexOutOfBoundsException  {
127     if ( i < 0 || lkm <= i ) throw new IndexOutOfBoundsException("Laiton indeksi: " + i);
128     return alkiot[i];
129   }
130 
131 /*
132   private void koe() {
133       Object eka = annaObj(0);
134       if ( eka instanceof Jasen ) {
135         Jasen jasen = (Jasen)eka;
136       }  
137   }
138 */  
139 
140   /**
141    * Lukee jäsenistän tiedostosta.  Kesken.
142    * @param tied tiedoston nimen alkuosa
143    * @throws SailoException jos lukeminen epäonnistuu
144    * 
145    * @example
146    * <pre name="test">
147    * #THROWS SailoException 
148    * #import java.io.File;
149    * 
150    *  Jasenet jasenet = new Jasenet();
151    *  Jasen aku1 = new Jasen(), aku2 = new Jasen();
152    *  aku1.vastaaAkuAnkka();
153    *  aku2.vastaaAkuAnkka();
154    *  String tiedNimi = "testikelmit";
155    *  File ftied = new File(tiedNimi+".dat");
156    *  ftied.delete();
157    *  jasenet.lueTiedostosta(tiedNimi); #THROWS SailoException
158    *  jasenet.lisaa(aku1);
159    *  jasenet.lisaa(aku2);
160    *  jasenet.talleta();
161    *  jasenet = new Jasenet();           // Poistetaan vanhat luomalla uusi
162    *  jasenet.lueTiedostosta(tiedNimi);  // johon ladataan tiedot tiedostosta.
163    *  Iterator<Jasen> i = jasenet.iterator();
164    *  i.next().toString() === aku1.toString();
165    *  i.next().toString() === aku2.toString();
166    *  i.hasNext() === false;
167    *  jasenet.lisaa(aku2);
168    *  jasenet.talleta();
169    *  ftied.delete() === true;
170    *  File fbak = new File(tiedNimi+".bak");
171    *  fbak.delete() === true;
172    * </pre>
173    */
174   public void lueTiedostosta(String tied) throws SailoException {
175     setTiedostonPerusNimi(tied);
176     BufferedReader fi = Tiedosto.avaa_lukemista_varten(getTiedostonNimi());
177     if ( fi == null ) throw new SailoException("Tiedosto " + getTiedostonNimi() + " ei aukea");
178 
179     try {
180       kokoNimi = fi.readLine();
181       if ( kokoNimi == null ) throw new SailoException("Kerhon nimi puuttuu");
182       String rivi = fi.readLine();
183       if ( rivi == null ) throw new SailoException("Maksimikoko puuttuu");
184       // int maxKoko = Mjonot.erotaInt(rivi,10); // tehdään jotakin
185 
186       while ( ( rivi = fi.readLine() ) != null ) { // NOPMD
187         rivi = rivi.trim();
188         if ( "".equals(rivi) || rivi.charAt(0) == ';') continue;
189         Jasen jasen = new Jasen(); // NOPMD, pakko luoda täällä
190         jasen.parse(rivi);  // voisi olla virhekäsittely
191         lisaa(jasen);
192       }
193       muutettu = false;
194 
195 
196     } catch ( IOException e ) {
197       throw new SailoException("Ongelmia tiedoston kanssa: " + e.getMessage()); // NOPMD
198     } finally {
199       try {
200         fi.close();
201       } catch (IOException e) {
202         throw new SailoException("Tiedoston sulkeminen ei onnistu: " + e.getMessage()); // NOPMD
203       }
204     }
205   }
206 
207   
208   /**
209    * Tallentaa jäsenistän tiedostoon.  
210    * Tiedoston muoto:
211    * <pre>
212    * Kelmien kerho
213    * 20
214    * ; kommenttirivi
215    * 2|Ankka Aku|121103-706Y|Ankkakuja 6|12345|ANKKALINNA|12-1234|||1996|50.0|30.0|Velkaa Roopelle
216    * 3|Ankka Tupu|121153-706Y|Ankkakuja 6|12345|ANKKALINNA|12-1234|||1996|50.0|30.0|Velkaa Roopelle
217    * </pre>
218    * @throws SailoException jos talletus epäonnistuu
219    */
220   public void talleta() throws SailoException {
221     if ( !muutettu ) return;
222 
223     File fbak = new File(getBakNimi());
224     File ftied = new File(getTiedostonNimi());
225     fbak.delete();                  // if .. System.err.println("Ei voi tuhota");
226     ftied.renameTo(fbak);           // if .. System.err.println("Ei voi nimetä");
227 
228     PrintWriter fo = Tiedosto.avaa_kirjoittamista_varten(ftied.getName());
229     if ( fo == null ) throw new SailoException("Tiedosto " + ftied.getName() + "ei aukea");
230     try {
231       fo.println(getKokoNimi());
232       fo.println(alkiot.length);
233       for (Jasen jasen:this) {
234         fo.println(jasen.toString());
235       }
236     //} catch ( IOException e ) { // ei heitä poikkeusta
237     //  throw new SailoException("Tallettamisessa ongelmia: " + e.getMessage()); // NOPMD
238     } finally {
239       fo.close();
240     }
241 
242     muutettu = false;
243   }
244   
245   /**
246    * Palauttaa Kerhon koko nimen
247    * @return Kerhon koko nimi merkkijononna
248    */
249   public String getKokoNimi()           { return kokoNimi;                   }
250 
251   
252   /**
253    * Palauttaa kerhon jäsenten lukumäärän
254    * @return jäsenten lukumäärä
255    */
256   public int getLkm()                    { return lkm;                         }
257 
258   
259   /**
260    * Asettaa tiedoston perusnimen ilan tarkenninta
261    * @param tied tallennustiedoston perusnimi
262    */
263   public void setTiedostonPerusNimi(String tied)  { tiedostonPerusNimi = tied;     }
264 
265   
266   /**
267    * Palauttaa tiedoston nimen, jota käytetään tallennukseen
268    * @return tallennustiedoston nimi
269    */
270   public String getTiedostonPerusNimi()  { return tiedostonPerusNimi;              }
271 
272   
273   /**
274    * Palauttaa tiedoston nimen, jota käytetään tallennukseen
275    * @return tallennustiedoston nimi
276    */
277   public String getTiedostonNimi()       { return tiedostonPerusNimi + ".dat";     }
278 
279   
280   /**
281    * Palauttaa varakopiotiedoston nimen
282    * @return varakopiotiedoston nimi
283    */
284   public String getBakNimi()             { return tiedostonPerusNimi + ".bak";     }
285 
286   
287 
288   /**
289    * Luokka jäsenten iteroimiseksi.
290    * @example
291    * <pre name="test">
292    * #THROWS SailoException 
293    * #PACKAGEIMPORT
294    * #import java.util.*;
295    * 
296    * Jasenet jasenet = new Jasenet();
297    * Jasen aku1 = new Jasen(), aku2 = new Jasen();
298    * aku1.rekisteroi(); aku2.rekisteroi();
299    *
300    * jasenet.lisaa(aku1); 
301    * jasenet.lisaa(aku2); 
302    * jasenet.lisaa(aku1); 
303    * 
304    * StringBuffer ids = new StringBuffer(30);
305    * for (Jasen jasen:jasenet)   // Kokeillaan for-silmukan toimintaa
306    *   ids.append(" "+jasen.getTunnusnro());           // NOPMD
307    * 
308    * String tulos = " " + aku1.getTunnusnro() + " " + aku2.getTunnusnro() + " " + aku1.getTunnusnro();
309    * 
310    * ids.toString() === tulos; 
311    * 
312    * ids = new StringBuffer(30);
313    * for (Iterator<Jasen>  i=jasenet.iterator(); i.hasNext(); ) { // ja iteraattorin toimintaa
314    *   Jasen jasen = i.next();
315    *   ids.append(" "+jasen.getTunnusnro());           // NOPMD
316    * }
317    * 
318    * ids.toString() === tulos;
319    * 
320    * Iterator<Jasen>  i=jasenet.iterator();
321    * i.next() == aku1  === true;
322    * i.next() == aku2  === true;
323    * i.next() == aku1  === true;
324    * 
325    * i.next();  #THROWS NoSuchElementException
326    *  
327    * </pre>
328    */
329   public class JasenetIterator implements Iterator<Jasen> {
330       private int kohdalla = -1;
331 
332       /**
333        * Onko olemassa vielä seuraavaa jäsentä
334        * @see java.util.Iterator#hasNext()
335        * @return true jos on vielä jäseniä
336        */
337       public boolean hasNext() {
338 //       if ( kohdalla + 1 >= lkm )  return false;
339 //       return true;
340         return kohdalla + 1 < lkm;
341       }
342 
343       
344     /**
345      * Annetaan seuraava jäsen
346      * @return seuraava jäsen
347      * @throws NoSuchElementException jos seuraava alkiota ei enää ole
348      * @see java.util.Iterator#next()
349      */
350     public Jasen next() throws NoSuchElementException {
351         if ( !hasNext() ) throw new NoSuchElementException("Ei oo");
352         kohdalla++;
353         return alkiot[kohdalla];
354       }
355 
356     
357       
358     /**
359      * Tuhoamista ei ole toteutettu
360      * @throws UnsupportedOperationException aina
361      * @see java.util.Iterator#remove()
362      */
363     public void remove() throws UnsupportedOperationException {
364         throw new UnsupportedOperationException("Me ei poisteta");
365       }
366     }
367 
368     /**
369      * Palautetaan iteraattori jäsenistään.
370      * @return jäsen iteraattori
371      */
372     public Iterator<Jasen> iterator() {
373 //      return alkiot.iterator();
374       return new JasenetIterator();
375     }
376   
377     
378     /**
379      * Palauttaa "taulukossa" hakuehtoon vastaavien jäsenten viitteet
380      * @param hakuehto hakuehto
381      * @param k etsittävän kentän indeksi 
382      * @return tietorakenteen löytyneistä jäsenistä
383      * @example
384      * <pre name="test">
385      * #THROWS SailoException 
386      *   Jasenet jasenet = new Jasenet();
387      *   Jasen jasen1 = new Jasen(); jasen1.parse("1|Ankka Aku|130227-411J|Ankkakuja 6|");
388      *   Jasen jasen2 = new Jasen(); jasen2.parse("2|Ankka Tupu|091007-408T|Ankkakuja 6|");
389      *   Jasen jasen3 = new Jasen(); jasen3.parse("3|Ankka Roope|180880-846M|Ankkakuja 12");
390      *   Jasen jasen4 = new Jasen(); jasen4.parse("4|Ankka Iines|150883-179N|Ankkakuja 9");
391      *   Jasen jasen5 = new Jasen(); jasen5.parse("5|Susi Sepe|121236-1213||131313|Perämetsä");
392      *   jasenet.lisaa(jasen1); jasenet.lisaa(jasen2); jasenet.lisaa(jasen3); jasenet.lisaa(jasen4); jasenet.lisaa(jasen5);
393      *   List loytyneet;
394      *   loytyneet = (List)jasenet.etsi("*s*",1);
395      *   loytyneet.size() === 2;
396      *   loytyneet.get(0) == jasen4 === true;
397      *   loytyneet.get(1) == jasen5 === true;
398      *   
399      *   loytyneet = (List)jasenet.etsi("*7-*",2);
400      *   loytyneet.size() === 2;
401      *   loytyneet.get(0) == jasen2 === true;
402      *   loytyneet.get(1) == jasen1 === true;
403      * </pre>
404      */
405     public Collection<Jasen> etsi(String hakuehto,int k) {
406       List<Jasen> loytyneet = new ArrayList<Jasen>();
407       for (Jasen jasen : this) {
408 //        if ( jasen.anna(k).toUpperCase().startsWith(hakuehto.toUpperCase()) ) loytyneet.add(jasen);  // NOPMD
409         if ( WildChars.onkoSamat(jasen.anna(k),hakuehto) ) loytyneet.add(jasen);  // NOPMD
410       }
411       Collections.sort(loytyneet,new Jasen.Vertailija(k));
412       return loytyneet;
413     }
414 
415     
416     /**
417      * Etsii jäsenen id:n perusteella
418      * @param id tunnusnumero, jonka mukaan etsitään
419      * @return jäsen jolla etsittävä id tai null
420      */
421     public Jasen annaId(int id) {
422       for (Jasen jasen : this ) {
423         if ( id == jasen.getTunnusnro() ) return jasen;
424       }
425       return null;
426 
427     }
428     
429     
430   /**
431    * Testiohjelma jäseniställe
432    * @param args ei käytässä
433    */
434   public static void main(String args[]) {
435     Jasenet jasenet = new Jasenet();
436 
437     Jasen aku = new Jasen(), aku2 = new Jasen();
438     aku.rekisteroi();    aku.vastaaAkuAnkka();
439     aku2.rekisteroi();   aku2.vastaaAkuAnkka();
440 
441     try {
442       jasenet.lisaa(aku);
443       jasenet.lisaa(aku2);
444   
445       System.out.println("============= Jäsenet testi =================");
446 
447       for (Iterator<Jasen>  i=jasenet.iterator(); i.hasNext(); ) {
448 //        Jasen jasen = jasenet.anna(i);
449 //        System.out.println("Jäsen nro: " + i);
450         Jasen jasen = i.next();
451         jasen.tulosta(System.out);
452       }
453       jasenet.anna(100);
454       
455     } catch ( IndexOutOfBoundsException ex ) {
456         System.out.println(ex.getMessage());
457     } catch ( SailoException ex ) {
458       System.out.println(ex.getMessage());
459     }
460   }
461 
462 
463 }
464 
465 
466