Erottelija.java |
1 package fi.jyu.mit.ohj2; 2 3 import java.util.Enumeration; 4 5 /** 6 * Luokka StringTokenizerin korvaajaksi. Erona 7 * on se että jonon loppuessa ei tule ongelmia ja 8 * peräkkäisten erottimien välistä tulee tyhjä jono. 9 * <pre> 10 * Esimerkki: 11 * public static void main(String[] args) { 12 * Erottelija erottaja = new Erottelija("12;3.5:kissa,,,istuu puussa,3.4", 13 * ";:,"); 14 * System.out.println("Palasia: " + erottaja.countTokens()); 15 * for (int i=1; erottaja.hasMoreTokens(); i++ ) 16 * System.out.println(i + ": |" + erottaja.nextToken()+"|"); 17 * System.err.println("8: |"+erottaja.nextToken()+"|"); 18 * erottaja.reset(); 19 * System.out.println(erottaja.nextToken(0)); 20 * System.out.println(erottaja.countRemaininTokens()); 21 * System.out.println(erottaja.rest()); 22 * System.out.println(erottaja.nextToken(0.0)); 23 * System.out.println(erottaja.nextToken(2)); 24 * System.out.println(erottaja.nextToken(2.1)); 25 * System.out.println(erottaja.countRemainingTokens()); 26 * System.out.println(erottaja.rest()); 27 * } 28 * 29 * Tulostaa: 30 * 31 * Palasia: 7 32 * 1: |12| 33 * 2: |3.5| 34 * 3: |kissa| 35 * 4: || 36 * 5: || 37 * 6: |istuu puussa| 38 * 7: |3.4| 39 * 8: || 40 * 12 41 * 6 42 * 3.5:kissa,,,istuu puussa,3.4 43 * 3 44 * ,istuu puussa,3.4 45 * 12 46 * 3.5 47 * 2 48 * 2.1 49 * </pre> 50 * @author vesal 51 * @version 11.3.2007 52 * 53 * @example 54 * <pre name="testErottelija"> 55 * Erottelija erottaja = new Erottelija("12;3.5:kissa,,,istuu puussa,3.4",";:,"); 56 * erottaja.nextToken() === $s; erottaja.hasMoreTokens() === $tokens; 57 * erottaja.countRemainingTokens() === $n; erottaja.rest() === $rest; 58 * 59 * $i | $s | $tokens | $n | $rest 60 * -------------------------------------------------------------------------- 61 * 0 | --- | true | 7 | "12;3.5:kissa,,,istuu puussa,3.4" 62 * 1 | "12" | true | 6 | "3.5:kissa,,,istuu puussa,3.4" 63 * 2 | "3.5" | true | 5 | "kissa,,,istuu puussa,3.4" 64 * 3 | "kissa" | true | 4 | ",,istuu puussa,3.4" 65 * 4 | "" | true | 3 | ",istuu puussa,3.4" 66 * 5 | "" | true | 2 | "istuu puussa,3.4" 67 * 6 | "istuu puussa" | true | 1 | "3.4" 68 * 7 | "3.4" | false | 0 | "" 69 * 8 | "" | false | 0 | "" 70 * 9 | "" | false | 0 | "" 71 * 72 * erottaja.nextToken(";","kissa") === "kissa"; 73 * 74 * @example 75 * </pre> 76 * <pre name="testErottelijaEnd"> 77 * Erottelija erottaja = new Erottelija("12;3.5:kissa,,,istuu puussa,3.4;;",";:,"); 78 * erottaja.nextToken() === $s; erottaja.hasMoreTokens() === $tokens; 79 * erottaja.countRemainingTokens() === $n; erottaja.rest() === $rest; 80 * 81 * $i | $s | $tokens | $n | $rest 82 * -------------------------------------------------------------------------- 83 * 0 | --- | true | 9 | "12;3.5:kissa,,,istuu puussa,3.4;;" 84 * 1 | "12" | true | 8 | "3.5:kissa,,,istuu puussa,3.4;;" 85 * 2 | "3.5" | true | 7 | "kissa,,,istuu puussa,3.4;;" 86 * 3 | "kissa" | true | 6 | ",,istuu puussa,3.4;;" 87 * 4 | "" | true | 5 | ",istuu puussa,3.4;;" 88 * 5 | "" | true | 4 | "istuu puussa,3.4;;" 89 * 6 | "istuu puussa" | true | 3 | "3.4;;" 90 * 7 | "3.4" | true | 2 | ";" 91 * 8 | "" | true | 1 | "" 92 * 9 | "" | false | 0 | "" 93 * 10 | "" | false | 0 | "" 94 * 95 * erottaja.nextToken(";","kissa") === "kissa"; 96 * </pre> 97 */ 98 public class Erottelija implements Enumeration<String> { // NOPMD - enum ok 99 100 /** 101 * Etsitään mistä kohti jonosta str löytyy ensimmäinen erotinmerkki 102 * joukosta delim. Etsintä aloitetaan paikasta pos. 103 * @param str mistä jonosta etsitään 104 * @param delim joukko erotinmerkkejä 105 * @param pos paikka josta aloitetaan 106 * @return palauttaa ensimmäisen esiintymän tai -1 jos ei löydy 107 * @example 108 * <pre name="test"> 109 * indexOfAny("a;, b",",; ",0) === 1 110 * indexOfAny("a;, b",",; ",2) === 2 111 * indexOfAny("a;, b"," ",0) === 3 112 * indexOfAny("a;, b",".",0) === -1 113 * indexOfAny(null,",; ",0) === -1 114 * indexOfAny("a b",",; ",-1) === 1 115 * </pre> 116 */ 117 public static int indexOfAny(String str, String delim, int pos) { 118 int i = pos; 119 if (i < 0) 120 i = 0; 121 if (str == null || delim == null) 122 return -1; 123 for (; i < str.length(); i++) { 124 char c = str.charAt(i); 125 if (delim.indexOf(c) >= 0) 126 return i; 127 } 128 return -1; 129 } 130 131 132 /** 133 * Etsitään mistä kohti jonosta str löytyy ensimmäinen erotinmerkki 134 * joukosta delim. Etsintä aloitetaan alusta. 135 * @param str mistä jonosta etsitään 136 * @param delim joukko erotinmerkkejä 137 * @return palauttaa ensimmäisen esiintymän tai -1 jos ei löydy 138 * @example 139 * <pre name="test"> 140 * indexOfAny("a;, b",",; ") === 1 141 * </pre> 142 */ 143 public static int indexOfAny(String str, String delim) { 144 return indexOfAny(str, delim, 0); 145 } 146 147 private String originalString; 148 private final String delimiters; 149 private int startPos = 0; 150 151 152 /** 153 * Luodaan erottelija, joka erottelee jonosta str palasia minkä tahansa 154 * joukosta delim löytyvän merkin kohdalta. 155 * @param str jono josta erotellaan 156 * @param delim erottavien merkkien joukko 157 * @example 158 * <pre name="test"> 159 * //01234 160 * Erottelija erottaja = new Erottelija("a b "," "); 161 * erottaja.countTokens() === 3; 162 * erottaja = new Erottelija("a b ",","); 163 * erottaja.countTokens() === 1; 164 * </pre> 165 */ 166 public Erottelija(String str, String delim) { 167 this.originalString = str; 168 this.delimiters = delim; 169 } 170 171 172 /** 173 * Luodaan erottelija, joka erottelee jonosta str palasia välilyönnin 174 * kohdalta. 175 * @param str jono josta erotellaan 176 * @example 177 * <pre name="test"> 178 * //01234 179 * Erottelija erottaja = new Erottelija("a b "); 180 * erottaja.countTokens() === 3; 181 * </pre> 182 */ 183 public Erottelija(String str) { 184 this.originalString = str; 185 this.delimiters = " "; 186 } 187 188 189 /** 190 * Palauttaa seuraavan palasen jonosta 191 * @return jonon seuraava palanen 192 * @example 193 * <pre name="test"> 194 * Erottelija erottaja = new Erottelija("a;b;",";"); 195 * erottaja.nextToken() === $s; erottaja.hasMoreTokens() === $tokens; 196 * erottaja.countRemainingTokens() === $n; erottaja.rest() === $rest; 197 * 198 * $i | $s | $tokens | $n | $rest 199 * -------------------------------------------------------------------------- 200 * 0 | --- | true | 3 | "a;b;" 201 * 1 | "a" | true | 2 | "b;" 202 * 2 | "b" | true | 1 | "" 203 * 3 | "" | false | 0 | "" 204 * 4 | "" | false | 0 | "" 205 * </pre> 206 */ 207 public String nextToken() { 208 return nextToken(delimiters); 209 } 210 211 212 /** 213 * Tarkistaa onko erotinmerkkiä juuri paikan vasemmalla puolella 214 * @param pos paikka josta tutkitaan 215 * @param delim kelpaavat erotinmerkit 216 * @return true jos on erotin merkki vasemmalla puolella, false muuten 217 * @example 218 * <pre name="test"> 219 * //01234 220 * Erottelija erottaja = new Erottelija("a;b;",";"); 221 * erottaja.isDelimBefore(0,";") === false; 222 * erottaja.isDelimBefore(1,";") === false; 223 * erottaja.isDelimBefore(2,";") === true; 224 * erottaja.isDelimBefore(3,";") === false; 225 * erottaja.isDelimBefore(4,";") === true; 226 * erottaja.isDelimBefore(5,";") === false; 227 * </pre> 228 */ 229 public boolean isDelimBefore(int pos, String delim) { 230 if (pos <= 0) 231 return false; 232 if (pos > originalString.length()) 233 return false; 234 String usedDelim = delim; 235 if (delim == null) 236 usedDelim = this.delimiters; 237 char c = originalString.charAt(pos - 1); 238 return (usedDelim.indexOf(c) >= 0); 239 } 240 241 242 /** 243 * Tarkistaa onko erotinmerkkiä juuri paikan vasemmalla puolella 244 * @param pos paikka josta tutkitaan 245 * @return true jos on erotin merkki vasemmalla puolella, false muuten 246 * @example 247 * <pre name="test"> 248 * //01234 249 * Erottelija erottaja = new Erottelija("a;b;",";"); 250 * erottaja.isDelimBefore(0) === false; 251 * erottaja.isDelimBefore(1) === false; 252 * erottaja.isDelimBefore(2) === true; 253 * erottaja.isDelimBefore(3) === false; 254 * erottaja.isDelimBefore(4) === true; 255 * erottaja.isDelimBefore(5) === false; 256 * </pre> 257 */ 258 public boolean isDelimBefore(int pos) { 259 return isDelimBefore(pos, null); 260 } 261 262 263 /** 264 * Tarkistaa onko erotinmerkkiä juuri nykypaikan vasemmalla puolella 265 * @return true jos on erotin merkki vasemmalla puolella, false muuten 266 * @example 267 * <pre name="test"> 268 * //01234 269 * Erottelija erottaja = new Erottelija("a;b;",";"); 270 * erottaja.isDelimBefore() === false; 271 * erottaja.nextToken() === "a"; erottaja.isDelimBefore() === true; 272 * erottaja.nextToken() === "b"; erottaja.isDelimBefore() === true; 273 * erottaja.nextToken() === ""; erottaja.isDelimBefore() === false; 274 * erottaja.nextToken() === ""; erottaja.isDelimBefore() === false; 275 * </pre> 276 */ 277 public boolean isDelimBefore() { 278 return isDelimBefore(startPos, null); 279 } 280 281 282 /** 283 * Palauttaa seuraavan palasen jonosta. 284 * @param delim erotinjoukko, jonka perusteella perusteella erotetaan 285 * @return jonon seuraava palanen 286 * @example 287 * <pre name="test"> 288 * Erottelija erottaja = new Erottelija("a b;c"); 289 * erottaja.nextToken(" ") === "a"; 290 * erottaja.nextToken(" ") === "b;c"; 291 * erottaja = new Erottelija("a b;c"); 292 * erottaja.nextToken(" ") === "a"; 293 * erottaja.nextToken(";") === "b"; 294 * erottaja.nextToken(" ") === "c"; 295 * erottaja.nextToken(" ") === ""; 296 * erottaja = new Erottelija(null); 297 * erottaja.nextToken(" ") === ""; 298 * erottaja = new Erottelija("a b"); 299 * erottaja.nextToken(null) === "a"; 300 * </pre> 301 */ 302 public String nextToken(String delim) { 303 if (originalString == null) 304 return ""; 305 int len = originalString.length(); 306 if (startPos > len) 307 return ""; 308 if (startPos == len) { 309 startPos = len + 1; 310 return ""; 311 } 312 String usedDelim = delim; 313 if (delim == null) 314 usedDelim = this.delimiters; 315 int nextpos = indexOfAny(originalString, usedDelim, startPos); 316 if (nextpos < 0) 317 nextpos = len; 318 String result = originalString.substring(startPos, nextpos); 319 startPos = nextpos; 320 if (startPos < len) 321 startPos++; 322 return result; 323 } 324 325 326 /** 327 * Ottaa seuraavan palasen ja jos se on tyhjä, niin palauttaa def-jonon. 328 * @param delim erotinjoukko, jonka perusteella perusteella erotetaan 329 * @param def oletusarvo jos seuraava palanen on tyhjä 330 * @return jonon seuraava palanen tai oletus 331 * @example 332 * <pre name="test"> 333 * Erottelija erottaja = new Erottelija("a b;c"); 334 * erottaja.nextToken(" ","d") === "a"; 335 * erottaja.nextToken(" ","d") === "b;c"; 336 * erottaja.nextToken(" ","d") === "d"; 337 * </pre> 338 */ 339 public String nextToken(String delim, String def) { 340 String piece = nextToken(delim); 341 if (piece.length() > 0) 342 return piece; 343 return def; 344 } 345 346 347 /** 348 * Palauttaa jonosta seuraavan kokonaisluvun ja oletuksen jos luku ei ole 349 * kunnollinen. 350 * @param delim erotinjoukko, jonka perusteella perusteella erotetaan 351 * @param def oletusarvo jos luku ei ole kunnollinen 352 * @return seuraava kokonaisluku tai oletus 353 * @example 354 * <pre name="test"> 355 * Erottelija erottaja = new Erottelija("1;2"); 356 * erottaja.nextToken(";",3) === 1; 357 * erottaja.nextToken(";",3) === 2; 358 * erottaja.nextToken(";",3) === 3; 359 * </pre> 360 */ 361 public int nextToken(String delim, int def) { 362 String piece = nextToken(delim); 363 return Mjonot.erotaInt(piece, def); 364 } 365 366 367 /** 368 * Palauttaa jonosta seuraavan kokonaisluvun ja oletuksen jos luku ei ole 369 * kunnollinen. 370 * @param def oletusarvo jos luku ei ole kunnollinen 371 * @return seuraava kokonaisluku tai oletus 372 * @example 373 * <pre name="test"> 374 * Erottelija erottaja = new Erottelija("1 2"); 375 * erottaja.nextToken(3) === 1; 376 * erottaja.nextToken(3) === 2; 377 * erottaja.nextToken(3) === 3; 378 * </pre> 379 */ 380 public int nextToken(int def) { 381 return nextToken(null, def); 382 } 383 384 385 /** 386 * Palauttaa jonosta seuraavan kokonaisluvun ja 0 jos luku ei ole 387 * kunnollinen. 388 * @return seuraava kokonaisluku tai 0 389 * @example 390 * <pre name="test"> 391 * Erottelija erottaja = new Erottelija("1 2"); 392 * erottaja.nextInt() === 1; 393 * erottaja.nextInt() === 2; 394 * erottaja.nextInt() === 0; 395 * </pre> 396 */ 397 public int nextInt() { 398 return nextToken(0); 399 } 400 401 402 /** 403 * Palauttaa jonosta seuraavan reaaliluvun ja oletuksen jos luku ei ole 404 * kunnollinen. 405 * @param delim erotinjoukko, jonka perusteella perusteella erotetaan 406 * @param def oletusarvo jos luku ei ole kunnollinen 407 * @return seuraava reaaliluku tai oletus 408 * @example 409 * <pre name="test"> 410 * Erottelija erottaja = new Erottelija("1;2"); 411 * erottaja.nextToken(";",3.1) ~~~ 1.0; 412 * erottaja.nextToken(";",3.1) ~~~ 2.0; 413 * erottaja.nextToken(";",3.1) ~~~ 3.1; 414 * </pre> 415 */ 416 public double nextToken(String delim, double def) { 417 String piece = nextToken(delim); 418 return Mjonot.erotaDouble(piece, def); 419 } 420 421 422 /** 423 * Palauttaa jonosta seuraavan reaaliluvun ja oletuksen jos luku ei ole 424 * kunnollinen. 425 * @param def oletusarvo jos luku ei ole kunnollinen 426 * @return seuraava reaaliluku tai oletus 427 * @example 428 * <pre name="test"> 429 * Erottelija erottaja = new Erottelija("1 2"); 430 * erottaja.nextToken(3.1) ~~~ 1.0; 431 * erottaja.nextToken(3.1) ~~~ 2.0; 432 * erottaja.nextToken(3.1) ~~~ 3.1; 433 * </pre> 434 */ 435 public double nextToken(double def) { 436 return nextToken(null, def); 437 } 438 439 440 /** 441 * Palauttaa jonosta seuraavan reaaliluvun ja 0.0 jos luku ei ole 442 * kunnollinen. 443 * @return seuraava reaaliluku tai 0.0 444 * @example 445 * <pre name="test"> 446 * Erottelija erottaja = new Erottelija("1 2"); 447 * erottaja.nextDouble() ~~~ 1.0; 448 * erottaja.nextDouble() ~~~ 2.0; 449 * erottaja.nextDouble() ~~~ 0.0; 450 * </pre> 451 */ 452 public double nextDouble() { 453 return nextToken(0.0); 454 } 455 456 457 /** 458 * Laskee palasten lukumäärän. 459 * @param pos paikka josta laskeminen aloitetaan 460 * @return palasten lukumäärä. 461 * @example 462 * <pre name="test"> 463 * Erottelija erottaja = new Erottelija("1 2"); 464 * erottaja.countTokens(0) === 2; 465 * erottaja.countTokens(2) === 1; 466 * erottaja = new Erottelija("1"); 467 * erottaja.countTokens(0) === 1; 468 * erottaja.countTokens(1) === 0; 469 * </pre> 470 */ 471 public int countTokens(int pos) { 472 int n = 1; 473 int len = originalString.length(); 474 if (pos > len) 475 return 0; 476 if (pos == len) 477 return isDelimBefore(pos) ? 1 : 0; 478 for (int i = pos; i < len; i++) { 479 char c = originalString.charAt(i); 480 if (delimiters.indexOf(c) >= 0) 481 n++; 482 } 483 return n; 484 } 485 486 487 /** 488 * Laskee palasten lukumäärän. 489 * @return palasten lukumäärä. 490 * @example 491 * <pre name="test"> 492 * Erottelija erottaja = new Erottelija("1 2"); 493 * erottaja.countTokens() === 2; 494 * </pre> 495 */ 496 public int countTokens() { 497 return countTokens(0); 498 } 499 500 501 /** 502 * Laskee palasten lukumäärän. 503 * @return palasten lukumäärä. 504 * @example 505 * <pre name="test"> 506 * Erottelija erottaja = new Erottelija("1 2"); 507 * erottaja.countRemainingTokens() === 2; 508 * erottaja.nextDouble() ~~~ 1.0; 509 * erottaja.countRemainingTokens() === 1; 510 * erottaja.nextDouble() ~~~ 2.0; 511 * erottaja.countRemainingTokens() === 0; 512 * erottaja.nextDouble() ~~~ 0.0; 513 * </pre> 514 */ 515 public int countRemainingTokens() { 516 return countTokens(startPos); 517 } 518 519 520 /** 521 * Tarkistaa että vieläkö palasia on jäljellä. 522 * @return onko palasia jäljellä 523 * @example 524 * <pre name="test"> 525 * Erottelija erottaja = new Erottelija("1 2"); 526 * erottaja.hasMoreElements() === true; 527 * erottaja.nextDouble() ~~~ 1.0; 528 * erottaja.hasMoreElements() === true; 529 * erottaja.nextDouble() ~~~ 2.0; 530 * erottaja.hasMoreElements() === false; 531 * erottaja.nextDouble() ~~~ 0.0; 532 * </pre> 533 */ 534 @Override 535 public boolean hasMoreElements() { 536 if (isDelimBefore()) 537 return true; 538 return (startPos < originalString.length()); 539 } 540 541 542 /** 543 * Tarkistaa että vieläkö palasia on jäljellä. 544 * @return onko palasia jäljellä 545 * @example 546 * <pre name="test"> 547 * Erottelija erottaja = new Erottelija("1 2"); 548 * erottaja.hasMoreTokens() === true; 549 * erottaja.nextDouble() ~~~ 1.0; 550 * erottaja.nextDouble() ~~~ 2.0; 551 * erottaja.hasMoreTokens() === false; 552 * erottaja.nextDouble() ~~~ 0.0; 553 * </pre> 554 */ 555 public boolean hasMoreTokens() { 556 return hasMoreElements(); 557 } 558 559 560 /** 561 * Palauttaa seuraavan palasen Objectina. 562 * @return seuraava palanen 563 * @example 564 * <pre name="test"> 565 * Erottelija erottaja = new Erottelija("1 2"); 566 * erottaja.nextElement() === "1"; 567 * erottaja.nextElement() === "2"; 568 * erottaja.nextElement() === ""; 569 * </pre> 570 */ 571 @Override 572 public String nextElement() { 573 return nextToken(); 574 } 575 576 577 /** 578 * Siivoaa palasteltavan jonon turhista välilyönneistä 579 * @example 580 * <pre name="test"> 581 * Erottelija erottaja = new Erottelija(" 1 2 "); 582 * erottaja.countTokens() === 6; 583 * erottaja.trim(); 584 * erottaja.countTokens() === 4; 585 * </pre> 586 */ 587 public void trim() { 588 originalString = Mjonot.poista_2_tyhjat(originalString); 589 startPos = 0; 590 } 591 592 593 /** 594 * Palauttaa jäljellä olevan jonon. 595 * @return jäljellä oleva jono. 596 * @example 597 * <pre name="test"> 598 * Erottelija erottaja = new Erottelija(" 1 2 "); 599 * erottaja.trim(); 600 * erottaja.rest() === " 1 2 "; 601 * </pre> 602 */ 603 public String rest() { 604 if (!hasMoreTokens()) 605 return ""; 606 return originalString.substring(startPos); 607 } 608 609 610 /** 611 * Palauttaa paikan erottelijan alkuun. 612 * @example 613 * <pre name="test"> 614 * Erottelija erottaja = new Erottelija("1 2"); 615 * erottaja.nextDouble() ~~~ 1.0; 616 * erottaja.nextDouble() ~~~ 2.0; 617 * erottaja.nextDouble() ~~~ 0.0; 618 * erottaja.reset(); 619 * erottaja.nextDouble() ~~~ 1.0; 620 * erottaja.reset(); 621 * erottaja.nextDouble() ~~~ 1.0; 622 * </pre> 623 */ 624 public void reset() { 625 startPos = 0; 626 } 627 628 /** 629 * Testataan Erottelijaluokkaa 630 * @param args ei käytössä 631 */ 632 /* 633 public static void main(String[] args) { 634 Erottelija erottaja = new Erottelija("12;3.5:kissa,,,istuu puussa,3.4",";:,"); 635 System.out.println("Palasia: " + erottaja.countTokens()); 636 for (int i=1; erottaja.hasMoreTokens(); i++ ) 637 System.out.println(i + ": |" + erottaja.nextToken()+"|"); 638 System.out.println("8: |"+erottaja.nextToken()+"|"); 639 erottaja.reset(); 640 System.out.println(erottaja.nextToken(0)); 641 System.out.println(erottaja.countRemainingTokens()); 642 System.out.println(erottaja.rest()); 643 System.out.println(erottaja.nextToken(0.0)); 644 System.out.println(erottaja.nextToken(2)); 645 System.out.println(erottaja.nextToken(2.1)); 646 System.out.println(erottaja.countRemainingTokens()); 647 System.out.println(erottaja.rest()); 648 } 649 */ 650 } 651