001: /* ====================================================================
002: Licensed to the Apache Software Foundation (ASF) under one or more
003: contributor license agreements. See the NOTICE file distributed with
004: this work for additional information regarding copyright ownership.
005: The ASF licenses this file to You under the Apache License, Version 2.0
006: (the "License"); you may not use this file except in compliance with
007: the License. You may obtain a copy of the License at
008:
009: http://www.apache.org/licenses/LICENSE-2.0
010:
011: Unless required by applicable law or agreed to in writing, software
012: distributed under the License is distributed on an "AS IS" BASIS,
013: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: See the License for the specific language governing permissions and
015: limitations under the License.
016: ==================================================================== */
017:
018: package org.apache.poi.hwpf.usermodel;
019:
020: import org.apache.poi.util.LittleEndian;
021:
022: import org.apache.poi.hwpf.HWPFDocument;
023:
024: import org.apache.poi.hwpf.usermodel.CharacterRun;
025: import org.apache.poi.hwpf.usermodel.Paragraph;
026: import org.apache.poi.hwpf.usermodel.ParagraphProperties;
027: import org.apache.poi.hwpf.usermodel.Section;
028:
029: import org.apache.poi.hwpf.model.PropertyNode;
030: import org.apache.poi.hwpf.model.StyleSheet;
031: import org.apache.poi.hwpf.model.CHPX;
032: import org.apache.poi.hwpf.model.PAPX;
033: import org.apache.poi.hwpf.model.SEPX;
034: import org.apache.poi.hwpf.model.TextPiece;
035: import org.apache.poi.hwpf.model.ListTables;
036:
037: import org.apache.poi.hwpf.sprm.CharacterSprmCompressor;
038: import org.apache.poi.hwpf.sprm.ParagraphSprmCompressor;
039: import org.apache.poi.hwpf.sprm.SprmBuffer;
040:
041: import java.util.List;
042: import java.util.NoSuchElementException;
043: import java.lang.ref.WeakReference;
044:
045: /**
046: * This class is the central class of the HWPF object model. All properties
047: * that apply to a range of characters in a Word document extend this class.
048: *
049: * It is possible to insert text and/or properties at the beginning or end of a
050: * range.
051: *
052: * Ranges are only valid if there hasn't been an insert in a prior Range since
053: * the Range's creation. Once an element (text, paragraph, etc.) has been
054: * inserted into a Range, subsequent Ranges become unstable.
055: *
056: * @author Ryan Ackley
057: */
058: public class Range {
059:
060: public static final int TYPE_PARAGRAPH = 0;
061: public static final int TYPE_CHARACTER = 1;
062: public static final int TYPE_SECTION = 2;
063: public static final int TYPE_TEXT = 3;
064: public static final int TYPE_LISTENTRY = 4;
065: public static final int TYPE_TABLE = 5;
066: public static final int TYPE_UNDEFINED = 6;
067:
068: /** Needed so inserts and deletes will ripple up through containing Ranges */
069: private WeakReference _parent;
070:
071: /** The starting character offset of this range.*/
072: protected int _start;
073:
074: /** The ending character offset of this range.*/
075: protected int _end;
076:
077: /** The document this range blongs to.*/
078: protected HWPFDocument _doc;
079:
080: /** Have we loaded the section indexes yet*/
081: boolean _sectionRangeFound;
082:
083: /** All sections that belong to the document this Range belongs to.*/
084: protected List _sections;
085:
086: /** The start index in the sections list for this Range*/
087: protected int _sectionStart;
088:
089: /** The end index in the sections list for this Range.*/
090: protected int _sectionEnd;
091:
092: /** Have we loaded the paragraph indexes yet.*/
093: protected boolean _parRangeFound;
094:
095: /** All paragraphs that belong to the document this Range belongs to.*/
096: protected List _paragraphs;
097:
098: /** The start index in the paragraphs list for this Range*/
099: protected int _parStart;
100:
101: /** The end index in the paragraphs list for this Range.*/
102: protected int _parEnd;
103:
104: /** Have we loaded the characterRun indexes yet.*/
105: protected boolean _charRangeFound;
106:
107: /** All CharacterRuns that belong to the document this Range belongs to.*/
108: protected List _characters;
109:
110: /** The start index in the characterRuns list for this Range*/
111: protected int _charStart;
112:
113: /** The end index in the characterRuns list for this Range. */
114: protected int _charEnd;
115:
116: /** Have we loaded the Text indexes yet*/
117: protected boolean _textRangeFound;
118:
119: /** All text pieces that belong to the document this Range belongs to.*/
120: protected List _text;
121:
122: /** The start index in the text list for this Range.*/
123: protected int _textStart;
124:
125: /** The end index in the text list for this Range.*/
126: protected int _textEnd;
127:
128: // protected Range()
129: // {
130: //
131: // }
132:
133: /**
134: * Used to construct a Range from a document. This is generally used to
135: * create a Range that spans the whole document.
136: *
137: * @param start Starting character offset of the range.
138: * @param end Ending character offset of the range.
139: * @param doc The HWPFDocument the range is based on.
140: */
141: public Range(int start, int end, HWPFDocument doc) {
142: _start = start;
143: _end = end;
144: _doc = doc;
145: _sections = _doc.getSectionTable().getSections();
146: _paragraphs = _doc.getParagraphTable().getParagraphs();
147: _characters = _doc.getCharacterTable().getTextRuns();
148: _text = _doc.getTextTable().getTextPieces();
149: _parent = new WeakReference(null);
150: }
151:
152: /**
153: * Used to create Ranges that are children of other Ranges.
154: *
155: * @param start Starting character offset of the range.
156: * @param end Ending character offset of the range.
157: * @param parent The parent this range belongs to.
158: */
159: protected Range(int start, int end, Range parent) {
160: _start = start;
161: _end = end;
162: _doc = parent._doc;
163: _sections = parent._sections;
164: _paragraphs = parent._paragraphs;
165: _characters = parent._characters;
166: _text = parent._text;
167: _parent = new WeakReference(parent);
168: }
169:
170: /**
171: * Constructor used to build a Range from indexes in one of its internal
172: * lists.
173: *
174: * @param startIdx The starting index in the list.
175: * @param endIdx The ending index in the list.
176: * @param idxType The list type.
177: * @param parent The parent Range this range belongs to.
178: */
179: protected Range(int startIdx, int endIdx, int idxType, Range parent) {
180: _doc = parent._doc;
181: _sections = parent._sections;
182: _paragraphs = parent._paragraphs;
183: _characters = parent._characters;
184: _text = parent._text;
185: _parent = new WeakReference(parent);
186:
187: switch (idxType) {
188: case TYPE_PARAGRAPH:
189: _parStart = parent._parStart + startIdx;
190: _parEnd = parent._parStart + endIdx;
191: _start = ((PropertyNode) _paragraphs.get(_parStart))
192: .getStart();
193: _end = ((PropertyNode) _paragraphs.get(_parEnd)).getEnd();
194: _parRangeFound = true;
195: break;
196: case TYPE_CHARACTER:
197: _charStart = parent._charStart + startIdx;
198: _charEnd = parent._charStart + endIdx;
199: _start = ((PropertyNode) _characters.get(_charStart))
200: .getStart();
201: _end = ((PropertyNode) _characters.get(_charEnd)).getEnd();
202: _charRangeFound = true;
203: break;
204: case TYPE_SECTION:
205: _sectionStart = parent._sectionStart + startIdx;
206: _sectionEnd = parent._sectionStart + endIdx;
207: _start = ((PropertyNode) _sections.get(_sectionStart))
208: .getStart();
209: _end = ((PropertyNode) _sections.get(_sectionEnd)).getEnd();
210: _sectionRangeFound = true;
211: break;
212: case TYPE_TEXT:
213: _textStart = parent._textStart + startIdx;
214: _textEnd = parent._textStart + endIdx;
215: _start = ((PropertyNode) _text.get(_textStart)).getStart();
216: _end = ((PropertyNode) _text.get(_textEnd)).getEnd();
217: _textRangeFound = true;
218: break;
219: }
220: }
221:
222: /**
223: * Gets the text that this Range contains.
224: *
225: * @return The text for this range.
226: */
227: public String text() {
228: initText();
229:
230: StringBuffer sb = new StringBuffer();
231:
232: for (int x = _textStart; x < _textEnd; x++) {
233: TextPiece piece = (TextPiece) _text.get(x);
234: int start = _start > piece.getStart() ? _start
235: - piece.getStart() : 0;
236: int end = _end <= piece.getEnd() ? _end - piece.getStart()
237: : piece.getEnd() - piece.getStart();
238:
239: if (piece.usesUnicode()) // convert the byte pointers to char pointers
240: {
241: start /= 2;
242: end /= 2;
243: }
244: sb.append(piece.getStringBuffer().substring(start, end));
245: }
246: return sb.toString();
247: }
248:
249: /**
250: * Used to get the number of sections in a range. If this range is smaller
251: * than a section, it will return 1 for its containing section.
252: *
253: * @return The number of sections in this range.
254: */
255: public int numSections() {
256: initSections();
257: return _sectionEnd - _sectionStart;
258: }
259:
260: /**
261: * Used to get the number of paragraphs in a range. If this range is smaller
262: * than a paragraph, it will return 1 for its containing paragraph.
263: *
264: * @return The number of paragraphs in this range.
265: */
266:
267: public int numParagraphs() {
268: initParagraphs();
269: return _parEnd - _parStart;
270: }
271:
272: /**
273: *
274: * @return The number of characterRuns in this range.
275: */
276:
277: public int numCharacterRuns() {
278: initCharacterRuns();
279: return _charEnd - _charStart;
280: }
281:
282: /**
283: * Inserts text into the front of this range.
284: *
285: * @param text The text to insert
286: * @return The character run that text was inserted into.
287: */
288: public CharacterRun insertBefore(String text)
289: //throws UnsupportedEncodingException
290: {
291: initAll();
292:
293: TextPiece tp = (TextPiece) _text.get(_textStart);
294: StringBuffer sb = (StringBuffer) tp.getStringBuffer();
295:
296: // Since this is the first item in our list, it is safe to assume that
297: // _start >= tp.getStart()
298: int insertIndex = _start - tp.getStart();
299: sb.insert(insertIndex, text);
300: int adjustedLength = _doc.getTextTable().adjustForInsert(
301: _textStart, text.length());
302: _doc.getCharacterTable().adjustForInsert(_charStart,
303: adjustedLength);
304: _doc.getParagraphTable().adjustForInsert(_parStart,
305: adjustedLength);
306: _doc.getSectionTable().adjustForInsert(_sectionStart,
307: adjustedLength);
308: adjustForInsert(text.length());
309:
310: return getCharacterRun(0);
311: }
312:
313: /**
314: * Inserts text onto the end of this range
315: *
316: * @param text The text to insert
317: * @return The character run the text was inserted into.
318: */
319: public CharacterRun insertAfter(String text) {
320: initAll();
321:
322: int listIndex = _textEnd - 1;
323: TextPiece tp = (TextPiece) _text.get(listIndex);
324: StringBuffer sb = (StringBuffer) tp.getStringBuffer();
325:
326: int insertIndex = _end - tp.getStart();
327:
328: if (tp.getStringBuffer().charAt(_end - 1) == '\r'
329: && text.charAt(0) != '\u0007') {
330: insertIndex--;
331: }
332: sb.insert(insertIndex, text);
333: int adjustedLength = _doc.getTextTable().adjustForInsert(
334: listIndex, text.length());
335: _doc.getCharacterTable().adjustForInsert(_charEnd - 1,
336: adjustedLength);
337: _doc.getParagraphTable().adjustForInsert(_parEnd - 1,
338: adjustedLength);
339: _doc.getSectionTable().adjustForInsert(_sectionEnd - 1,
340: adjustedLength);
341: adjustForInsert(text.length());
342:
343: return getCharacterRun(numCharacterRuns() - 1);
344:
345: }
346:
347: /**
348: * Inserts text into the front of this range and it gives that text the
349: * CharacterProperties specified in props.
350: *
351: * @param text The text to insert.
352: * @param props The CharacterProperties to give the text.
353: * @return A new CharacterRun that has the given text and properties and is n
354: * ow a part of the document.
355: */
356: public CharacterRun insertBefore(String text,
357: CharacterProperties props)
358: //throws UnsupportedEncodingException
359: {
360: initAll();
361: PAPX papx = (PAPX) _paragraphs.get(_parStart);
362: short istd = papx.getIstd();
363:
364: StyleSheet ss = _doc.getStyleSheet();
365: CharacterProperties baseStyle = ss.getCharacterStyle(istd);
366: byte[] grpprl = CharacterSprmCompressor
367: .compressCharacterProperty(props, baseStyle);
368: SprmBuffer buf = new SprmBuffer(grpprl);
369: _doc.getCharacterTable().insert(_charStart, _start, buf);
370:
371: return insertBefore(text);
372: }
373:
374: /**
375: * Inserts text onto the end of this range and gives that text the
376: * CharacterProperties specified in props.
377: *
378: * @param text The text to insert.
379: * @param props The CharacterProperties to give the text.
380: * @return A new CharacterRun that has the given text and properties and is n
381: * ow a part of the document.
382: */
383: public CharacterRun insertAfter(String text,
384: CharacterProperties props)
385: //throws UnsupportedEncodingException
386: {
387: initAll();
388: PAPX papx = (PAPX) _paragraphs.get(_parEnd - 1);
389: short istd = papx.getIstd();
390:
391: StyleSheet ss = _doc.getStyleSheet();
392: CharacterProperties baseStyle = ss.getCharacterStyle(istd);
393: byte[] grpprl = CharacterSprmCompressor
394: .compressCharacterProperty(props, baseStyle);
395: SprmBuffer buf = new SprmBuffer(grpprl);
396: _doc.getCharacterTable().insert(_charEnd, _end, buf);
397: _charEnd++;
398: return insertAfter(text);
399: }
400:
401: /**
402: * Inserts and empty paragraph into the front of this range.
403: *
404: * @param props The properties that the new paragraph will have.
405: * @param styleIndex The index into the stylesheet for the new paragraph.
406: * @return The newly inserted paragraph.
407: */
408: public Paragraph insertBefore(ParagraphProperties props,
409: int styleIndex)
410: //throws UnsupportedEncodingException
411: {
412: return this .insertBefore(props, styleIndex, "\r");
413: }
414:
415: /**
416: * Inserts a paragraph into the front of this range. The paragraph will
417: * contain one character run that has the default properties for the
418: * paragraph's style.
419: *
420: * It is necessary for the text to end with the character '\r'
421: *
422: * @param props The paragraph's properties.
423: * @param styleIndex The index of the paragraph's style in the style sheet.
424: * @param text The text to insert.
425: * @return A newly inserted paragraph.
426: */
427: protected Paragraph insertBefore(ParagraphProperties props,
428: int styleIndex, String text)
429: //throws UnsupportedEncodingException
430: {
431: initAll();
432: StyleSheet ss = _doc.getStyleSheet();
433: ParagraphProperties baseStyle = ss
434: .getParagraphStyle(styleIndex);
435: CharacterProperties baseChp = ss.getCharacterStyle(styleIndex);
436:
437: byte[] grpprl = ParagraphSprmCompressor
438: .compressParagraphProperty(props, baseStyle);
439: byte[] withIndex = new byte[grpprl.length
440: + LittleEndian.SHORT_SIZE];
441: LittleEndian.putShort(withIndex, (short) styleIndex);
442: System.arraycopy(grpprl, 0, withIndex, LittleEndian.SHORT_SIZE,
443: grpprl.length);
444: SprmBuffer buf = new SprmBuffer(withIndex);
445:
446: _doc.getParagraphTable().insert(_parStart, _start, buf);
447: insertBefore(text, baseChp);
448: return getParagraph(0);
449: }
450:
451: /**
452: * Inserts and empty paragraph into the end of this range.
453: *
454: * @param props The properties that the new paragraph will have.
455: * @param styleIndex The index into the stylesheet for the new paragraph.
456: * @return The newly inserted paragraph.
457: */
458:
459: public Paragraph insertAfter(ParagraphProperties props,
460: int styleIndex)
461: //throws UnsupportedEncodingException
462: {
463: return this .insertAfter(props, styleIndex, "\r");
464: }
465:
466: /**
467: * Inserts a paragraph into the end of this range. The paragraph will
468: * contain one character run that has the default properties for the
469: * paragraph's style.
470: *
471: * It is necessary for the text to end with the character '\r'
472: *
473: * @param props The paragraph's properties.
474: * @param styleIndex The index of the paragraph's style in the style sheet.
475: * @param text The text to insert.
476: * @return A newly inserted paragraph.
477: */
478: protected Paragraph insertAfter(ParagraphProperties props,
479: int styleIndex, String text)
480: //throws UnsupportedEncodingException
481: {
482: initAll();
483: StyleSheet ss = _doc.getStyleSheet();
484: ParagraphProperties baseStyle = ss
485: .getParagraphStyle(styleIndex);
486: CharacterProperties baseChp = ss.getCharacterStyle(styleIndex);
487:
488: byte[] grpprl = ParagraphSprmCompressor
489: .compressParagraphProperty(props, baseStyle);
490: byte[] withIndex = new byte[grpprl.length
491: + LittleEndian.SHORT_SIZE];
492: LittleEndian.putShort(withIndex, (short) styleIndex);
493: System.arraycopy(grpprl, 0, withIndex, LittleEndian.SHORT_SIZE,
494: grpprl.length);
495: SprmBuffer buf = new SprmBuffer(withIndex);
496:
497: _doc.getParagraphTable().insert(_parEnd, _end, buf);
498: _parEnd++;
499: insertAfter(text, baseChp);
500: return getParagraph(numParagraphs() - 1);
501: }
502:
503: public void delete() {
504: initAll();
505:
506: int numSections = _sections.size();
507: int numRuns = _characters.size();
508: int numParagraphs = _paragraphs.size();
509:
510: for (int x = _charStart; x < numRuns; x++) {
511: CHPX chpx = (CHPX) _characters.get(x);
512: chpx.adjustForDelete(_start, _end - _start);
513: }
514:
515: for (int x = _parStart; x < numParagraphs; x++) {
516: PAPX papx = (PAPX) _paragraphs.get(x);
517: papx.adjustForDelete(_start, _end - _start);
518: }
519:
520: for (int x = _sectionStart; x < numSections; x++) {
521: SEPX sepx = (SEPX) _sections.get(x);
522: sepx.adjustForDelete(_start, _end - _start);
523: }
524: }
525:
526: /**
527: * Inserts a simple table into the beginning of this range. The number of
528: * columns is determined by the TableProperties passed into this function.
529: *
530: * @param props The table properties for the table.
531: * @param rows The number of rows.
532: * @return The empty Table that is now part of the document.
533: */
534: public Table insertBefore(TableProperties props, int rows) {
535: ParagraphProperties parProps = new ParagraphProperties();
536: parProps.setFInTable((byte) 1);
537: parProps.setTableLevel((byte) 1);
538:
539: int columns = props.getItcMac();
540: for (int x = 0; x < rows; x++) {
541: Paragraph cell = this .insertBefore(parProps,
542: StyleSheet.NIL_STYLE);
543: cell.insertAfter(String.valueOf('\u0007'));
544: for (int y = 1; y < columns; y++) {
545: cell = cell.insertAfter(parProps, StyleSheet.NIL_STYLE);
546: cell.insertAfter(String.valueOf('\u0007'));
547: }
548: cell = cell.insertAfter(parProps, StyleSheet.NIL_STYLE,
549: String.valueOf('\u0007'));
550: cell.setTableRowEnd(props);
551: }
552: return new Table(_start, _start + (rows * (columns + 1)), this ,
553: 1);
554: }
555:
556: /**
557: * Inserts a list into the beginning of this range.
558: *
559: * @param props The properties of the list entry. All list entries are
560: * paragraphs.
561: * @param listID The id of the list that contains the properties.
562: * @param level The indentation level of the list.
563: * @param styleIndex The base style's index in the stylesheet.
564: * @return The empty ListEntry that is now part of the document.
565: */
566: public ListEntry insertBefore(ParagraphProperties props,
567: int listID, int level, int styleIndex) {
568: ListTables lt = _doc.getListTables();
569: if (lt.getLevel(listID, level) == null) {
570: throw new NoSuchElementException(
571: "The specified list and level do not exist");
572: }
573:
574: int ilfo = lt.getOverrideIndexFromListID(listID);
575: props.setIlfo(ilfo);
576: props.setIlvl((byte) level);
577:
578: return (ListEntry) insertBefore(props, styleIndex);
579: }
580:
581: /**
582: * Inserts a list into the beginning of this range.
583: *
584: * @param props The properties of the list entry. All list entries are
585: * paragraphs.
586: * @param listID The id of the list that contains the properties.
587: * @param level The indentation level of the list.
588: * @param styleIndex The base style's index in the stylesheet.
589: * @return The empty ListEntry that is now part of the document.
590: */
591: public ListEntry insertAfter(ParagraphProperties props, int listID,
592: int level, int styleIndex) {
593: ListTables lt = _doc.getListTables();
594: if (lt.getLevel(listID, level) == null) {
595: throw new NoSuchElementException(
596: "The specified list and level do not exist");
597: }
598: int ilfo = lt.getOverrideIndexFromListID(listID);
599: props.setIlfo(ilfo);
600: props.setIlvl((byte) level);
601:
602: return (ListEntry) insertAfter(props, styleIndex);
603: }
604:
605: /**
606: * Gets the character run at index. The index is relative to this range.
607: *
608: * @param index The index of the character run to get.
609: * @return The character run at the specified index in this range.
610: */
611: public CharacterRun getCharacterRun(int index) {
612: initCharacterRuns();
613: CHPX chpx = (CHPX) _characters.get(index + _charStart);
614:
615: int[] point = findRange(_paragraphs, _parStart, Math.max(chpx
616: .getStart(), _start), chpx.getEnd());
617: PAPX papx = (PAPX) _paragraphs.get(point[0]);
618: short istd = papx.getIstd();
619:
620: CharacterRun chp = new CharacterRun(chpx, _doc.getStyleSheet(),
621: istd, this );
622:
623: return chp;
624: }
625:
626: /**
627: * Gets the section at index. The index is relative to this range.
628: *
629: * @param index The index of the section to get.
630: * @return The section at the specified index in this range.
631: */
632: public Section getSection(int index) {
633: initSections();
634: SEPX sepx = (SEPX) _sections.get(index + _sectionStart);
635: Section sep = new Section(sepx, this );
636: return sep;
637: }
638:
639: /**
640: * Gets the paragraph at index. The index is relative to this range.
641: *
642: * @param index The index of the paragraph to get.
643: * @return The paragraph at the specified index in this range.
644: */
645:
646: public Paragraph getParagraph(int index) {
647: initParagraphs();
648: PAPX papx = (PAPX) _paragraphs.get(index + _parStart);
649:
650: ParagraphProperties props = papx.getParagraphProperties(_doc
651: .getStyleSheet());
652: Paragraph pap = null;
653: if (props.getIlfo() > 0) {
654: pap = new ListEntry(papx, this , _doc.getListTables());
655: } else {
656: pap = new Paragraph(papx, this );
657: }
658:
659: return pap;
660: }
661:
662: /**
663: * This method is used to determine the type. Handy for switch statements
664: * compared to the instanceof operator.
665: *
666: * @return A TYPE constant.
667: */
668: public int type() {
669: return TYPE_UNDEFINED;
670: }
671:
672: /**
673: * Gets the table that starts with paragraph. In a Word file, a table consists
674: * of a group of paragraphs with certain flags set.
675: *
676: * @param paragraph The paragraph that is the first paragraph in the table.
677: * @return The table that starts with paragraph
678: */
679: public Table getTable(Paragraph paragraph) {
680: if (!paragraph.isInTable()) {
681: throw new IllegalArgumentException(
682: "This paragraph doesn't belong to a table");
683: }
684:
685: Range r = (Range) paragraph;
686: if (r._parent.get() != this ) {
687: throw new IllegalArgumentException(
688: "This paragraph is not a child of this range");
689: }
690:
691: r.initAll();
692: int tableEnd = r._parEnd;
693:
694: if (r._parStart != 0
695: && getParagraph(r._parStart - 1).isInTable()
696: && getParagraph(r._parStart - 1)._sectionEnd >= r._sectionStart) {
697: throw new IllegalArgumentException(
698: "This paragraph is not the first one in the table");
699: }
700:
701: int limit = _paragraphs.size();
702: for (; tableEnd < limit; tableEnd++) {
703: if (!getParagraph(tableEnd).isInTable()) {
704: break;
705: }
706: }
707:
708: initAll();
709: if (tableEnd > _parEnd) {
710: throw new ArrayIndexOutOfBoundsException(
711: "The table's bounds fall outside of this Range");
712: }
713: return new Table(r._parStart, tableEnd, r._doc.getRange(),
714: paragraph.getTableLevel());
715: }
716:
717: /**
718: * loads all of the list indexes.
719: */
720: protected void initAll() {
721: initText();
722: initCharacterRuns();
723: initParagraphs();
724: initSections();
725: }
726:
727: /**
728: * inits the paragraph list indexes.
729: */
730: private void initParagraphs() {
731: if (!_parRangeFound) {
732: int[] point = findRange(_paragraphs, _parStart, _start,
733: _end);
734: _parStart = point[0];
735: _parEnd = point[1];
736: _parRangeFound = true;
737: }
738: }
739:
740: /**
741: * inits the character run list indexes.
742: */
743: private void initCharacterRuns() {
744: if (!_charRangeFound) {
745: int[] point = findRange(_characters, _charStart, _start,
746: _end);
747: _charStart = point[0];
748: _charEnd = point[1];
749: _charRangeFound = true;
750: }
751: }
752:
753: /**
754: * inits the text piece list indexes.
755: */
756: private void initText() {
757: if (!_textRangeFound) {
758: int[] point = findRange(_text, _textStart, _start, _end);
759: _textStart = point[0];
760: _textEnd = point[1];
761: _textRangeFound = true;
762: }
763: }
764:
765: /**
766: * inits the section list indexes.
767: */
768: private void initSections() {
769: if (!_sectionRangeFound) {
770: int[] point = findRange(_sections, _sectionStart, _start,
771: _end);
772: _sectionStart = point[0];
773: _sectionEnd = point[1];
774: _sectionRangeFound = true;
775: }
776: }
777:
778: /**
779: * Used to find the list indexes of a particular property.
780: *
781: * @param rpl A list of property nodes.
782: * @param min A hint on where to start looking.
783: * @param start The starting character offset.
784: * @param end The ending character offset.
785: * @return An int array of length 2. The first int is the start index and the
786: * second int is the end index.
787: */
788: private int[] findRange(List rpl, int min, int start, int end) {
789: int x = min;
790: PropertyNode node = (PropertyNode) rpl.get(x);
791: while (node.getEnd() <= start && x < rpl.size() - 1) {
792: x++;
793: node = (PropertyNode) rpl.get(x);
794: }
795:
796: int y = x;
797: node = (PropertyNode) rpl.get(y);
798: while (node.getEnd() < end && y < rpl.size() - 1) {
799: y++;
800: node = (PropertyNode) rpl.get(y);
801: }
802: return new int[] { x, y + 1 };
803: }
804:
805: /**
806: * resets the list indexes.
807: */
808: private void reset() {
809: _textRangeFound = false;
810: _charRangeFound = false;
811: _parRangeFound = false;
812: _sectionRangeFound = false;
813: }
814:
815: /**
816: * adjust this range after an insert happens.
817: * @param length the length to adjust for
818: */
819: private void adjustForInsert(int length) {
820: _end += length;
821: reset();
822: Range parent = (Range) _parent.get();
823: if (parent != null) {
824: parent.adjustForInsert(length);
825: }
826: }
827:
828: }
|