1 /*
2  * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 import java.text.BreakIterator;
25 import java.text.CharacterIterator;
26 import java.util.ArrayList;
27 import java.util.Collections;
28 import java.util.Iterator;
29 import java.util.List;
30 import java.util.NoSuchElementException;
31 
32 public class MirroredBreakIterator extends BreakIterator {
33     private final List<Integer> boundaries;
34     private int charIndex;
35     private int boundaryIndex;
36 
MirroredBreakIterator(BreakIterator bi)37     MirroredBreakIterator(BreakIterator bi) {
38         List<Integer> b = new ArrayList<Integer>();
39         int i = bi.first();
40         charIndex = i;
41         for (; i != DONE; i = bi.next()) {
42             b.add(i);
43         }
44         boundaries = Collections.unmodifiableList(b);
45     }
46 
47     @Override
clone()48     public Object clone() {
49         try {
50             return super.clone();
51         } catch (Exception e) {
52             throw new RuntimeException("clone failed", e);
53         }
54     }
55 
56     @Override
first()57     public int first() {
58         return changeIndices(0);
59     }
60 
61     @Override
last()62     public int last() {
63         return changeIndices(boundaries.size() - 1);
64     }
65 
66     @Override
next(int n)67     public int next(int n) {
68         if (n == 0) {
69             return current();
70         }
71         int newBoundary = boundaryIndex + n;
72         if (newBoundary < 0) {
73             first();
74             return DONE;
75         }
76         if (newBoundary > lastBoundary()) {
77             last();
78             return DONE;
79         }
80         return changeIndices(newBoundary);
81     }
82 
83     @Override
next()84     public int next() {
85         if (boundaryIndex == lastBoundary()) {
86             return DONE;
87         }
88         return changeIndices(boundaryIndex + 1);
89     }
90 
91     @Override
previous()92     public int previous() {
93         if (boundaryIndex == 0) {
94             return DONE;
95         }
96         return changeIndices(boundaryIndex - 1);
97     }
98 
99     @Override
following(int offset)100     public int following(int offset) {
101         validateOffset(offset);
102         for (int b = 0; b <= lastBoundary(); b++) {
103             int i = boundaries.get(b);
104             if (i > offset) {
105                 return changeIndices(i, b);
106             }
107         }
108         return DONE;
109     }
110 
111     @Override
preceding(int offset)112     public int preceding(int offset) {
113         validateOffset(offset);
114         for (int b = lastBoundary(); b >= 0; b--) {
115             int i = boundaries.get(b);
116             if (i < offset) {
117                 return changeIndices(i, b);
118             }
119         }
120         return DONE;
121     }
122 
123     @Override
isBoundary(int offset)124     public boolean isBoundary(int offset) {
125         // Call the default impelementation in BreakIterator
126         return super.isBoundary(offset);
127     }
128 
129     @Override
current()130     public int current() {
131         return charIndex;
132     }
133 
134     @Override
getText()135     public CharacterIterator getText() {
136         throw new UnsupportedOperationException();
137     }
138 
139     @Override
setText(CharacterIterator newText)140     public void setText(CharacterIterator newText) {
141         throw new UnsupportedOperationException();
142     }
143 
lastBoundary()144     private int lastBoundary() {
145         return boundaries.size() - 1;
146     }
147 
changeIndices(int newCharIndex, int newBoundary)148     private int changeIndices(int newCharIndex, int newBoundary) {
149         boundaryIndex = newBoundary;
150         return charIndex = newCharIndex;
151     }
152 
changeIndices(int newBoundary)153     private int changeIndices(int newBoundary) {
154         try {
155             return changeIndices(boundaries.get(newBoundary), newBoundary);
156         } catch (IndexOutOfBoundsException e) {
157             throw new IllegalArgumentException(e);
158         }
159     }
160 
validateOffset(int offset)161     private void validateOffset(int offset) {
162         if (offset < boundaries.get(0) || offset > boundaries.get(lastBoundary())) {
163             throw new IllegalArgumentException();
164         }
165     }
166 }
167