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