RepositioningInfo.java
001 /*
002  *  RepositioningInfo.java
003  *
004  *  Copyright (c) 1995-2012, The University of Sheffield. See the file
005  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
006  *
007  *  This file is part of GATE (see http://gate.ac.uk/), and is free
008  *  software, licenced under the GNU Library General Public License,
009  *  Version 2, June 1991 (in the distribution as file licence.html,
010  *  and also available at http://gate.ac.uk/gate/licence.html).
011  *
012  *  Angel Kirilov, 04/January/2002
013  *
014  *  $Id: RepositioningInfo.java 18950 2015-10-15 12:37:29Z ian_roberts $
015  */
016 
017 package gate.corpora;
018 
019 import gate.corpora.RepositioningInfo.PositionInfo;
020 import gate.util.Out;
021 
022 import java.io.Serializable;
023 import java.util.ArrayList;
024 
025 /**
026  * RepositioningInfo keep information about correspondence of positions
027  * between the original and extracted document content. With this information
028  * this class could be used for computing of this correspondence in the strict
029  * way (return -1 where is no correspondence)
030  * or in "flow" way (return near computable position)
031  */
032 
033 public class RepositioningInfo extends ArrayList<PositionInfo> {
034 
035   /** Freeze the serialization UID. */
036   static final long serialVersionUID = -2895662600168468559L;
037   /** Debug flag */
038   private static final boolean DEBUG = false;
039 
040   /**
041    * Just information keeper inner class. No significant functionality.
042    */
043   public class PositionInfo implements Serializable {
044 
045     /** Freeze the serialization UID. */
046     static final long serialVersionUID = -7747351720249898499L;
047 
048     /** Data members for one peace of text information */
049     private long m_origPos, m_origLength, m_currPos, m_currLength;
050 
051     /** The only constructor. We haven't set methods for data members. */
052     public PositionInfo(long orig, long origLen, long curr, long currLen) {
053       m_origPos = orig;
054       m_origLength = origLen;
055       m_currPos = curr;
056       m_currLength = currLen;
057     // PositionInfo
058 
059     /** Position in the extracted (and probably changed) content */
060     public long getCurrentPosition() {
061       return m_currPos;
062     // getCurrentPosition
063 
064     /** Position in the original content */
065     public long getOriginalPosition() {
066       return m_origPos;
067     // getOriginalPosition
068 
069     /** Length of peace of text in the original content */
070     public long getOriginalLength() {
071       return m_origLength;
072     // getOriginalLength
073 
074     /** Length of peace of text in the extracted content */
075     public long getCurrentLength() {
076       return m_currLength;
077     // getCurrentLength
078 
079     /** For debug purposes */
080     @Override
081     public String toString() {
082       return "("+m_origPos+","+m_origLength+","
083                 +m_currPos+","+m_currLength+")";
084     // toString
085   // class PositionInfo
086 
087   /** Default constructor */
088   public RepositioningInfo() {
089     super();
090   // RepositioningInfo
091 
092   /** Create a new position information record. */
093   public void addPositionInfo(long origPos, long origLength,
094                               long currPos, long currLength) {
095     // sorted add of new position
096     int insertPos = 0;
097     PositionInfo lastPI;
098 
099     for(int i = size(); i>0; i--) {
100       lastPI = get(i-1);
101       if(lastPI.getOriginalPosition() < origPos) {
102         insertPos = i;
103         break;
104       // if - sort key
105     // for
106 
107     add(insertPos, new PositionInfo(origPos, origLength, currPos, currLength));
108   // addPositionInfo
109 
110   /** Compute position in extracted content by position in the original content.
111    *  If there is no correspondence return -1.
112    */
113   public long getExtractedPos(long absPos) {
114     long result = absPos;
115     PositionInfo currPI = null;
116     int size = size();
117 
118     if(size != 0) {
119       long origPos, origLen;
120       boolean found = false;
121 
122       for(int i=0; i<size; ++i) {
123         currPI = get(i);
124         origPos = currPI.getOriginalPosition();
125         origLen = currPI.getOriginalLength();
126 
127         if(absPos <= origPos+origLen) {
128           if(absPos < origPos) {
129             // outside the range of information
130             result = -1;
131           }
132           else {
133             if(absPos == origPos+origLen) {
134               // special case for boundaries - if absPos is the end boundary of
135               // this PositionInfo record (origPos+origLen) then result should
136               // always be the end boundary too (extracted pos + extracted len).
137               // Without this check we may get the wrong position when the "orig"
138               // length is shorter than the "extracted" length.
139               result = currPI.getCurrentPosition() + currPI.getCurrentLength();
140             else {
141               // current position + offset in this PositionInfo record
142               result = currPI.getCurrentPosition() + absPos - origPos;
143             }
144             // but don't go beyond the extracted length
145             if(result > currPI.getCurrentPosition() + currPI.getCurrentLength()) {
146               result = currPI.getCurrentPosition() + currPI.getCurrentLength();
147             }
148           // if
149           found = true;
150           break;
151         // if
152       // for
153 
154       if(!found) {
155         // after the last repositioning info
156         result = -1;
157       // if - !found
158     // if
159 
160     return result;
161   // getExtractedPos
162 
163   public long getOriginalPos(long relPos) {
164     return getOriginalPos(relPos, false);
165   // getOriginalPos
166 
167   /** Compute position in original content by position in the extracted content.
168    *  If there is no correspondence return -1.
169    */
170   public long getOriginalPos(long relPos, boolean afterChar) {
171     long result = relPos;
172     PositionInfo currPI = null;
173     int size = size();
174 
175     if(size != 0) {
176       long currPos, currLen;
177       boolean found = false;
178 
179       for(int i=0; i<size; ++i) {
180         currPI = get(i);
181         currPos = currPI.getCurrentPosition();
182         currLen = currPI.getCurrentLength();
183 
184         if(afterChar && relPos == currPos+currLen) {
185           result = currPI.getOriginalPosition() + currPI.getOriginalLength();
186           found = true;
187           break;
188         // if
189 
190         if(relPos < currPos+currLen) {
191           if(relPos < currPos) {
192             // outside the range of information
193             result = -1;
194           }
195           else {
196             // current position + offset in this PositionInfo record
197             result = currPI.getOriginalPosition() + relPos - currPos;
198           // if
199           found = true;
200           break;
201         // if
202       // for
203 
204       if(!found) {
205         // after the last repositioning info
206         result = -1;
207       // if - !found
208     // if
209 
210     return result;
211   // getOriginalPos
212 
213   /** Not finished yet */
214   public long getExtractedPosFlow(long absPos) {
215     long result = -1;
216     return result;
217   // getExtractedPosFlow
218 
219   /** Not finished yet */
220   public long getOriginalPosFlow(long relPos) {
221     long result = -1;
222     return result;
223   // getOriginalPosFlow
224 
225   /**
226    * Return the position info index containing <B>@param absPos</B>
227    * If there is no such position info return -1.
228    */
229   public int getIndexByOriginalPosition(long absPos) {
230     PositionInfo currPI = null;
231     int result = -1;
232 
233     int size = size();
234     long origPos, origLen;
235 
236     // Find with the liniear algorithm. Could be extended to binary search.
237     for(int i=0; i<size; ++i) {
238       currPI = get(i);
239       origPos = currPI.getOriginalPosition();
240       origLen = currPI.getOriginalLength();
241 
242       if(absPos <= origPos+origLen) {
243         if(absPos >= origPos) {
244           result = i;
245         // if
246         break;
247       // if
248     // for
249 
250     return result;
251   // getItemByOriginalPosition
252 
253   /**
254    * Return the position info index containing <B>@param absPos</B>
255    * or the index of record before this position.
256    * Result is -1 if the position is before the first record.
257    * Rezult is size() if the position is after the last record.
258    */
259   public int getIndexByOriginalPositionFlow(long absPos) {
260     PositionInfo currPI = null;
261 
262     int size = size();
263     int result = size;
264     long origPos, origLen;
265 
266     // Find with the liniear algorithm. Could be extended to binary search.
267     for(int i=0; i<size; ++i) {
268       currPI = get(i);
269       origPos = currPI.getOriginalPosition();
270       origLen = currPI.getOriginalLength();
271 
272       if(absPos <= origPos+origLen) {
273         // is inside of current record
274         if(absPos >= origPos) {
275           result = i;
276         }
277         else {
278           // not inside the current recort - return previous
279           result = i-1;
280         // if
281         break;
282       // if
283     // for
284 
285     return result;
286   // getItemByOriginalPositionFlow
287 
288   /**
289    *  Correct the RepositioningInfo structure for shrink/expand changes.
290    *  <br>
291    *
292    *  Normaly the text peaces have same sizes in both original text and
293    *  extracted text. But in some cases there are nonlinear substitutions.
294    *  For example the sequence "&lt;" is converted to "<".
295    *  <br>
296    *
297    *  The correction will split the corresponding PositionInfo structure to
298    *  3 new records - before correction, correction record and after correction.
299    *  Front and end records are the same maner like the original record -
300    *  m_origLength == m_currLength, since the middle record has different
301    *  values because of shrink/expand changes. All records after this middle
302    *  record should be corrected with the difference between these values.
303    *  <br>
304    *
305    *  All m_currPos above the current information record should be corrected
306    *  with (origLen - newLen) i.e.
307    *  <code> m_currPos -= origLen - newLen; </code>
308    *  <br>
309    *
310    *  @param originalPos Position of changed text in the original content.
311    *  @param origLen Length of changed peace of text in the original content.
312    *  @param newLen Length of new peace of text substiting the original peace.
313    */
314   public void correctInformation(long originalPos, long origLen, long newLen) {
315     PositionInfo currPI;
316     PositionInfo frontPI, correctPI, endPI;
317 
318     int index = getIndexByOriginalPositionFlow(originalPos);
319 
320     // correct the index when the originalPos precede all records
321     if(index == -1) {
322       index = 0;
323     // if
324 
325    // correction of all other information records
326     // All m_currPos above the current record should be corrected with
327     // (origLen - newLen) i.e. <code> m_currPos -= origLen - newLen; </code>
328 
329     for(int i=index; i<size(); ++i) {
330       currPI = get(i);
331       currPI.m_currPos -= origLen - newLen;
332     // for
333 
334     currPI = get(index);
335     if(originalPos >= currPI.m_origPos
336         && currPI.m_origPos + currPI.m_origLength >= originalPos + origLen) {
337       long frontLen = originalPos - currPI.m_origPos;
338 
339       frontPI = new PositionInfo(currPI.m_origPos,
340                               frontLen,
341                               currPI.m_currPos,
342                               frontLen);
343       correctPI = new PositionInfo(originalPos,
344                               origLen,
345                               currPI.m_currPos + frontLen,
346                               newLen);
347       long endLen = currPI.m_origLength - frontLen - origLen;
348       endPI = new PositionInfo(originalPos + origLen,
349                               endLen,
350                               currPI.m_currPos + frontLen + newLen,
351                               endLen);
352 
353       set(index, frontPI)// substitute old element
354       if(endPI.m_origLength > 0) {
355         add(index+1, endPI)// insert new end element
356       // if
357       if(correctPI.m_origLength > 0) {
358         add(index+1, correctPI)// insert middle new element
359       // if
360     // if - substitution range check
361   // correctInformation
362 
363   /**
364    *  Correct the original position information in the records. When some text
365    *  is shrinked/expanded by the parser. With this method is corrected the
366    *  substitution of "\r\n" with "\n".
367    */
368   public void correctInformationOriginalMove(long originalPos, long moveLen) {
369     PositionInfo currPI;
370 
371     if(DEBUG) {
372       if(originalPos < 380// debug information restriction
373         Out.println("Before correction: "+this);
374     // DEBUG
375 
376     int index = getIndexByOriginalPositionFlow(originalPos);
377 
378     // correct the index when the originalPos precede all records
379     if(index == -1) {
380       index = 0;
381     // if
382 
383     // position is after all records in list
384     if(index == size()) {
385       return;
386     // if
387 
388     for(int i = index+1; i<size(); ++i) {
389       currPI = get(i);
390       currPI.m_origPos += moveLen;
391     // for
392 
393     currPI = get(index);
394 
395     // should we split this record to two new records (inside the record)
396     if(originalPos > currPI.m_origPos) {
397       if(originalPos < currPI.m_origPos + currPI.m_origLength) {
398         PositionInfo frontPI, endPI;
399         long frontLen = originalPos - currPI.m_origPos;
400         frontPI = new PositionInfo(currPI.m_origPos,
401                                 frontLen,
402                                 currPI.m_currPos,
403                                 frontLen);
404 
405         long endLen = currPI.m_origLength - frontLen;
406         endPI = new PositionInfo(originalPos + moveLen,
407                                 endLen,
408                                 currPI.m_currPos + frontLen,
409                                 endLen);
410         set(index, frontPI)// substitute old element
411         if(endPI.m_origLength != 0) {
412           add(index+1, endPI)// insert new end element
413         // if - should add this record
414 
415         if(DEBUG) {
416           if(originalPos < 380) { // debug information restriction
417             Out.println("Point 2. Current: "+currPI);
418             Out.println("Point 2. frontPI: "+frontPI);
419             Out.println("Point 2. endPI: "+endPI);
420           }
421         // DEBUG
422       // if - inside the record
423     // if
424     else {
425       // correction if the position is before the current record
426       currPI.m_origPos += moveLen;
427     }
428 
429     if(DEBUG) {
430       if(originalPos < 380) {
431         Out.println("Correction move: "+originalPos+", "+moveLen);
432         Out.println("Corrected: "+this);
433         Out.println("index: "+index);
434         /*
435         Exception ex = new Exception();
436         Out.println("Call point: ");
437         ex.printStackTrace();
438         */
439       }
440     // DEBUG
441   // correctInformationOriginalMove
442 
443 // class RepositioningInfo