1
21 package org.crosswire.jsword.passage;
22
23 import java.io.IOException;
24 import java.io.ObjectInputStream;
25 import java.io.ObjectOutputStream;
26 import java.util.Iterator;
27 import java.util.NoSuchElementException;
28 import java.util.Set;
29 import java.util.TreeSet;
30
31 import org.crosswire.jsword.JSOtherMsg;
32 import org.crosswire.jsword.versification.Versification;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35
36
102 public class PassageTally extends AbstractPassage {
103
109 public PassageTally(Versification v11n) {
110 super(v11n);
111 board = new int[v11n.maximumOrdinal() + 1];
112 }
113
114
126 protected PassageTally(Versification v11n, String refs, Key basis) throws NoSuchVerseException {
127 super(v11n, refs);
128 board = new int[v11n.maximumOrdinal() + 1];
129 addVerses(refs, basis);
130 }
131
132 protected PassageTally(Versification v11n, String refs) throws NoSuchVerseException {
133 this(v11n, refs, null);
134 }
135
136 @Override
137 public boolean isEmpty() {
138 return size == 0;
139 }
140
141 @Override
142 public int countVerses() {
143 return size;
144 }
145
146
156 public void setOrdering(Order order) {
157 this.order = order;
158 }
159
160
165 public Order getOrdering() {
166 return order;
167 }
168
169
172 public int getTotal() {
173 return total;
174 }
175
176
180 public void setTotal(int total) {
181 this.total = total;
182 }
183
184 @Override
185 public PassageTally clone() {
186 PassageTally copy = (PassageTally) super.clone();
188
189 copy.board = board.clone();
190
191 return copy;
192 }
193
194 @Override
195 public String toString() {
196 return getName(0);
197 }
198
199 @Override
200 public String getName() {
201 return getName(0);
202 }
203
204
212 public String getName(int cnt) {
213 int maxCount = cnt;
214 if (PassageUtil.isPersistentNaming() && originalName != null) {
215 return originalName;
216 }
217
218 StringBuilder retcode = new StringBuilder();
219
220 if (order == Order.BIBLICAL) {
221 Iterator<VerseRange> it = rangeIterator(RestrictionType.NONE);
222 Verse current = null;
223 while (it.hasNext()) {
224 VerseRange range = it.next();
225 retcode.append(range.getName(current));
226
227 if (it.hasNext()) {
228 retcode.append(AbstractPassage.REF_PREF_DELIM);
229 }
230
231 current = range.getStart();
232 }
233 } else {
234 if (maxCount == 0) {
235 maxCount = Integer.MAX_VALUE;
236 }
237
238 Iterator<Key> it = new OrderedVerseIterator(getVersification(), board);
239 Key current = null;
240 int count = 0;
241
242 while (it.hasNext() && count < maxCount) {
243 Key verse = it.next();
244 retcode.append(verse.getName(current));
245
246 current = verse;
247 count++;
248
249 if (it.hasNext() && count < maxCount) {
250 retcode.append(AbstractPassage.REF_PREF_DELIM);
251 }
252 }
253 }
254
255 return retcode.toString();
256 }
257
258
264 public String getNameAndTally() {
265 return getNameAndTally(0);
266 }
267
268
276 public String getNameAndTally(int cnt) {
277 int maxCount = cnt;
278 StringBuilder retcode = new StringBuilder();
279 if (maxCount == 0) {
280 maxCount = Integer.MAX_VALUE;
281 }
282
283 OrderedVerseIterator it = new OrderedVerseIterator(getVersification(), board);
284 int count = 0;
285
286 while (it.hasNext() && count < maxCount) {
287 Key verse = it.next();
288 retcode.append(verse.getName());
289 retcode.append(" (");
290 retcode.append(100 * it.lastRank() / max);
291 retcode.append("%)");
292
293 count++;
294
295 if (it.hasNext() && count < maxCount) {
296 retcode.append(AbstractPassage.REF_PREF_DELIM);
297 }
298 }
299
300 return retcode.toString();
301 }
302
303
308 public Iterator<Key> iterator() {
309 if (order == Order.BIBLICAL) {
310 return new VerseIterator();
311 }
312 return new OrderedVerseIterator(getVersification(), board);
313 }
314
315 @Override
316 public Iterator<VerseRange> rangeIterator(RestrictionType restrict) {
317 if (order == Order.BIBLICAL) {
318 return new VerseRangeIterator(getVersification(), iterator(), restrict);
319 }
320 return new OrderedVerseRangeIterator(getVersification(), iterator(), board);
321 }
322
323
330 @Override
331 public boolean contains(Key that) {
332 for (Key aKey : that) {
333 Verse verse = (Verse) aKey;
334 if (board[verse.getOrdinal()] == 0) {
335 return false;
336 }
337 }
338
339 return true;
340 }
341
342
349 public int getTallyOf(Verse verse) {
350 return board[verse.getOrdinal()];
351 }
352
353
360 public int getIndexOf(Verse verse) {
361 int pos = verse.getOrdinal();
362 int tally = board[pos];
363 return tally > 0 ? pos : -1;
364 }
365
366
372 public void add(Key that) {
373 optimizeWrites();
374
375 alterVerseBase(that, 1);
376 fireIntervalAdded(this, null, null);
377 }
378
379
390 public void add(Key that, int count) {
391 optimizeWrites();
392
393 alterVerseBase(that, count);
394 fireIntervalAdded(this, null, null);
395 }
396
397
403 public void unAdd(Key that) {
404 optimizeWrites();
405
406 alterVerseBase(that, -1);
407 fireIntervalRemoved(this, null, null);
408 }
409
410
416 public void remove(Key that) {
417 optimizeWrites();
418
419 for (Key aKey : that) {
420 Verse verse = (Verse) aKey;
421 kill(verse.getOrdinal());
422 }
423
424 fireIntervalRemoved(this, null, null);
425 }
426
427 @Override
428 public void addAll(Key that) {
429 optimizeWrites();
430
431 if (that instanceof PassageTally) {
432 PassageTally tally = (PassageTally) that;
433
434 int vib = getVersification().maximumOrdinal();
435 for (int i = 0; i <= vib; i++) {
436 increment(i, tally.board[i]);
437 }
438
439 incrementMax(tally.max);
440 } else {
441 for (Key aKey : that) {
442 Verse verse = (Verse) aKey;
443 increment(verse.getOrdinal(), 1);
444 }
445
446 incrementMax(1);
447 }
448
449 fireIntervalAdded(this, null, null);
450 }
451
452
458 public void unAddAll(Passage that) {
459 optimizeWrites();
460
461 if (that instanceof PassageTally) {
462 PassageTally tally = (PassageTally) that;
463
464 int vib = getVersification().maximumOrdinal();
465 for (int i = 0; i <= vib; i++) {
466 increment(i, -tally.board[i]);
467 }
468 } else {
469 for (Key aKey : that) {
470 Verse verse = (Verse) aKey;
471 increment(verse.getOrdinal(), -1);
472 }
473 }
474
475 fireIntervalRemoved(this, null, null);
476
477 }
481
482 @Override
483 public void removeAll(Key key) {
484 optimizeWrites();
485
486 if (key instanceof PassageTally) {
487 PassageTally tally = (PassageTally) key;
488
489 int vib = getVersification().maximumOrdinal();
490 for (int i = 0; i <= vib; i++) {
491 if (tally.board[i] != 0) {
492 kill(i);
493 }
494 }
495 } else {
496 for (Key aKey : key) {
497 Verse verse = (Verse) aKey;
498 kill(verse.getOrdinal());
499 }
500 }
501
502 fireIntervalRemoved(this, null, null);
503
504 }
508
509 @Override
510 public void clear() {
511 optimizeWrites();
512
513 for (int i = 0; i < board.length; i++) {
514 board[i] = 0;
515 }
516
517 size = 0;
518
519 fireIntervalRemoved(this, null, null);
520 }
521
522
534 @Override
535 public Passage trimVerses(int count) {
536 optimizeWrites();
537
538 int i = 0;
539 boolean overflow = false;
540
541 Passage remainder = this.clone();
542
543 for (Key verse : this) {
544 i++;
545 if (i > count) {
546 remove(verse);
547 overflow = true;
548 } else {
549 remainder.remove(verse);
550 }
551
552 }
553
554 if (overflow) {
555 return remainder;
556 }
557 return null;
558 }
559
560
564 public void flatten() {
565 optimizeWrites();
566
567 for (int i = 0; i < board.length; i++) {
568 if (board[i] != 0) {
569 board[i] = 1;
570 }
571 }
572
573 max = 1;
574 }
575
576 @Override
577 public void blur(int verses, RestrictionType restrict) {
578 assert verses >= 0;
579
580 optimizeWrites();
581 raiseEventSuppresion();
582 raiseNormalizeProtection();
583
584 if (!restrict.equals(RestrictionType.NONE)) {
585 log.warn("Restrict={} is not properly supported.", restrict);
586
587 PassageTally temp = this.clone();
590 Iterator<VerseRange> it = temp.rangeIterator(RestrictionType.NONE);
591
592 while (it.hasNext()) {
593 VerseRange range = it.next();
594 for (int i = 0; i <= verses; i++) {
595 add(restrict.blur(getVersification(), range, i, i));
596 }
597 }
598 } else {
599 int[] newBoard = new int[board.length];
600
601 for (int i = 0; i < board.length; i++) {
602 if (board[i] != 0) {
603
612 for (int j = -verses; j < 0; j++) {
613 int k = i + j;
614 if (k >= 0) {
615 newBoard[k] += board[i] + verses + j;
616 }
617 }
618
619 newBoard[i] += board[i] + verses;
620
621 for (int j = 1; j <= verses; j++) {
622 int k = i + j;
623 if (k < board.length - 1) {
624 newBoard[k] += board[i] + verses - j;
625 }
626 }
627 }
628 }
629
630 board = newBoard;
631 }
632
633 resetMax();
634
635 lowerNormalizeProtection();
636 if (lowerEventSuppressionAndTest()) {
637 fireIntervalAdded(this, null, null);
638 }
639 }
640
641
645 private void resetMax() {
646 optimizeWrites();
647
648 max = 0;
649 size = 0;
650 for (int i = 0; i < board.length; i++) {
651 if (board[i] > 0) {
652 size++;
653 }
654 if (board[i] > max) {
655 max = board[i];
656 }
657 }
658 }
659
660
668 private void alterVerseBase(Key that, int tally) {
669 for (Key aKey : that) {
670 Verse verse = (Verse) aKey;
671 increment(verse.getOrdinal(), tally);
672 }
673
674 if (tally > 0) {
675 incrementMax(tally);
676 }
677 }
678
679
687 private void increment(int ord, int tally) {
688 boolean exists = board[ord] > 0;
689 board[ord] += tally;
690 if (board[ord] > MAX_TALLY) {
691 board[ord] = MAX_TALLY;
692 }
693 if (board[ord] < 0) {
694 board[ord] = 0;
695 }
696
697 if (exists && board[ord] == 0) {
699 size--;
700 } else if (!exists && board[ord] > 0) {
701 size++;
702 }
703 }
704
705
711 private void incrementMax(int tally) {
712 max += tally;
713 if (max > MAX_TALLY) {
714 max = MAX_TALLY;
715 }
716 if (max < 0) {
717 max = 0;
718 }
719 }
720
721
727 private void kill(int ord) {
728 if (board[ord] > 0) {
729 size--;
730 }
731
732 board[ord] = 0;
733 }
734
735
745 private void writeObject(ObjectOutputStream out) throws IOException {
746 out.defaultWriteObject();
747
748 writeObjectSupport(out);
749 }
750
751
763 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
764 optimizeWrites();
765
766 in.defaultReadObject();
767
768 readObjectSupport(in);
769 }
770
771
774 public enum Order {
775
778 BIBLICAL,
779
780
783 TALLY
784 }
785
786
789 public static final int MAX_TALLY = 20000;
790
791
794 private int size;
795
796
799 private int total;
800
801
804 protected int[] board;
805
806
809 private int max;
810
811
814 private Order order = Order.BIBLICAL;
815
816
819 private static final Logger log = LoggerFactory.getLogger(PassageTally.class);
820
821
824 private static final long serialVersionUID = 3761128240928274229L;
825
826
831 private final class VerseIterator implements Iterator<Key> {
832
835 protected VerseIterator() {
836 calculateNext();
837 }
838
839
842 public boolean hasNext() {
843 return next <= board.length - 1;
844 }
845
846
849 public Key next() throws NoSuchElementException {
850 if (next >= board.length) {
851 throw new NoSuchElementException();
852 }
853
854 Key retcode = getVersification().decodeOrdinal(next);
855 calculateNext();
856
857 return retcode;
858 }
859
860
863 public void remove() throws UnsupportedOperationException {
864 throw new UnsupportedOperationException();
865 }
866
867
870 private void calculateNext() {
871 do {
872 next++;
873 } while (next < board.length && board[next] == 0);
874 }
875
876
877 private int next;
878 }
879
880
885 private static final class OrderedVerseIterator implements Iterator<Key> {
886
889 protected OrderedVerseIterator(Versification v11n, int[] board) {
890 referenceSystem = v11n;
891 TreeSet<TalliedVerse> output = new TreeSet<TalliedVerse>();
892
893 int vib = board.length - 1;
894 for (int i = 0; i <= vib; i++) {
895 if (board[i] != 0) {
896 output.add(new TalliedVerse(i, board[i]));
897 }
898 }
899
900 it = output.iterator();
901 last = null;
902 }
903
904
907 public boolean hasNext() {
908 return it.hasNext();
909 }
910
911
914 public Key next() throws NoSuchElementException {
915 last = it.next();
916 return referenceSystem.decodeOrdinal(last.ord);
917 }
918
919
922 public void remove() throws UnsupportedOperationException {
923 throw new UnsupportedOperationException();
924 }
925
926
931 public int lastRank() throws NoSuchElementException {
932 if (last != null) {
933 return last.tally;
934 }
935 throw new NoSuchElementException(JSOtherMsg.lookupText("nextElement() has not been called yet."));
936 }
937
938
941 private Versification referenceSystem;
942
945 private TalliedVerse last;
946
947
950 private Iterator<TalliedVerse> it;
951 }
952
953
957 private static class TalliedVerse implements Comparable<TalliedVerse> {
958
966 protected TalliedVerse(int ord, int tally) {
967 this.ord = ord;
968 this.tally = tally;
969 }
970
971 @Override
972 public int hashCode() {
973 int result = 31 + ord;
974 return 31 * result + tally;
975 }
976
977 @Override
978 public boolean equals(Object obj) {
979 if (this == obj) {
980 return true;
981 }
982 if (obj == null) {
983 return false;
984 }
985 if (getClass() != obj.getClass()) {
986 return false;
987 }
988 final TalliedVerse other = (TalliedVerse) obj;
989 if (tally != other.tally) {
990 return false;
991 }
992 if (ord != other.ord) {
993 return false;
994 }
995 return true;
996 }
997
998
1001 public int compareTo(TalliedVerse that) {
1002 if (that.tally == this.tally) {
1003 return this.ord - that.ord;
1004 }
1005
1006 return that.tally - this.tally;
1007 }
1008
1009
1012 protected int ord;
1013
1014
1017 protected int tally;
1018 }
1019
1020
1025 private static final class OrderedVerseRangeIterator implements Iterator<VerseRange> {
1026
1034 protected OrderedVerseRangeIterator(Versification v11n, Iterator<Key> vit, int[] board) {
1035 Set<TalliedVerseRange> output = new TreeSet<TalliedVerseRange>();
1036
1037 Iterator<VerseRange> rit = new VerseRangeIterator(v11n, vit, RestrictionType.NONE);
1038 while (rit.hasNext()) {
1039 VerseRange range = rit.next();
1040
1041 int rank = 0;
1043 Iterator<Key> iter = range.iterator();
1044 while (iter.hasNext()) {
1045 Verse verse = (Verse) iter.next();
1046 int temp = board[verse.getOrdinal()];
1047 if (temp > rank) {
1048 rank = temp;
1049 }
1050 }
1051
1052 output.add(new TalliedVerseRange(range, rank));
1053 }
1054
1055 this.it = output.iterator();
1056 last = null;
1057 }
1058
1059
1062 public boolean hasNext() {
1063 return it.hasNext();
1064 }
1065
1066
1069 public VerseRange next() throws NoSuchElementException {
1070 last = it.next();
1071 return last.range;
1072 }
1073
1074
1077 public void remove() throws UnsupportedOperationException {
1078 throw new UnsupportedOperationException();
1079 }
1080
1081
1084 private TalliedVerseRange last;
1085
1086
1089 private Iterator<TalliedVerseRange> it;
1090 }
1091
1092
1096 private static class TalliedVerseRange implements Comparable<TalliedVerseRange> {
1097
1105 protected TalliedVerseRange(VerseRange range, int tally) {
1106 this.range = range;
1107 this.tally = tally;
1108 }
1109
1110 @Override
1111 public int hashCode() {
1112 int result = 31 + tally;
1113 return 31 * result + ((range == null) ? 0 : range.hashCode());
1114 }
1115
1116 @Override
1117 public boolean equals(Object obj) {
1118 if (this == obj) {
1119 return true;
1120 }
1121 if (obj == null) {
1122 return false;
1123 }
1124 if (getClass() != obj.getClass()) {
1125 return false;
1126 }
1127 final TalliedVerseRange other = (TalliedVerseRange) obj;
1128 if (tally != other.tally) {
1129 return false;
1130 }
1131 if (range == null) {
1132 if (other.range != null) {
1133 return false;
1134 }
1135 } else if (!range.equals(other.range)) {
1136 return false;
1137 }
1138 return true;
1139 }
1140
1141
1144 public int compareTo(TalliedVerseRange that) {
1145 if (that.tally == this.tally) {
1146 return this.range.compareTo(that.range);
1147 }
1148
1149 return that.tally - this.tally;
1150 }
1151
1152
1155 protected VerseRange range;
1156
1157
1160 protected int tally;
1161 }
1162}
1163