1   package kanta;
2   
3   import java.io.*;
4   
5   import fi.jyu.mit.ohj2.Mjonot;
6   
7   /**
8    * Tietue joka osaa mm. itse huolehtia tunnus_nro:staan.
9    * Oikean tietueen tekemiseksi tämä on perittävä ja lisättävä mm.
10   * Kentta-taulukko. 0's kenttä on aina id-numero.
11   *
12   * @author Vesa Lappalainen
13   * @version 1.0, 22.02.2003
14   * @version 1.1, 23.02.2012
15   * @example
16   * <pre name="testJAVA">
17   * public static class Harrastus extends PerusTietue {
18   *     private Kentta kentat[] = { 
19   *        new IntKentta("id"),
20   *        new IntKentta("jäsenId"),
21   *        new JonoKentta("ala"),
22   *        new IntKentta("aloitusvuosi"),
23   *        new IntKentta("h/vko")
24   *     };
25   *     private static int seuraavaNro = 1;
26   *     @Override public int ekaKentta() { return 2; }
27   *     @Override public Kentta[] getKentat() { return kentat; }
28   *     @Override public int getSeuraavaNro() { return seuraavaNro; }
29   *     @Override protected void setSeuraavaNro(int i) { seuraavaNro = i; }
30   *     @Override protected void setKentat(Kentta[] kentat) { this.kentat = kentat; }
31   *     @Override public Harrastus clone() throws CloneNotSupportedException {return (Harrastus)super.clone(); }
32   *     public int getJasenNro() { return ((IntKentta)getKentta(1)).getValue(); }
33   * }    
34   * </pre>
35   */
36  public abstract class PerusTietue implements Cloneable,Tietue  {
37     
38      /**
39       * @param i seuraava id-numero jota tästä lähiten käytetään
40       */
41      abstract protected void setSeuraavaNro(int i);
42      
43      
44      /**
45       * Asetetaan kentät.  Käytetään lähinnä kloonaamisessa
46       * @param kentat uudet kentät tietueelle
47       */
48      abstract protected void setKentat(Kentta[] kentat);
49  
50      
51      /**
52       * @return tietueen kenttien lukumäärä
53       * @example
54       * <pre name="test">
55       *   Harrastus har = new Harrastus();
56       *   har.getKenttia() === 5;
57       * </pre>
58       */
59      @Override
60      public int getKenttia() {
61          return getKentat().length;
62      }
63  
64  
65      /**
66       * @return ensimmäinen käyttäjän syötettävän kentän indeksi
67       * @example
68       * <pre name="test">
69       *   Harrastus har = new Harrastus();
70       *   har.ekaKentta() === 2;
71       * </pre>
72       */
73      @Override
74      public int ekaKentta() {
75          return 1;
76      }
77  
78  
79      /**
80       * @param k Mikä kenttä halutaan
81       * @return k.s kenttä
82       * @example
83       * <pre name="test">
84       *   Harrastus har = new Harrastus();
85       *   har.parse("   2   |  10  |   Kalastus  | 1949 | 22 t ");
86       *   IntKentta kentta = (IntKentta)har.getKentta(3);   
87       *   kentta.getValue() === 1949;   
88       * </pre>
89       */
90      @Override
91      public Kentta getKentta(int k) {
92          return getKentat()[k];
93      }
94  
95      
96      /**
97       * @param k minkä kentän kysymys halutaan
98       * @return valitun kentän kysymysteksti
99       * @example
100      * <pre name="test">
101      *   Harrastus har = new Harrastus();
102      *   har.getKysymys(2) === "ala";
103      * </pre>
104      */
105     @Override
106     public String getKysymys(int k) {
107         try {
108             return getKentta(k).getKysymys();
109         } catch (Exception ex) {
110             return "Ääliö";
111         }
112     }
113 
114 
115     /**
116      * @return kaikkien näytettävien kysymysten otsikot merkkijonotaulukkona
117      * @example
118      * <pre name="test">
119      * #import java.util.Arrays;
120      *   Harrastus har = new Harrastus();
121      *   Arrays.toString(har.getOtsikot()) =R= "\\[ala, aloitusvuosi, h/vko.*";
122      * </pre>
123      */
124     @Override
125     public String[] getOtsikot() {
126         int n = getKenttia() - ekaKentta();
127         String[] otsikot = new String[n];
128         for (int i=0,k=ekaKentta(); i<n; i++,k++) 
129             otsikot[i] = getKysymys(k);
130         return otsikot;        
131     }
132     
133     
134     /**
135      * @param k Minkä kentän sisältö halutaan
136      * @return valitun kentän sisältö
137      * @example
138      * <pre name="test">
139      *   Harrastus har = new Harrastus();
140      *   har.parse("   2   |  10  |   Kalastus  | 1949 | 22 t ");
141      *   har.anna(0) === "2";   
142      *   har.anna(1) === "10";   
143      *   har.anna(2) === "Kalastus";   
144      *   har.anna(3) === "1949";   
145      *   har.anna(4) === "22";   
146      * </pre>
147      */
148     @Override
149     public String anna(int k) {
150         try {
151             return getKentta(k).toString();
152         } catch (Exception ex) {
153             return "";
154         }
155     }
156 
157 
158     /**
159      * Kentän sisältö kokonaislukuna.  Jos ei ole IntKentta,
160      * niin -1.
161      * @param k Minkä kentän sisältö halutaan
162      * @return valitun kentän sisältö kokonaislukuna.  
163      * @example
164      * <pre name="test">
165      *   Harrastus har = new Harrastus();
166      *   har.parse("   2   |  10  |   Kalastus  | 1949 | 22 t ");
167      *   har.annaInt(0) === 2;   
168      *   har.annaInt(1) === 10;   
169      *   har.annaInt(2) === -1;   
170      *   har.annaInt(3) === 1949;   
171      *   har.annaInt(4) === 22;   
172      * </pre>
173      */
174     @Override
175     public int annaInt(int k) {
176         Kentta kentta = getKentta(k);
177         if ( !(kentta instanceof IntKentta) ) return -1;
178         return ((IntKentta)kentta).getValue();
179     }
180     
181     
182     
183     /**
184      * Asetetaan valitun kentän sisältö.  Mikäli asettaminen onnistuu,
185      * palautetaan null, muutoin virheteksti.
186      * @param k minkä kentän sisältö asetetaan
187      * @param s asetettava sisältö merkkijonona
188      * @return null jos ok, muuten virheteksti
189      * @example
190      * <pre name="test">
191      *   Harrastus har = new Harrastus();
192      *   har.aseta(3,"kissa") === "aloitusvuosi: Ei kokonaisluku (kissa)";
193      *   har.aseta(3,"1940")  === null;
194      *   har.aseta(4,"kissa") === "h/vko: Ei kokonaisluku (kissa)";
195      *   har.aseta(4,"20")    === null;
196      * </pre>
197      */
198     @Override
199     public String aseta(int k, String s) {
200         try {
201             String virhe = getKentta(k).aseta(s.trim());
202             if ( virhe == null && k == 0 ) setTunnusNro(getTunnusNro());
203             if ( virhe == null ) return virhe;
204             return getKysymys(k) +": " + virhe;
205         } catch (Exception ex) {
206             return "Virhe: " + ex.getMessage();
207         }
208     }
209 
210 
211     /**
212      * Tehdään identtinen klooni tietueesta
213      * @return Object kloonattu tietue
214      * @example
215      * <pre name="test">
216      * #THROWS CloneNotSupportedException 
217      *   Harrastus har = new Harrastus();
218      *   har.parse("   2   |  10  |   Kalastus  | 1949 | 22 t ");
219      *   Harrastus kopio = har.clone();
220      *   kopio.toString() === har.toString();
221      *   har.parse("   1   |  11  |   Uinti  | 1949 | 22 t ");
222      *   kopio.toString().equals(har.toString()) === false;
223      *   kopio instanceof Harrastus === true;
224      * </pre>
225      */
226     @Override
227     public Tietue clone() throws CloneNotSupportedException { // NOPMD
228         PerusTietue uusi;
229         uusi = (PerusTietue)super.clone();
230         uusi.setKentat(getKentat().clone());
231 
232         for (int k = 0; k < getKenttia(); k++)
233             uusi.getKentat()[k] = getKentta(k).clone();
234         return uusi;
235     }
236     
237 
238     /**
239      * Tulostetaan tietueen tiedot
240      * @param out tietovirta johon tulostetaan
241      */
242     @Override
243     public void tulosta(PrintWriter out) {
244         String erotin = "";
245         for (int k=ekaKentta(); k<getKenttia(); k++) {
246             out.print(erotin + anna(k));
247             erotin = " ";
248         }
249         out.println();
250     }
251 
252 
253     /**
254      * Tulostetaan tietueen tiedot
255      * @param os tietovirta johon tulostetaan
256      */
257     @Override
258     public void tulosta(OutputStream os) {
259         tulosta(new PrintStream(os));
260     }
261     
262 
263     /**
264      * Tulostetaan tietueen tiedot
265      * @param out tietovirta johon tulostetaan
266      */
267     @Override
268     public void tulosta(PrintStream out) {
269         tulosta(new PrintWriter(out, true)); // ilman autoflushia ei mitään
270                                              // tulostu!
271     }
272 
273     
274     /**
275      * Antaa tietueelle seuraavan rekisterinumeron
276      * @return tietueen uusi tunnus_nro
277      * @example
278      * <pre name="test">
279      *   Harrastus pitsi1 = new Harrastus();
280      *   pitsi1.getTunnusNro() === 0;
281      *   pitsi1.rekisteroi();
282      *   int n1 = pitsi1.getTunnusNro();
283      *   Harrastus pitsi2 = new Harrastus();
284      *   pitsi2.rekisteroi() === n1 + 1;
285      *   int n2 = pitsi2.getTunnusNro();
286      *   n1 === n2-1;
287      * </pre>
288      */
289     @Override
290     public int rekisteroi() {
291         return setTunnusNro(getSeuraavaNro());
292     }
293 
294 
295     /**
296      * Palautetaan tietueen oma id
297      * @return tietueen id
298      * @example
299      * <pre name="test">
300      *   Harrastus harrastus = new Harrastus();
301      *   harrastus.parse("   2   |  10  |   Kalastus  | 1949 | 22 t ");
302      *   harrastus.getTunnusNro() === 2;
303      * </pre>
304      */
305     @Override
306     public int getTunnusNro() {
307         return ((IntKentta)(getKentta(0))).getValue();
308     }
309 
310 
311     /**
312      * Asettaa tunnusnumeron ja samalla varmistaa että
313      * seuraava numero on aina suurempi kuin tähän mennessä suurin.
314      * @param nr asetettava tunnusnumero
315      */
316     private int setTunnusNro(int nr) {
317         IntKentta k = ((IntKentta)(getKentta(0)));
318         k.setValue(nr);
319         if (nr >= getSeuraavaNro()) setSeuraavaNro(nr + 1);
320         return k.getValue();
321     }
322     
323 
324     /**
325      * Palauttaa tietueen tiedot merkkijonona jonka voi tallentaa tiedostoon.
326      * @return tietue tolppaeroteltuna merkkijonona 
327      * @example
328      * <pre name="test">
329      *   Harrastus harrastus = new Harrastus();
330      *   harrastus.parse("   2   |  10  |   Kalastus  | 1949 | 22 t ");
331      *   harrastus.toString()    =R= "2\\|10\\|Kalastus\\|1949\\|22.*";
332      * </pre>
333      */
334     @Override
335     public String toString() {
336         StringBuffer sb = new StringBuffer("");
337         String erotin = "";
338         for (int k = 0; k < getKenttia(); k++) {
339             sb.append(erotin);
340             sb.append(anna(k));
341             erotin = "|";
342         }
343         return sb.toString();
344      }
345 
346 
347     /**
348      * Selvittää tietueen tiedot | erotellusta merkkijonosta.
349      * Pitää huolen että seuraavaNro on suurempi kuin tuleva tunnusnro.
350      * @param rivi josta harrastuksen tiedot otetaan
351      * @example
352      * <pre name="test">
353      *   Harrastus harrastus = new Harrastus();
354      *   harrastus.parse("   2   |  10  |   Kalastus  | 1949 | 22 t ");
355      *   harrastus.getJasenNro() === 10;
356      *   harrastus.toString()     =R= "2\\|10\\|Kalastus\\|1949\\|22.*";
357      *   
358      *   harrastus.rekisteroi();
359      *   int n = harrastus.getTunnusNro();
360      *   harrastus.parse(""+(n+20));
361      *   harrastus.rekisteroi();
362      *   harrastus.getTunnusNro() === n+20+1;
363      *   harrastus.toString()     =R= "" + (n+20+1) + "\\|10\\|\\|1949\\|22.*";
364      * </pre>
365      */
366     @Override
367     public void parse(String rivi) {
368         StringBuffer sb = new StringBuffer(rivi);
369         for (int k = 0; k < getKenttia(); k++)
370             aseta(k, Mjonot.erota(sb, '|'));
371     }
372 
373     
374     /**
375      * Antaa k:n kentän sisällön avain-merkkijonona
376      * jonka perusteella voi lajitella
377      * @param k monenenko kentän sisältö palautetaan
378      * @return kentän sisältö merkkijonona
379      *
380      * @example
381      * <pre name="test">
382      *   Harrastus har = new Harrastus();
383      *   har.parse("   2   |  10  |   Kalastus  | 1949 | 22 t ");
384      *   har.getAvain(0) === "         2";
385      *   har.getAvain(1) === "        10";
386      *   har.getAvain(2) === "KALASTUS";
387      *   har.getAvain(20) === "";
388      * </pre>
389      */
390     @Override
391     public String getAvain(int k) {
392         try {
393             return getKentta(k).getAvain();
394         } catch (Exception ex) {
395             return "";
396         }
397     }
398 
399 
400     /**
401      * Tutkii onko tietueen tiedot samat kuin parametrina tuodun tietueen tiedot
402      * @param tietue johon verrataan
403      * @return true jos kaikki tiedot samat, false muuten
404      * @example
405      * <pre name="test">
406      *   Harrastus har1 = new Harrastus();
407      *   har1.parse("   2   |  10  |   Kalastus  | 1949 | 22 t ");
408      *   Harrastus har2 = new Harrastus();
409      *   har2.parse("   2   |  10  |   Kalastus  | 1949 | 22 t ");
410      *   Harrastus har3 = new Harrastus();
411      *   har3.parse("   3   |  12  |   Kalastus  | 1949 | 22 t ");
412      *   
413      *   
414      *   har1.equals(har2) === true;
415      *   har2.equals(har1) === true;
416      *   har1.equals(har3) === false;
417      *   har3.equals(har2) === false;
418      * </pre>
419      */
420     @Override
421     public boolean equals(Tietue tietue) {
422         if ( getKenttia() != tietue.getKenttia() ) return false;
423         for (int k = 0; k < getKenttia(); k++)
424             if (!anna(k).equals(tietue.anna(k))) return false;
425         return true;
426     }
427 
428 }
429