1   package kerho;
2   
3   import java.io.*;
4   import java.util.Comparator;
5   
6   import kanta.HetuKentta;
7   import kanta.HetuTarkistus;
8   import kanta.IntKentta;
9   import kanta.JonoKentta;
10  import kanta.Kentta;
11  import kanta.PuhelinKentta;
12  import kanta.RahaKentta;
13  import kanta.RegExpTarkistaja;
14  import kanta.SisaltaaTarkistaja;
15  
16  import fi.jyu.mit.ohj2.Mjonot;
17  
18  import static kanta.HetuTarkistus.*;
19  
20  /**
21   * Kerhon jäsen joka osaa mm. itse huolehtia tunnusNro:staan.
22   *
23   * @author Vesa Lappalainen
24   * @version 1.0, 22.02.2003
25   * @version 2.0, 09.01.2011 - kentät indeksoituna
26   */
27  public class Jasen implements Cloneable {
28      private Kentta     kentat[]    = { // valitettavasti ei voi olla final
29                                         // vaikka pitäisi, clone estää tämän :-(
30              new IntKentta("id"),
31              new JonoKentta("nimi"),
32              new HetuKentta("hetu", new HetuTarkistus()),
33              new JonoKentta("katuosoite"),
34              new JonoKentta("postinumero", new SisaltaaTarkistaja(SisaltaaTarkistaja.NUMEROT)),
35              new JonoKentta("postiosoite"),
36              new PuhelinKentta("kotipuhelin"),
37              new PuhelinKentta("työpuhelin"),
38              new JonoKentta("autopuhelin", new RegExpTarkistaja("[- 0-9]*")),
39              new IntKentta("liittymisvuosi"),
40              new RahaKentta("jäsenmaksu"),
41              new RahaKentta("maksettumaksu"),
42              new JonoKentta("lisätietoja")
43      };
44  
45      private static int seuraavaNro    = 1;
46  
47      /**
48       * Luokka joka vertaa kahta jäsentä keskenään 
49       */
50      public static class Vertailija implements Comparator<Jasen> {
51  
52          private final int kenttanro;
53  
54  
55          /**
56           * Alustetaan vertailija vertailemaan tietyn kentän perusteella
57           * @param k vertailtavan kentän indeksi.
58           */
59          public Vertailija(int k) {
60              this.kenttanro = k;
61          }
62  
63  
64          /**
65           * Verrataana kahta jäsentä keskenään.
66           * @param j1 1. verrattava jäsen
67           * @param j2 2. verrattava jäsen
68           * @return <0 jos j1 < j2, == 0 jos j1 == j2 ja muuten >0
69           */
70          @Override
71          public int compare(Jasen j1, Jasen j2) {
72              String s1 = j1.getAvain(kenttanro);
73              String s2 = j2.getAvain(kenttanro);
74  
75              return s1.compareTo(s2);
76  
77          }
78  
79      }
80  
81      /**
82       * Palauttaa jäsenen kenttien lukumäärän
83       * @return kenttien lukumäärä
84       */
85      public int getKenttia() {
86          return kentat.length;
87      }
88  
89  
90      /**
91       * Eka kenttä joka on mielekäs kysyttäväksi
92       * @return eknn kentän indeksi
93       */
94      public int ekaKentta() {
95          return 1;
96      }
97  
98      
99      /**
100      * Alustetaan jäsenen merkkijono-attribuuti tyhjiksi jonoiksi
101      * ja tunnusnro = 0.
102      */
103     public Jasen() {
104         // Toistaiseksi ei tarvita mitään
105     }
106 
107 
108     /**
109      * @return jäsenen nimi
110      * @example
111      * <pre name="test">
112      *   Jasen aku = new Jasen();
113      *   aku.vastaaAkuAnkka();
114      *   aku.getNimi() =R= "Ankka Aku .*";
115      * </pre>
116      */
117     public String getNimi() {
118         return anna(1);
119     }
120 
121     
122     /**
123      * Antaa k:n kentän sisällön merkkijonona
124      * @param k monenenko kentän sisältö palautetaan
125      * @return kentän sisältö merkkijonona
126      */
127     public String anna(int k) { 
128         try {
129             return kentat[k].toString();
130         } catch (Exception ex) {
131             return "";
132         }
133     }
134 
135 
136     /**
137      * Antaa k:n kentän sisällön avain-merkkijonona
138      * jonka perusteella voi lajitella
139      * @param k monenenko kentän sisältö palautetaan
140      * @return kentän sisältö merkkijonona
141      *
142      * @example
143      * <pre name="test">
144      *   Jasen aku = new Jasen();
145      *   aku.parse("   1  |  Ankka Aku   | 030201-111C");
146      *   aku.getAvain(0) === "         1";
147      *   aku.getAvain(1) === "ANKKA AKU";
148      *   aku.getAvain(2) === "010203-111C";
149      *   aku.getAvain(20) === "";
150      * </pre>
151      */
152     public String getAvain(int k) {
153         try {
154             return kentat[k].getAvain();
155         } catch (Exception ex) {
156             return "";
157         }
158     }
159 
160 
161     /**
162      * Asettaa k:n kentän arvoksi parametrina tuodun merkkijonon arvon
163      * @param k kuinka monennen kentän arvo asetetaan
164      * @param jono jonoa joka asetetaan kentän arvoksi
165      * @return null jos asettaminen onnistuu, muuten vastaava virheilmoitus.
166      * @example
167      * <pre name="test">
168      *   Jasen jasen = new Jasen();
169      *   jasen.aseta(1,"Ankka Aku") === null;
170      *   jasen.aseta(2,"kissa") =R= ".*Hetu liian lyhyt"
171      *   jasen.aseta(2,"030201-1111") === "hetu: Tarkistusmerkin kuuluisi olla C"; 
172      *   jasen.aseta(2,"030201-111C") === null; 
173      *   jasen.aseta(9,"kissa") === "liittymisvuosi: Ei kokonaisluku (kissa)";
174      *   jasen.aseta(9,"1940") === null;
175      * </pre>
176      */
177     public String aseta(int k, String jono) { 
178         try {
179             String virhe = kentat[k].aseta(jono.trim());
180             if ( virhe == null && k == 0 ) setTunnusNro(getTunnusNro());
181             if ( virhe == null ) return virhe;
182             return getKysymys(k) +": " + virhe;
183         } catch (Exception ex) {
184             return "Virhe: " + ex.getMessage();
185         }
186     }
187 
188 
189     /**
190      * Palauttaa k:tta jäsenen kenttää vastaavan kysymyksen
191      * @param k kuinka monennen kentän kysymys palautetaan (0-alkuinen)
192      * @return k:netta kenttää vastaava kysymys
193      */
194     public String getKysymys(int k) { 
195         try {
196             return kentat[k].getKysymys();
197         } catch (Exception ex) {
198             return "Ääliö";
199         }
200     }
201 
202     
203     /**
204      * @return kaikkien näytettävien kysymysten otsikot merkkijonotaulukkona
205      * @example
206      * <pre name="test">
207      * #import java.util.Arrays;
208      *   Jasen jasen = new Jasen();
209      *   Arrays.toString(jasen.getOtsikot()) =R= "\\[nimi, hetu, katuosoite.*";
210      * </pre>
211      */
212     public String[] getOtsikot() {
213         int n = getKenttia() - ekaKentta();
214         String[] otsikot = new String[n];
215         for (int i=0,k=ekaKentta(); i<n; i++,k++) 
216             otsikot[i] = getKysymys(k);
217         return otsikot;        
218     }
219     
220     
221     /**
222      * Apumetodi, jolla saadaan täytettyä testiarvot jäsenelle.
223      * @param apuhetu hetu joka annetaan henkilölle 
224      */
225     public void vastaaAkuAnkka(String apuhetu) {
226         aseta(1,"Ankka Aku " + kanta.HetuTarkistus.rand(1000, 9999));
227         aseta(2,apuhetu);                                            
228         aseta(3,"Ankkakuja 6");                                      
229         aseta(4,"12345");                                            
230         aseta(5,"ANKKALINNA");                                       
231         aseta(6,"12-1234");                                          
232         aseta(7,"");                                                 
233         aseta(8,"");                                                 
234         aseta(9,"1996");                                             
235         aseta(10,"50");                                              
236         aseta(11,"30");                                              
237         aseta(12,"Velkaa Roopelle");                                 
238     }
239 
240 
241     /**
242      * Apumetodi, jolla saadaan täytettyä testiarvot jäsenelle.
243      * Henkilötunnus arvotaan, jotta kahdella jäsenellä ei olisi
244      * samoja tietoja.
245      */
246     public void vastaaAkuAnkka() {
247         String apuhetu = arvoHetu();
248         vastaaAkuAnkka(apuhetu);
249     }
250 
251 
252     /**
253      * Tulostetaan henkilön tiedot
254      * @param out tietovirta johon tulostetaan
255      */
256     public void tulosta(PrintWriter out) {
257         int pisin = 0;
258         for (Kentta kentta : kentat)
259             if (kentta.getKysymys().length() > pisin)
260                 pisin = kentta.getKysymys().length();
261 
262         for (Kentta kentta : kentat)
263             out.println(Mjonot.fmt(kentta.getKysymys(), -pisin - 1) +
264                     ": " + kentta.toString());
265     }
266 
267 
268     /**
269      * Tulostetaan henkilön tiedot
270      * @param os tietovirta johon tulostetaan
271      */
272     public void tulosta(OutputStream os) {
273         tulosta(new PrintStream(os));
274     }
275 
276 
277     /**
278      * Tulostetaan henkilön tiedot
279      * @param out tietovirta johon tulostetaan
280      */
281     public void tulosta(PrintStream out) {
282         tulosta(new PrintWriter(out, true)); // ilman autoflushia ei mitään
283                                              // tulostu!
284     }
285 
286     
287     /**
288      * Antaa jäsenelle seuraavan rekisterinumeron.
289      * @return jäsenen uusi tunnusNro
290      * @example
291      * <pre name="test">
292      *   Jasen aku1 = new Jasen();
293      *   aku1.getTunnusNro() === 0;
294      *   aku1.rekisteroi();
295      *   Jasen aku2 = new Jasen();
296      *   aku2.rekisteroi();
297      *   int n1 = aku1.getTunnusNro();
298      *   int n2 = aku2.getTunnusNro();
299      *   n1 === n2-1;
300      * </pre>
301      */
302     public int rekisteroi() {
303         return setTunnusNro(seuraavaNro);
304     }
305 
306 
307     /**
308      * Palauttaa jäsenen tunnusnumeron.
309      * @return jäsenen tunnusnumero
310      */
311     public int getTunnusNro() {
312         return ((IntKentta)(kentat[0])).getValue();
313     }
314 
315 
316     /**
317      * Asettaa tunnusnumeron ja samalla varmistaa että
318      * seuraava numero on aina suurempi kuin tähän mennessä suurin.
319      * @param nr asetettava tunnusnumero
320      */
321     private int setTunnusNro(int nr) {
322         IntKentta k = ((IntKentta)(kentat[0]));
323         k.setValue(nr);
324         if (nr >= seuraavaNro) seuraavaNro = nr + 1;
325         return k.getValue();
326     }
327     
328 
329     /**
330      * Palauttaa jäsenen tiedot merkkijonona jonka voi tallentaa tiedostoon.
331      * @return jäsen tolppaeroteltuna merkkijonona 
332      * @example
333      * <pre name="test">
334      *   Jasen jasen = new Jasen();
335      *   jasen.parse("   3  |  Ankka Aku   | 030201-111C");
336      *   jasen.toString().startsWith("3|Ankka Aku|030201-111C|") === true; // on enemmäkin kuin 3 kenttää, siksi loppu |
337      * </pre>  
338      */
339     @Override
340     public String toString() {
341         StringBuffer sb = new StringBuffer("");
342         String erotin = "";
343         for (int k = 0; k < getKenttia(); k++) {
344             sb.append(erotin);
345             sb.append(anna(k));
346             erotin = "|";
347         }
348         return sb.toString();
349     }
350 
351 
352     /**
353      * Selvitää jäsenen tiedot | erotellusta merkkijonosta
354      * Pitää huolen että seuraavaNro on suurempi kuin tuleva tunnusNro.
355      * @param rivi josta jäsenen tiedot otetaan
356      * 
357      * @example
358      * <pre name="test">
359      *   Jasen jasen = new Jasen();
360      *   jasen.parse("   3  |  Ankka Aku   | 030201-111C");
361      *   jasen.getTunnusNro() === 3;
362      *   jasen.toString().startsWith("3|Ankka Aku|030201-111C|") === true; // on enemmäkin kuin 3 kenttää, siksi loppu |
363      *
364      *   jasen.rekisteroi();
365      *   int n = jasen.getTunnusNro();
366      *   jasen.parse(""+(n+20));       // Otetaan merkkijonosta vain tunnusnumero
367      *   jasen.rekisteroi();           // ja tarkistetaan että seuraavalla kertaa tulee yhtä isompi
368      *   jasen.getTunnusNro() === n+20+1;
369      *     
370      * </pre>
371      */
372     public void parse(String rivi) {
373         StringBuffer sb = new StringBuffer(rivi);
374         for (int k = 0; k < getKenttia(); k++)
375             aseta(k, Mjonot.erota(sb, '|'));
376     }
377     
378     
379     /**
380      * Tehdään identtinen klooni jäsenestä
381      * @return Object kloonattu jäsen
382      * @example
383      * <pre name="test">
384      * #THROWS CloneNotSupportedException 
385      *   Jasen jasen = new Jasen();
386      *   jasen.parse("   3  |  Ankka Aku   | 123");
387      *   Jasen kopio = jasen.clone();
388      *   kopio.toString() === jasen.toString();
389      *   jasen.parse("   4  |  Ankka Tupu   | 123");
390      *   kopio.toString().equals(jasen.toString()) === false;
391      * </pre>
392      */
393     @Override
394     public Jasen clone() throws CloneNotSupportedException { // NOPMD
395         Jasen uusi;
396         uusi = (Jasen)super.clone();
397         uusi.kentat = kentat.clone();
398 
399         for (int k = 0; k < getKenttia(); k++)
400             uusi.kentat[k] = kentat[k].clone();
401         return uusi;
402     }
403 
404 
405     /**
406      * Tutkii onko jäsenen tiedot samat kuin parametrina tuodun jäsenen tiedot
407      * @param jasen jäsen johon verrataan
408      * @return true jos kaikki tiedot samat, false muuten
409      * @example
410      * <pre name="test">
411      *   Jasen jasen1 = new Jasen();
412      *   jasen1.parse("   3  |  Ankka Aku   | 030201-111C");
413      *   Jasen jasen2 = new Jasen();
414      *   jasen2.parse("   3  |  Ankka Aku   | 030201-111C");
415      *   Jasen jasen3 = new Jasen();
416      *   jasen3.parse("   3  |  Ankka Aku   | 030201-115H");
417      *   
418      *   jasen1.equals(jasen2) === true;
419      *   jasen2.equals(jasen1) === true;
420      *   jasen1.equals(jasen3) === false;
421      *   jasen3.equals(jasen2) === false;
422      * </pre>
423      */
424     public boolean equals(Jasen jasen) { 
425         for (int k = 0; k < getKenttia(); k++)
426             if (!anna(k).equals(jasen.anna(k))) return false;
427         return true;
428     }
429 
430     
431     /**
432      * Testiohjelma jäsenelle.
433      * @param args ei käytössä
434      */
435     public static void main(String args[]) {
436         Jasen aku = new Jasen(), aku2 = new Jasen();
437         aku.rekisteroi();
438         aku2.rekisteroi();
439         aku.tulosta(System.out);
440         aku.vastaaAkuAnkka();
441         aku.tulosta(System.out);
442 
443         aku2.vastaaAkuAnkka();
444         aku2.tulosta(System.out);
445 
446         aku2.vastaaAkuAnkka();
447         aku2.tulosta(System.out);
448     }
449 
450 }
451