1
2
3
4
5
6
7
8
9
10
11
12 package sk.uniba.euromath.editor.textEditor;
13
14 import java.util.ArrayList;
15 import java.util.HashMap;
16 import java.util.List;
17 import java.util.Map;
18
19 import sk.baka.ikslibs.levelmapper.NodeListID;
20 import sk.uniba.euromath.document.XMLAccess;
21
22 /***
23 * Base implementation of ITextpieceContainer. To resolve indexex, are used tree
24 * types of text: original, logical and rendered. Logical replaces transformed
25 * text, because transformed text is not available. Logical text is simply
26 * created from rendered, adding line breaks at end of lines and interpreting
27 * them as white space.
28 *
29 * @author Tomáš Studva 29.9.2005
30 */
31 public class TextPieceContainer implements ITextPieceContainer {
32
33 /***
34 * Id of text node.
35 */
36 private final String nodeID;
37
38 /***
39 * List of keepers in this container.
40 */
41 private final List<ITextPieceKeeper> keepers;
42
43 /***
44 * Maps keepers to their ITextPieceInfo.
45 */
46 private final Map<ITextPieceKeeper, ITextPieceInfo> keeperInfoMap;
47
48 /***
49 * Constructor.
50 *
51 * @param firstPieceKeeper
52 * first TextPieceKeeper in this container
53 * @param nodeId
54 * id of node corresponding to this TextPieceContainer
55 * @param originalNodeText
56 * original DOM node's text
57 */
58 public TextPieceContainer(ITextPieceKeeper firstPieceKeeper,
59 String nodeId) {
60 super();
61 assert (nodeId != null);
62 assert (firstPieceKeeper != null);
63
64 this.nodeID = nodeId;
65 this.keepers = new ArrayList<ITextPieceKeeper>();
66 this.keeperInfoMap = new HashMap<ITextPieceKeeper, ITextPieceInfo>();
67
68 add(firstPieceKeeper);
69 }
70
71
72
73
74
75
76 public ITextPieceInfo add(ITextPieceKeeper pieceKeeper) {
77
78 assert (pieceKeeper.getTextLocator().getID()
79 .equals(getNodeID()));
80
81
82 if (this.keeperInfoMap.get(pieceKeeper) != null)
83 return this.keeperInfoMap.get(pieceKeeper);
84
85 TextPieceInfoImpl pieceInfo;
86
87 if (this.keepers.size() == 0) {
88
89 pieceInfo = new TextPieceInfoImpl(pieceKeeper,
90 getNodeID(), 0);
91 } else {
92
93
94 ITextPieceInfo prevInfo = this.keepers.get(
95 this.keepers.size() - 1)
96 .getTextPieceInfo();
97
98 pieceInfo = new TextPieceInfoImpl(
99 pieceKeeper,
100 getNodeID(),
101 prevInfo.getRenderedOffset()
102 + prevInfo
103 .getRenderedText()
104 .length());
105 }
106 this.keepers.add(pieceKeeper);
107 this.keeperInfoMap.put(pieceKeeper, pieceInfo);
108 return pieceInfo;
109 }
110
111
112
113
114
115
116 public ITextPieceKeeper getKeeper(int index) {
117 if ((index < 0) || (index >= this.keepers.size()))
118 throw new IllegalArgumentException("Invalid index.");
119 return this.keepers.get(index);
120 }
121
122
123
124
125
126
127 public int size() {
128 return this.keepers.size();
129 }
130
131
132
133
134
135
136 public ITextPieceInfo getInfo(ITextPieceKeeper forPiece) {
137 return this.keeperInfoMap.get(forPiece);
138 }
139
140
141
142
143
144
145 public ITextPieceKeeper getPrevious(ITextPieceKeeper keeper) {
146 return getKeeper(indexOf(keeper) - 1);
147 }
148
149 public ITextPieceKeeper getNext(ITextPieceKeeper keeper) {
150 return getKeeper(indexOf(keeper) + 1);
151 }
152
153
154
155
156
157
158 public int indexOf(ITextPieceKeeper piece) {
159 return this.keepers.indexOf(piece);
160 }
161
162 /***
163 * Getter
164 *
165 * @return Returns the nodeID.
166 */
167 private String getNodeID() {
168 return this.nodeID;
169 }
170
171
172
173
174
175
176 public List<ITextPieceKeeper> getAll() {
177 return this.keepers;
178 }
179
180
181
182
183
184
185 public ITextPieceKeeper getKeeperByRenderedOffset(int offset) {
186 if ((offset < 0) || (offset > getWholeRenderedText().length()))
187 throw new IllegalArgumentException(
188 "Container does not contain this offset");
189
190 for (ITextPieceKeeper keeper : this.keepers) {
191 int offsetInContainer = keeper.getTextPieceInfo()
192 .getRenderedOffset();
193 if ((offsetInContainer <= offset)
194 && (offset <= offsetInContainer
195 + keeper
196 .getTextPieceInfo()
197 .getLastIndex()))
198 return keeper;
199 }
200
201 return this.keepers.get(this.keepers.size() - 1);
202 }
203
204
205
206
207
208
209
210 public List<ITextPieceKeeper> getKeepers(int startIndex, int endIndex) {
211 return this.keepers.subList(startIndex, endIndex);
212 }
213
214
215
216
217
218
219
220 public List<ITextPieceKeeper> getKeepers(ITextPieceKeeper startKeeper,
221 ITextPieceKeeper endKeeper) {
222 return this.keepers.subList(indexOf(startKeeper),
223 indexOf(endKeeper));
224 }
225
226
227
228
229
230
231 public String getWholeRenderedText() {
232
233 StringBuilder result = new StringBuilder();
234 for (ITextPieceKeeper keeper : this.keepers) {
235 result.append(keeper.getText());
236 }
237 return result.toString();
238 }
239
240 /***
241 * Build from rendered text. Adds to every line end one whitespace.
242 *
243 * @return whole rendered text enriched with whitespaces at places of
244 * line breaks
245 */
246 private String getWholeLogicalText() {
247 StringBuilder result = new StringBuilder();
248 for (ITextPieceKeeper keeper : this.keepers) {
249 result.append(keeper.getText());
250 result.append(" ");
251 }
252 return result.toString();
253 }
254
255 /***
256 * Resolves index from logical to rendered text.
257 *
258 * @param indexInLogicalRenderedText
259 * index in whole logical rendered text
260 * @return index in whole rendered text
261 */
262 private int resolveLogicalIndex(int indexInLogicalRenderedText) {
263 if (this.keepers.size() == 0)
264 throw new IllegalArgumentException("Illegal call.");
265
266 StringBuilder result = new StringBuilder();
267 int i = 0;
268
269 while (result.length() <= indexInLogicalRenderedText) {
270 result.append(this.keepers.get(i).getText());
271 result.append(" ");
272 i++;
273 }
274 return indexInLogicalRenderedText - (i - 1);
275 }
276
277
278
279
280
281
282
283 public int resolveOriginalIndex(int indexInOriginalText,
284 XMLAccess xmlAccess) {
285
286 String originalText = xmlAccess.getIDManager().getNode(
287 getNodeID()).getTextValue();
288 if (indexInOriginalText > originalText.length())
289 throw new IllegalArgumentException();
290
291 int indexInLogicalText = NodeListID.resolveIndex(originalText,
292 indexInOriginalText, getWholeLogicalText());
293
294 return resolveLogicalIndex(indexInLogicalText);
295 }
296
297 /***
298 * Returns number of added line breaks to rendered text until
299 * indexInRenderedText.
300 *
301 * @param indexInRenderedText
302 * index in rendered text of container (node) or one
303 * behind the text
304 * @return number of added line breaks to rendered text until
305 * indexInRenderedText.
306 */
307 private int countLogicalLineBreaks(int indexInRenderedText) {
308 if (indexInRenderedText < 0)
309 throw new IllegalArgumentException(
310 "Illegal indexInRenderedText.");
311 if (this.keepers.size() == 0)
312 throw new IllegalArgumentException(
313 "Illegal method call.");
314
315 int countLB = 0;
316 ITextPieceInfo keeperInfo = this.keepers.get(0)
317 .getTextPieceInfo();
318
319
320 while (indexInRenderedText > keeperInfo.getLastIndex()) {
321 if (!hasNext(keeperInfo.getKeeper())) {
322 if (indexInRenderedText == keeperInfo
323 .getRenderedText().length())
324
325 return countLB;
326
327 throw new IllegalArgumentException(
328 "Illegal indexInRenderedText.");
329 }
330 indexInRenderedText = indexInRenderedText
331 - keeperInfo.getRenderedText().length();
332 countLB++;
333 keeperInfo = getNext(keeperInfo.getKeeper())
334 .getTextPieceInfo();
335 }
336
337 return countLB;
338 }
339
340
341
342
343
344
345
346 public int resolveRenderedIndex(int indexInRenderedText,
347 XMLAccess xmlAccess) {
348
349 int indexInLogicalText = indexInRenderedText
350 + countLogicalLineBreaks(indexInRenderedText);
351
352
353 NodeListID textNode = xmlAccess.getIDManager().getNodeNull(
354 getNodeID());
355 assert textNode != null;
356 assert textNode.isTextual();
357
358
359
360 String wholeLogicalText = getWholeLogicalText();
361 return textNode.resolveIndex(wholeLogicalText,
362 indexInLogicalText);
363 }
364
365
366
367
368
369
370 public boolean hasNext(ITextPieceKeeper keeper) {
371 return (indexOf(keeper) + 1) < this.keepers.size();
372 }
373
374
375
376
377
378
379 public boolean hasPrevious(ITextPieceKeeper keeper) {
380 return indexOf(keeper) > 0;
381 }
382
383
384
385
386
387
388 @Override
389 public String toString() {
390 StringBuilder result = new StringBuilder();
391 result.append(this.nodeID + " ");
392 for (ITextPieceKeeper keeper : this.keepers) {
393 ITextPieceInfo info = keeper.getTextPieceInfo();
394 result.append("["
395 + info.getRenderedOffset()
396 + ","
397 + (info.getRenderedOffset() + info
398 .getLastIndex()) + ","
399 + keeper.getText() + "],");
400 }
401 return result.toString();
402 }
403 }