| PerusTietueet.java |
1 package kanta;
2
3 import java.io.BufferedReader;
4 import java.io.File;
5 import java.io.IOException;
6 import java.io.PrintWriter;
7 import java.util.*;
8
9 import kanta.Tietue;
10 import fi.jyu.mit.ohj2.Tiedosto;
11 import fi.jyu.mit.ohj2.WildChars;
12
13 /**
14 * Rakenne tietueita varten.
15 * Osaa lukea ja kirjoittaa tiedoston.
16 * Osaa poistaa ja etsiä tietyn viitteen tietueet
17 *
18 * @author Vesa Lappalainen
19 * @version 1.0, 22.02.2003
20 * @version 2.0, 24.03.2012 - geneerinen versio
21 * @param <TYPE> Minkä tyyppisiä tietueita talletetaan
22 * @example
23 * <pre name="testJAVA">
24 * public static class Harrastus extends PerusTietue {
25 * private Kentta kentat[] = {
26 * new IntKentta("id"),
27 * new IntKentta("jäsenId"),
28 * new JonoKentta("ala"),
29 * new IntKentta("aloitusvuosi"),
30 * new IntKentta("h/vko")
31 * };
32 * private static int seuraavaNro = 1;
33 * public Harrastus() { }
34 * public Harrastus(int jnro) { ((IntKentta)(kentat[1])).setValue(jnro); }
35 * @Override public int ekaKentta() { return 2; }
36 * @Override public Kentta[] getKentat() { return kentat; }
37 * @Override public int getSeuraavaNro() { return seuraavaNro; }
38 * @Override protected void setSeuraavaNro(int i) { seuraavaNro = i; }
39 * @Override protected void setKentat(Kentta[] kentat) { this.kentat = kentat; }
40 * @Override public Harrastus clone() throws CloneNotSupportedException {return (Harrastus)super.clone(); }
41 * public int getJasenNro() { return ((IntKentta)getKentta(1)).getValue(); }
42 * public void vastaaPitsinNyplays(int nro) {
43 * aseta(1,""+nro); aseta(2,"Pitsin nypläys"); aseta(3,"1952"); aseta(4,"30");
44 * }
45 * }
46 */
47 public class PerusTietueet<TYPE extends Tietue> implements Tietueet<TYPE> {
48 private boolean muutettu = false;
49 private String tiedostonPerusNimi = "";
50 private TYPE malli;
51 private String tark = ".dat";
52 private String baktark = ".bak";
53
54 /** Taulukko tietueista */
55 private final List<TYPE> alkiot = new ArrayList<TYPE>();
56
57
58 /**
59 * Rakenteen alustaminen.
60 * @param malli malliolio, jonka kloonilla voi luoda uusia
61 */
62 public PerusTietueet(TYPE malli) {
63 this.malli = malli;
64 }
65
66
67 /**
68 * Rakenteen alustaminen.
69 * @param malli malliolio, jonka kloonilla voi luoda uusia
70 * @param tark tiedoston tarkennin
71 * @param baktark backuptiedoston tarkennin, jos null, ei tehdä
72 */
73 public PerusTietueet(TYPE malli,String tark, String baktark) {
74 this(malli);
75 this.tark = tark;
76 this.baktark = baktark;
77 }
78
79
80 /**
81 * Luodaan uusi talletettavan kaltainen olio.
82 * Tarvitaan tiedoston lukemisessa.
83 * @return uusi talletettavan tyyppinen alkio
84 */
85 @SuppressWarnings("unchecked")
86 @Override
87 public TYPE luoUusi() {
88 try {
89 Tietue uusi = malli.clone();
90 return (TYPE)uusi;
91 } catch (CloneNotSupportedException e) {
92 return null;
93 }
94 }
95
96
97 /**
98 * Lisää uuden tietueen tietorakenteeseen. Ottaa tietueen omistukseensa.
99 * @param har lisättävä harrastus. Huom tietorakenne muuttuu omistajaksi
100 */
101 @Override
102 public void lisaa(TYPE har) {
103 alkiot.add(har);
104 muutettu = true;
105 }
106
107
108 /**
109 * Korvaa tietueen tietorakenteessa. Ottaa tietueen omistukseensa.
110 * Etsitään samalla tunnusnumerolla oleva tietue. Jos ei löydy,
111 * niin lisätään uutena tietueena.
112 * @param tietue lisätäävän tietueen viite. Huom tietorakenne muuttuu omistajaksi
113 * @example
114 * <pre name="test">
115 * #THROWS CloneNotSupportedException
116 * #PACKAGEIMPORT
117 * PerusTietueet<Harrastus> harrastukset = new PerusTietueet(new Harrastus());
118 * Harrastus har1 = new Harrastus(), har2 = new Harrastus();
119 * har1.rekisteroi(); har2.rekisteroi();
120 * harrastukset.getLkm() === 0;
121 * harrastukset.korvaaTaiLisaa(har1); harrastukset.getLkm() === 1;
122 * harrastukset.korvaaTaiLisaa(har2); harrastukset.getLkm() === 2;
123 * Harrastus har3 = har1.clone();
124 * har3.aseta(2,"kkk");
125 * Iterator<Harrastus> i2=harrastukset.iterator();
126 * i2.next() === har1;
127 * harrastukset.korvaaTaiLisaa(har3); harrastukset.getLkm() === 2;
128 * i2=harrastukset.iterator();
129 * Harrastus h = i2.next();
130 * h === har3;
131 * h == har3 === true;
132 * h == har1 === false;
133 * </pre>
134 */
135 @Override
136 public void korvaaTaiLisaa(TYPE tietue) {
137 int id = tietue.getTunnusNro();
138 for (int i = 0; i < getLkm(); i++) {
139 if (alkiot.get(i).getTunnusNro() == id) {
140 alkiot.set(i, tietue);
141 muutettu = true;
142 return;
143 }
144 }
145 lisaa(tietue);
146 }
147
148
149 /**
150 * Poistaa valitun tietueen
151 * @param tietue poistettava tietue
152 * @return tosi jos löytyi poistettava tietue
153 * @example
154 * <pre name="test">
155 * #THROWS SailoException
156 * #import java.io.File;
157 * PerusTietueet<Harrastus> harrasteet = new PerusTietueet(new Harrastus());
158 * Harrastus pitsi21 = new Harrastus(); pitsi21.vastaaPitsinNyplays(2);
159 * Harrastus pitsi11 = new Harrastus(); pitsi11.vastaaPitsinNyplays(1);
160 * Harrastus pitsi22 = new Harrastus(); pitsi22.vastaaPitsinNyplays(2);
161 * Harrastus pitsi12 = new Harrastus(); pitsi12.vastaaPitsinNyplays(1);
162 * Harrastus pitsi23 = new Harrastus(); pitsi23.vastaaPitsinNyplays(2);
163 * harrasteet.lisaa(pitsi21);
164 * harrasteet.lisaa(pitsi11);
165 * harrasteet.lisaa(pitsi22);
166 * harrasteet.lisaa(pitsi12);
167 * harrasteet.poista(pitsi23) === false ; harrasteet.getLkm() === 4;
168 * harrasteet.poista(pitsi11) === true; harrasteet.getLkm() === 3;
169 * List<Harrastus> h = harrasteet.annaTietueet(1,1);
170 * h.size() === 1;
171 * h.get(0) === pitsi12;
172 * </pre>
173 */
174 @Override
175 public boolean poista(TYPE tietue) {
176 boolean ret = alkiot.remove(tietue);
177 if (ret) muutettu = true;
178 return ret;
179 }
180
181
182 /**
183 * Poistaa tietueen jolla on valittu tunnusnumero
184 * @param id poistettavan tietueen tunnusnumero
185 * @return 1 jos poistettiin, 0 jos ei löydy
186 * @example
187 * <pre name="test">
188 * PerusTietueet<Harrastus> harrastukset = new PerusTietueet(new Harrastus());
189 * Harrastus har1 = new Harrastus(), har2 = new Harrastus(), har3 = new Harrastus();
190 * har1.rekisteroi(); har2.rekisteroi(); har3.rekisteroi();
191 * int id1 = har1.getTunnusNro();
192 * harrastukset.lisaa(har1); harrastukset.lisaa(har2); harrastukset.lisaa(har3);
193 * harrastukset.poista(id1+1) === 1;
194 * harrastukset.poista(id1+1) === 0; harrastukset.getLkm() === 2;
195 * harrastukset.poista(id1) === 1; harrastukset.getLkm() === 1;
196 * harrastukset.poista(id1+3) === 0; harrastukset.getLkm() === 1;
197 * </pre>
198 */
199 @Override
200 public int poista(int id) {
201 return poista(id,0);
202 }
203
204
205 /**
206 * Poistaa kaikki tietyn viitteen tietueet PerusTietueet
207 * @param viiteNro viite siihen, mihin liittyvät tietueet poistetaan
208 * @return montako poistettiin
209 * @example
210 * <pre name="test">
211 * #THROWS SailoException
212 * #import java.io.File;
213 * PerusTietueet<Harrastus> harrasteet = new PerusTietueet(new Harrastus());
214 * Harrastus pitsi21 = new Harrastus(); pitsi21.vastaaPitsinNyplays(2);
215 * Harrastus pitsi11 = new Harrastus(); pitsi11.vastaaPitsinNyplays(1);
216 * Harrastus pitsi22 = new Harrastus(); pitsi22.vastaaPitsinNyplays(2);
217 * Harrastus pitsi12 = new Harrastus(); pitsi12.vastaaPitsinNyplays(1);
218 * Harrastus pitsi23 = new Harrastus(); pitsi23.vastaaPitsinNyplays(2);
219 * harrasteet.lisaa(pitsi21);
220 * harrasteet.lisaa(pitsi11);
221 * harrasteet.lisaa(pitsi22);
222 * harrasteet.lisaa(pitsi12);
223 * harrasteet.lisaa(pitsi23);
224 * harrasteet.poista(2,1) === 3; harrasteet.getLkm() === 2;
225 * harrasteet.poista(3,1) === 0; harrasteet.getLkm() === 2;
226 * List<Harrastus> h = harrasteet.annaTietueet(2,1);
227 * h.size() === 0;
228 * h = harrasteet.annaTietueet(1,1);
229 * h.get(0) === pitsi11;
230 * h.get(1) === pitsi12;
231 * </pre>
232 */
233 @Override
234 public int poista(int viiteNro, int k) {
235 int n = 0;
236 for (Iterator<TYPE> it = alkiot.iterator(); it.hasNext();) {
237 TYPE tietue = it.next();
238 if ( tietue.annaInt(k) == viiteNro) {
239 it.remove();
240 n++;
241 }
242 }
243 if (n > 0) muutettu = true;
244 return n;
245 }
246
247
248 /**
249 * Lukee PerusTietueet tiedostosta.
250 * @param tied tiedoston nimen alkuosa
251 * @throws SailoException jos lukeminen epäonnistuu
252 *
253 * @example
254 * <pre name="test">
255 * #THROWS SailoException
256 * #import java.io.File;
257 * PerusTietueet<Harrastus> harrasteet = new PerusTietueet(new Harrastus());
258 * Harrastus pitsi21 = new Harrastus(); pitsi21.vastaaPitsinNyplays(2);
259 * Harrastus pitsi11 = new Harrastus(); pitsi11.vastaaPitsinNyplays(1);
260 * Harrastus pitsi22 = new Harrastus(); pitsi22.vastaaPitsinNyplays(2);
261 * Harrastus pitsi12 = new Harrastus(); pitsi12.vastaaPitsinNyplays(1);
262 * Harrastus pitsi23 = new Harrastus(); pitsi23.vastaaPitsinNyplays(2);
263 * String tiedNimi = "testikelmit";
264 * File ftied = new File(tiedNimi+".dat");
265 * ftied.delete();
266 * harrasteet.lueTiedostosta(tiedNimi); #THROWS SailoException
267 * harrasteet.lisaa(pitsi21);
268 * harrasteet.lisaa(pitsi11);
269 * harrasteet.lisaa(pitsi22);
270 * harrasteet.lisaa(pitsi12);
271 * harrasteet.lisaa(pitsi23);
272 * harrasteet.talleta();
273 * harrasteet = new PerusTietueet(new Harrastus());
274 * harrasteet.lueTiedostosta(tiedNimi);
275 * Iterator<Harrastus> i = harrasteet.iterator();
276 * i.next().toString() === pitsi21.toString();
277 * i.next().toString() === pitsi11.toString();
278 * i.next().toString() === pitsi22.toString();
279 * i.next().toString() === pitsi12.toString();
280 * i.next().toString() === pitsi23.toString();
281 * i.hasNext() === false;
282 * harrasteet.lisaa(pitsi23);
283 * harrasteet.talleta();
284 * ftied.delete() === true;
285 * File fbak = new File(tiedNimi+".bak");
286 * fbak.delete() === true;
287 * </pre>
288 */
289 @Override
290 public void lueTiedostosta(String tied) throws SailoException {
291 muutettu = true;
292 setTiedostonPerusNimi(tied);
293 BufferedReader fi = Tiedosto.avaa_lukemista_varten(getTiedostonNimi());
294 if (fi == null) throw new SailoException("Tiedosto " + getTiedostonNimi() + " ei aukea");
295
296 String rivi;
297 try {
298 lueAlkurivit(fi);
299 while ((rivi = fi.readLine()) != null) {
300 rivi = rivi.trim();
301 if ("".equals(rivi) || rivi.charAt(0) == ';') continue;
302 TYPE tietue = luoUusi();
303 /// TYPE tietue = new TYPE(); /// nyyh
304 tietue.parse(rivi); // voisi olla virhekäsittely
305 lisaa(tietue);
306 }
307 muutettu = false;
308
309 } catch (IOException e) {
310 throw new SailoException("Ongelmia tiedoston kanssa: " + e.getMessage());
311 } finally {
312 try {
313 fi.close();
314 } catch (IOException e) {
315 throw new SailoException("Tiedoston sulkeminen ei onnistu: " + e.getMessage());
316 }
317 }
318 }
319
320
321 /**
322 * Lukee rivit tiedoston alusta
323 * @param fi virta josta luetaan
324 * @throws IOException jos jokin menee vikaan
325 * @throws SailoException jos tieto virheellistä
326 */
327 protected void lueAlkurivit(BufferedReader fi) throws IOException, SailoException {
328 //
329 }
330
331
332 /**
333 * Tiedoston alkuun tulevat rivit
334 * @param fo virta johon talletetaan
335 */
336 protected void teeAlkutalletus(PrintWriter fo) {
337 //
338 }
339
340
341 /**
342 * Tallentaa PerusTietueet tiedostoon.
343 * @throws SailoException jos talletus epäonnistuu
344 */
345 @Override
346 public void talleta() throws SailoException {
347 if (!muutettu) return;
348
349 File ftied = new File(getTiedostonNimi());
350 if ( baktark != null ) {
351 File fbak = new File(getBakNimi());
352 fbak.delete(); // if ... System.err.println("Ei voi tuhota");
353 ftied.renameTo(fbak); // if ... System.err.println("Ei voi nimetä");
354 }
355
356 PrintWriter fo = Tiedosto.avaa_kirjoittamista_varten(ftied.getName());
357 if (fo == null) throw new SailoException("Tiedosto " + ftied.getName() + " ei aukea");
358
359 try {
360 teeAlkutalletus(fo);
361 for (TYPE tietue : this) {
362 fo.println(tietue.toString());
363 }
364 } finally {
365 fo.close();
366 }
367
368 muutettu = false;
369 }
370
371
372 /**
373 * Palauttaa tietueiden lukumäärän
374 * @return harrastusten lukumäärä
375 */
376 @Override
377 public int getLkm() {
378 return alkiot.size();
379 }
380
381
382 /**
383 * Asettaa tiedoston perusnimen ilman tarkenninta
384 * @param tied tallennustiedoston perusnimi
385 */
386 @Override
387 public void setTiedostonPerusNimi(String tied) {
388 tiedostonPerusNimi = tied;
389 }
390
391
392 /**
393 * Palauttaa tiedoston nimen, jota käytetään tallennukseen
394 * @return tallennustiedoston nimi
395 */
396 @Override
397 public String getTiedostonPerusNimi() {
398 return tiedostonPerusNimi;
399 }
400
401
402 /**
403 * Palauttaa tiedoston nimen, jota käytetään tallennukseen
404 * @return tallennustiedoston nimi
405 */
406 @Override
407 public String getTiedostonNimi() {
408 return getTiedostonPerusNimi() + getTark();
409 }
410
411
412 /**
413 * Palauttaa varakopiotiedoston nimen
414 * @return varakopiotiedoston nimi
415 */
416 @Override
417 public String getBakNimi() {
418 return getTiedostonPerusNimi() + getBaktark();
419 }
420
421
422 /**
423 * Iteraattori kaikkien harrastusten läpikäymiseen
424 * @return harrastusiteraattori
425 *
426 * @example
427 * <pre name="test">
428 * #PACKAGEIMPORT
429 * #import java.util.*;
430 *
431 * PerusTietueet<Harrastus> harrasteet = new PerusTietueet(new Harrastus());
432 * Harrastus pitsi21 = new Harrastus(2); harrasteet.lisaa(pitsi21);
433 * Harrastus pitsi11 = new Harrastus(1); harrasteet.lisaa(pitsi11);
434 * Harrastus pitsi22 = new Harrastus(2); harrasteet.lisaa(pitsi22);
435 * Harrastus pitsi12 = new Harrastus(1); harrasteet.lisaa(pitsi12);
436 * Harrastus pitsi23 = new Harrastus(2); harrasteet.lisaa(pitsi23);
437 *
438 * Iterator<Harrastus> i2=harrasteet.iterator();
439 * i2.next() === pitsi21;
440 * i2.next() === pitsi11;
441 * i2.next() === pitsi22;
442 * i2.next() === pitsi12;
443 * i2.next() === pitsi23;
444 * i2.next() === pitsi12; #THROWS NoSuchElementException
445 *
446 * int n = 0;
447 * int jnrot[] = {2,1,2,1,2};
448 *
449 * for ( Harrastus har:harrasteet ) {
450 * har.getJasenNro() === jnrot[n]; n++;
451 * }
452 *
453 * n === 5;
454 *
455 * </pre>
456 */
457 @Override
458 public Iterator<TYPE> iterator() {
459 return alkiot.iterator();
460 }
461
462
463 /**
464 * Haetaan kaikki tietyn viitteen tietueet
465 * @param viiteNro etsittävän viitteen tunnusnumero jolle tietueita haetaan
466 * @param k etsittävän kentän numero
467 * @return tietorakenne jossa viiteet löydetteyihin tietueisiin
468 * @example
469 * <pre name="test">
470 * #import java.util.*;
471 *
472 * PerusTietueet<Harrastus> harrasteet = new PerusTietueet(new Harrastus());
473 * Harrastus pitsi21 = new Harrastus(2); harrasteet.lisaa(pitsi21);
474 * Harrastus pitsi11 = new Harrastus(1); harrasteet.lisaa(pitsi11);
475 * Harrastus pitsi22 = new Harrastus(2); harrasteet.lisaa(pitsi22);
476 * Harrastus pitsi12 = new Harrastus(1); harrasteet.lisaa(pitsi12);
477 * Harrastus pitsi23 = new Harrastus(2); harrasteet.lisaa(pitsi23);
478 * Harrastus pitsi51 = new Harrastus(5); harrasteet.lisaa(pitsi51);
479 *
480 * List<Harrastus> loytyneet;
481 * loytyneet = harrasteet.annaTietueet(3,1);
482 * loytyneet.size() === 0;
483 * loytyneet = harrasteet.annaTietueet(1,1);
484 * loytyneet.size() === 2;
485 * loytyneet.get(0) == pitsi11 === true;
486 * loytyneet.get(1) == pitsi12 === true;
487 * loytyneet = harrasteet.annaTietueet(5,1);
488 * loytyneet.size() === 1;
489 * loytyneet.get(0) == pitsi51 === true;
490 * </pre>
491 */
492 @Override
493 public List<TYPE> annaTietueet(int viiteNro, int k) {
494 List<TYPE> loydetyt = new ArrayList<TYPE>();
495 for (TYPE tietue : this)
496 if ( tietue.annaInt(k) == viiteNro) loydetyt.add(tietue);
497 return loydetyt;
498 }
499
500
501 /**
502 * Palauttaa "taulukossa" hakuehtoon vastaavien tietueiden viitteet
503 * @param hakuehto hakuehto
504 * @param k etsittävän kentän indeksi
505 * @return tietorakenteen löytyneistä tietueista
506 * @example
507 * <pre name="test">
508 * #THROWS SailoException
509 * PerusTietueet<Harrastus> harrastukset = new PerusTietueet(new Harrastus());
510 * Harrastus har1 = new Harrastus(); har1.parse("1|2|Possujen jahtaaminen|1940|22");
511 * Harrastus har2 = new Harrastus(); har2.parse("2|1|Poikien hoitaminen|1953|1");
512 * Harrastus har3 = new Harrastus(); har3.parse("3|2|Kelmien kerho|1948|20");
513 * Harrastus har4 = new Harrastus(); har4.parse("4|1|Kalastus|1947|20");
514 * Harrastus har5 = new Harrastus(); har5.parse("5|3|Viulu|1952|15");
515 * harrastukset.lisaa(har1); harrastukset.lisaa(har2); harrastukset.lisaa(har3);
516 * harrastukset.lisaa(har4); harrastukset.lisaa(har5);
517 * List<Harrastus> loytyneet;
518 * loytyneet = harrastukset.etsi("*s*",2);
519 * loytyneet.size() === 2;
520 * loytyneet.get(0) == har4 === true;
521 * loytyneet.get(1) == har1 === true;
522 *
523 * loytyneet = harrastukset.etsi("*5*",3);
524 * loytyneet.size() === 2;
525 * loytyneet.get(0) == har5 === true;
526 * loytyneet.get(1) == har2 === true;
527 * </pre>
528 */
529 @Override
530 public List<TYPE> etsi(String hakuehto, int k) {
531 List<TYPE> loytyneet = new ArrayList<TYPE>();
532 for (TYPE jasen : this) {
533 if (WildChars.onkoSamat(jasen.anna(k), hakuehto)) loytyneet.add(jasen);
534 }
535 Collections.sort(loytyneet, new TietueVertailija(k));
536 return loytyneet;
537 }
538
539
540 /**
541 * Etsii tietueen id:n perusteella
542 * @param id tunnusnumero, jonka mukaan etsitään
543 * @return tietue jolla etsittävä id tai null
544 * @example
545 * <pre name="test">
546 * PerusTietueet<Harrastus> harrastukset = new PerusTietueet(new Harrastus());
547 * Harrastus har1 = new Harrastus(); har1.parse("1|2|Possujen jahtaaminen|1940|22");
548 * Harrastus har2 = new Harrastus(); har2.parse("2|1|Poikien hoitaminen|1953|1");
549 * Harrastus har3 = new Harrastus(); har3.parse("3|2|Kelmien kerho|1948|20");
550 * harrastukset.lisaa(har1); harrastukset.lisaa(har2); harrastukset.lisaa(har3);
551 * harrastukset.annaId(1) == har1 === true;
552 * harrastukset.annaId(2) == har2 === true;
553 * harrastukset.annaId(3) == har3 === true;
554 * </pre>
555 */
556 @Override
557 public TYPE annaId(int id) {
558 for (TYPE tietue : this) {
559 if (id == tietue.getTunnusNro()) return tietue;
560 }
561 return null;
562 }
563
564
565 /**
566 * Laitetaan muutos, jolloin pakotetaan tallentamaan.
567 */
568 @Override
569 public void setMuutos() {
570 muutettu = true;
571 }
572
573
574 /**
575 * @return tiedoston tarkennin
576 */
577 public String getTark() {
578 return tark;
579 }
580
581
582 /**
583 * @param tark tiedoston tarkennin
584 */
585 public void setTark(String tark) {
586 this.tark = tark;
587 }
588
589
590 /**
591 * @return backup tiedostojen tarkennin
592 */
593 public String getBaktark() {
594 return baktark;
595 }
596
597
598 /**
599 * @param baktark backup tiedostojen tarkennin
600 */
601 public void setBaktark(String baktark) {
602 this.baktark = baktark;
603 }
604 }
605