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