1 /*
2 * Copyright 1999-2006 Faculty of Mathematics, Physics and Informatics, Comenius
3 * University, Bratislava. This file is protected by the Mozilla Public License
4 * version 1.1 (the License); you may not use this file except in compliance
5 * with the License. You may obtain a copy of the License at
6 * http://euromath2.sourceforge.net/license.html Unless required by applicable
7 * law or agreed to in writing, software distributed under the License is
8 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
9 * KIND, either express or implied. See the License for the specific language
10 * governing permissions and limitations under the License.
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 * (non-Javadoc)
73 *
74 * @see sk.uniba.euromath.editor.textEditor.ITextPieceContainer#add(sk.uniba.euromath.editor.textEditor.ITextPieceKeeper)
75 */
76 public ITextPieceInfo add(ITextPieceKeeper pieceKeeper) {
77 // check id
78 assert (pieceKeeper.getTextLocator().getID()
79 .equals(getNodeID()));
80
81 // if is already in this container, do nothing
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 // it is first keeper
89 pieceInfo = new TextPieceInfoImpl(pieceKeeper,
90 getNodeID(), 0);
91 } else {
92 // it is not first keeper
93 // previous keeper's info
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 * (non-Javadoc)
113 *
114 * @see sk.uniba.euromath.editor.textEditor.ITextPieceContainer#getKeeper(int)
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 * (non-Javadoc)
124 *
125 * @see sk.uniba.euromath.editor.textEditor.ITextPieceContainer#size()
126 */
127 public int size() {
128 return this.keepers.size();
129 }
130
131 /*
132 * (non-Javadoc)
133 *
134 * @see sk.uniba.euromath.editor.textEditor.ITextPieceContainer#getInfo(sk.uniba.euromath.editor.textEditor.ITextPieceKeeper)
135 */
136 public ITextPieceInfo getInfo(ITextPieceKeeper forPiece) {
137 return this.keeperInfoMap.get(forPiece);
138 }
139
140 /*
141 * (non-Javadoc)
142 *
143 * @see sk.uniba.euromath.editor.textEditor.ITextPieceContainer#getPrevious(sk.uniba.euromath.editor.textEditor.ITextPieceKeeper)
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 * (non-Javadoc)
155 *
156 * @see sk.uniba.euromath.editor.textEditor.ITextPieceContainer#indexOf(sk.uniba.euromath.editor.textEditor.ITextPieceKeeper)
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 * (non-Javadoc)
173 *
174 * @see sk.uniba.euromath.editor.textEditor.ITextPieceContainer#getAll()
175 */
176 public List<ITextPieceKeeper> getAll() {
177 return this.keepers;
178 }
179
180 /*
181 * (non-Javadoc)
182 *
183 * @see sk.uniba.euromath.editor.textEditor.ITextPieceContainer#getKeeperByRenderedOffset(int)
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 // offset is one behind rendered text
201 return this.keepers.get(this.keepers.size() - 1);
202 }
203
204 /*
205 * (non-Javadoc)
206 *
207 * @see sk.uniba.euromath.editor.textEditor.ITextPieceContainer#getKeepers(int,
208 * int)
209 */
210 public List<ITextPieceKeeper> getKeepers(int startIndex, int endIndex) {
211 return this.keepers.subList(startIndex, endIndex);
212 }
213
214 /*
215 * (non-Javadoc)
216 *
217 * @see sk.uniba.euromath.editor.textEditor.ITextPieceContainer#getKeepers(sk.uniba.euromath.editor.textEditor.ITextPieceKeeper,
218 * sk.uniba.euromath.editor.textEditor.ITextPieceKeeper)
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 * (non-Javadoc)
228 *
229 * @see sk.uniba.euromath.editor.textEditor.ITextPieceContainer#getWholeRenderedText()
230 */
231 public String getWholeRenderedText() {
232 // TODO Studva caching
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 * (non-Javadoc)
279 *
280 * @see sk.uniba.euromath.editor.textEditor.ITextPieceContainer#resolveOriginalIndex(int,
281 * sk.uniba.euromath.document.XMLAccess)
282 */
283 public int resolveOriginalIndex(int indexInOriginalText,
284 XMLAccess xmlAccess) {
285 // transform original index to logical text index
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.");// //$NON-NLS-N$
311 if (this.keepers.size() == 0)
312 throw new IllegalArgumentException(
313 "Illegal method call.");// //$NON-NLS-N$
314
315 int countLB = 0;
316 ITextPieceInfo keeperInfo = this.keepers.get(0)
317 .getTextPieceInfo();
318
319 // while index is not in keeperInfo's text
320 while (indexInRenderedText > keeperInfo.getLastIndex()) {
321 if (!hasNext(keeperInfo.getKeeper())) {
322 if (indexInRenderedText == keeperInfo
323 .getRenderedText().length())
324 // index is one behind the text
325 return countLB;
326 // index is too big
327 throw new IllegalArgumentException(
328 "Illegal indexInRenderedText.");// //$NON-NLS-N$
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 * (non-Javadoc)
342 *
343 * @see sk.uniba.euromath.editor.textEditor.ITextPieceContainer#resolveRenderedIndex(int,
344 * sk.uniba.euromath.document.XMLAccess)
345 */
346 public int resolveRenderedIndex(int indexInRenderedText,
347 XMLAccess xmlAccess) {
348 // resolve index to logical text index
349 int indexInLogicalText = indexInRenderedText
350 + countLogicalLineBreaks(indexInRenderedText);
351
352 // get XML text node(s)
353 NodeListID textNode = xmlAccess.getIDManager().getNodeNull(
354 getNodeID());
355 assert textNode != null;
356 assert textNode.isTextual();
357
358 // resolve position in whole logical text to node's text
359 // position
360 String wholeLogicalText = getWholeLogicalText();
361 return textNode.resolveIndex(wholeLogicalText,
362 indexInLogicalText);
363 }
364
365 /*
366 * (non-Javadoc)
367 *
368 * @see sk.uniba.euromath.editor.textEditor.ITextPieceContainer#hasNext(sk.uniba.euromath.editor.textEditor.ITextPieceKeeper)
369 */
370 public boolean hasNext(ITextPieceKeeper keeper) {
371 return (indexOf(keeper) + 1) < this.keepers.size();
372 }
373
374 /*
375 * (non-Javadoc)
376 *
377 * @see sk.uniba.euromath.editor.textEditor.ITextPieceContainer#hasPrevious(sk.uniba.euromath.editor.textEditor.ITextPieceKeeper)
378 */
379 public boolean hasPrevious(ITextPieceKeeper keeper) {
380 return indexOf(keeper) > 0;
381 }
382
383 /*
384 * (non-Javadoc)
385 *
386 * @see java.lang.Object#toString()
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 }