View Javadoc

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 }