Implementing Java camelCase breaking in SCL module editor
[simantics/platform.git] / bundles / org.simantics.scl.ui / src / org / simantics / scl / ui / editor2 / iterator / JavaWordIterator.java
1 package org.simantics.scl.ui.editor2.iterator;
2
3 import java.text.CharacterIterator;
4
5 import com.ibm.icu.text.BreakIterator;
6
7 import org.eclipse.core.runtime.Assert;
8
9
10
11 /**
12  * Breaks java text into word starts, also stops at line start and end. No
13  * direction dependency.
14  *
15  * @since 3.0
16  */
17 public class JavaWordIterator extends BreakIterator {
18
19         /**
20          * The underlying java break iterator. It returns all breaks, including
21          * before and after every whitespace.
22          */
23         private JavaBreakIterator fIterator;
24         /** The current index for the stateful operations. */
25         private int fIndex;
26
27         /**
28          * Creates a new word iterator.
29          */
30         public JavaWordIterator() {
31                 fIterator= new JavaBreakIterator();
32                 first();
33         }
34
35         /*
36          * @see java.text.BreakIterator#first()
37          */
38         @Override
39         public int first() {
40                 fIndex= fIterator.first();
41                 return fIndex;
42         }
43
44         /*
45          * @see java.text.BreakIterator#last()
46          */
47         @Override
48         public int last() {
49                 fIndex= fIterator.last();
50                 return fIndex;
51         }
52
53         /*
54          * @see java.text.BreakIterator#next(int)
55          */
56         @Override
57         public int next(int n) {
58                 int next= 0;
59                 while (--n > 0 && next != DONE) {
60                         next= next();
61                 }
62                 return next;
63         }
64
65         /*
66          * @see java.text.BreakIterator#next()
67          */
68         @Override
69         public int next() {
70                 fIndex= following(fIndex);
71                 return fIndex;
72         }
73
74         /*
75          * @see java.text.BreakIterator#previous()
76          */
77         @Override
78         public int previous() {
79                 fIndex= preceding(fIndex);
80                 return fIndex;
81         }
82
83
84         /*
85          * @see java.text.BreakIterator#preceding(int)
86          */
87         @Override
88         public int preceding(int offset) {
89                 int first= fIterator.preceding(offset);
90                 if (isWhitespace(first, offset)) {
91                         int second= fIterator.preceding(first);
92                         if (second != DONE && !isDelimiter(second, first))
93                                 return second;
94                 }
95                 return first;
96         }
97
98         /*
99          * @see java.text.BreakIterator#following(int)
100          */
101         @Override
102         public int following(int offset) {
103                 int first= fIterator.following(offset);
104                 if (eatFollowingWhitespace(offset, first)) {
105                         int second= fIterator.following(first);
106                         if (isWhitespace(first, second))
107                                 return second;
108                 }
109                 return first;
110         }
111
112         private boolean eatFollowingWhitespace(int offset, int exclusiveEnd) {
113                 if (exclusiveEnd == DONE || offset == DONE)
114                         return false;
115
116                 if (isWhitespace(offset, exclusiveEnd))
117                         return false;
118                 if (isDelimiter(offset, exclusiveEnd))
119                         return false;
120
121                 return true;
122         }
123
124         /**
125          * Returns <code>true</code> if the given sequence into the underlying text
126          * represents a delimiter, <code>false</code> otherwise.
127          *
128          * @param offset the offset
129          * @param exclusiveEnd the end offset
130          * @return <code>true</code> if the given range is a delimiter
131          */
132         private boolean isDelimiter(int offset, int exclusiveEnd) {
133                 if (exclusiveEnd == DONE || offset == DONE)
134                         return false;
135
136                 Assert.isTrue(offset >= 0);
137                 Assert.isTrue(exclusiveEnd <= getText().getEndIndex());
138                 Assert.isTrue(exclusiveEnd > offset);
139
140                 CharSequence seq= fIterator.fText;
141
142                 while (offset < exclusiveEnd) {
143                         char ch= seq.charAt(offset);
144                         if (ch != '\n' && ch != '\r')
145                                 return false;
146                         offset++;
147                 }
148
149                 return true;
150         }
151
152         /**
153          * Returns <code>true</code> if the given sequence into the underlying text
154          * represents whitespace, but not a delimiter, <code>false</code> otherwise.
155          *
156          * @param offset the offset
157          * @param exclusiveEnd the end offset
158          * @return <code>true</code> if the given range is whitespace
159          */
160         private boolean isWhitespace(int offset, int exclusiveEnd) {
161                 if (exclusiveEnd == DONE || offset == DONE)
162                         return false;
163
164                 Assert.isTrue(offset >= 0);
165                 Assert.isTrue(exclusiveEnd <= getText().getEndIndex());
166                 Assert.isTrue(exclusiveEnd > offset);
167
168                 CharSequence seq= fIterator.fText;
169
170                 while (offset < exclusiveEnd) {
171                         char ch= seq.charAt(offset);
172                         if (!Character.isWhitespace(ch))
173                                 return false;
174                         if (ch == '\n' || ch == '\r')
175                                 return false;
176                         offset++;
177                 }
178
179                 return true;
180         }
181
182         /*
183          * @see java.text.BreakIterator#current()
184          */
185         @Override
186         public int current() {
187                 return fIndex;
188         }
189
190         /*
191          * @see java.text.BreakIterator#getText()
192          */
193         @Override
194         public CharacterIterator getText() {
195                 return fIterator.getText();
196         }
197
198         /**
199          * Sets the text as <code>CharSequence</code>.
200          * @param newText the new text
201          */
202         public void setText(CharSequence newText) {
203                 fIterator.setText(newText);
204                 first();
205         }
206
207         /*
208          * @see java.text.BreakIterator#setText(java.text.CharacterIterator)
209          */
210         @Override
211         public void setText(CharacterIterator newText) {
212                 fIterator.setText(newText);
213                 first();
214         }
215
216         /*
217          * @see java.text.BreakIterator#setText(java.lang.String)
218          */
219         @Override
220         public void setText(String newText) {
221                 setText((CharSequence) newText);
222         }
223
224 }