001: /*BEGIN_COPYRIGHT_BLOCK
002: *
003: * Copyright (c) 2001-2007, JavaPLT group at Rice University (javaplt@rice.edu)
004: * All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions are met:
008: * * Redistributions of source code must retain the above copyright
009: * notice, this list of conditions and the following disclaimer.
010: * * Redistributions in binary form must reproduce the above copyright
011: * notice, this list of conditions and the following disclaimer in the
012: * documentation and/or other materials provided with the distribution.
013: * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
014: * names of its contributors may be used to endorse or promote products
015: * derived from this software without specific prior written permission.
016: *
017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
018: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
019: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
020: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
021: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
022: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
023: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
024: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
025: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
026: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
027: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028: *
029: * This software is Open Source Initiative approved Open Source Software.
030: * Open Source Initative Approved is a trademark of the Open Source Initiative.
031: *
032: * This file is part of DrJava. Download the current version of this project
033: * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
034: *
035: * END_COPYRIGHT_BLOCK*/
036:
037: package edu.rice.cs.drjava.model.definitions.reducedmodel;
038:
039: /**
040: * A refactoring of the common code between ReducedModelComment and
041: * ReducedModelBrace. Both of the refactored classes extend this class.
042: * @version $Id: AbstractReducedModel.java 4255 2007-08-28 19:17:37Z mgricken $
043: * @author JavaPLT
044: */
045: public abstract class AbstractReducedModel implements
046: ReducedModelStates {
047:
048: /** The character that represents the cursor in toString(). @see #toString() */
049: public static final char PTR_CHAR = '#';
050:
051: /** The reduced model for a document is a list of ReducedTokens (braces and gaps).
052: * @see ModelList
053: */
054: TokenList _tokens;
055:
056: /** Keeps track of cursor position in document.
057: * @see ModelList.Iterator
058: */
059: TokenList.Iterator _cursor;
060:
061: /** Constructor. Creates a new reduced model with the cursor at the start of a blank "page." */
062: public AbstractReducedModel() {
063: _tokens = new TokenList();
064: _cursor = _tokens._getIterator();
065: // we should be pointing to the head of the list
066: _cursor.setBlockOffset(0);
067: }
068:
069: /** Get the offset into the current ReducedToken.
070: * @return the number of characters into the token where the cursor sits
071: */
072: int getBlockOffset() {
073: return _cursor.getBlockOffset();
074: }
075:
076: /** Change the offset into the current ReducedToken.
077: * @param offset the number of characters into the token to set the cursor
078: */
079: void setBlockOffset(int offset) {
080: _cursor.setBlockOffset(offset);
081: }
082:
083: /** Package private absolute offset for tests. We don't keep track of absolute offset as it causes too much confusion
084: * and trouble.
085: */
086: int absOffset() {
087: int off = _cursor.getBlockOffset();
088: TokenList.Iterator it = _cursor._copy();
089: if (!it.atStart())
090: it.prev();
091:
092: while (!it.atStart()) {
093: off += it.current().getSize();
094: it.prev();
095: }
096: it.dispose();
097: return off;
098: }
099:
100: /** A toString() replacement for testing - easier to read. */
101: public String simpleString() {
102: final StringBuilder val = new StringBuilder();
103: ReducedToken tmp;
104:
105: TokenList.Iterator it = _tokens._getIterator();
106: it.next(); // since we start at the head, which has no current item
107:
108: if (_cursor.atStart())
109: val.append(PTR_CHAR).append(_cursor.getBlockOffset());
110:
111: while (!it.atEnd()) {
112: tmp = it.current();
113:
114: if (!_cursor.atStart() && !_cursor.atEnd()
115: && (tmp == _cursor.current())) {
116: val.append(PTR_CHAR).append(_cursor.getBlockOffset());
117: }
118:
119: val.append('|').append(tmp).append('|').append('\t');
120: it.next();
121: }
122:
123: if (_cursor.atEnd())
124: val.append(PTR_CHAR).append(_cursor.getBlockOffset());
125:
126: val.append("|end|");
127: it.dispose();
128: return val.toString();
129: }
130:
131: /** Inserts a character into the reduced model. A method to be implemented in each specific reduced
132: * sub-model. */
133: public abstract void insertChar(char ch);
134:
135: /**
136: * Inserts a block of text into the reduced model which has no
137: * special consideration in the reduced model.
138: * <OL>
139: * <li> atStart: if gap to right, augment first gap, else insert
140: * <li> atEnd: if gap to left, augment left gap, else insert
141: * <li> inside a gap: grow current gap, move offset by length
142: * <li> inside a multiple character brace:
143: * <ol>
144: * <li> break current brace
145: * <li> insert new gap
146: * </ol>
147: * <li> gap to left: grow that gap and set offset to zero
148: * <li> gap to right: this case handled by inside gap (offset invariant)
149: * <li> between two braces: insert new gap
150: * @param length the length of the inserted text
151: */
152: public void _insertGap(int length) {
153: if (_cursor.atStart()) {
154: if (_gapToRight()) {
155: _cursor.next();
156: _augmentCurrentGap(length); //increases gap and moves offset
157: } else
158: _insertNewGap(length);//inserts gap and goes to next item
159: } else if (_cursor.atEnd()) {
160: if (_gapToLeft()) {
161: _augmentGapToLeft(length);
162: //increases the gap to the left and
163: //cursor to next item in list leaving offset 0
164: } else
165: _insertNewGap(length); //inserts gap and moves to next item
166: }
167: // should we insert a Gap in between the characters of a multiple char brace
168: else if ((_cursor.getBlockOffset() > 0)
169: && _cursor.current().isMultipleCharBrace())
170: insertGapBetweenMultiCharBrace(length);
171: // inserting inside a Gap
172: else if (_cursor.current().isGap()) {
173: _cursor.current().grow(length);
174: _cursor.setBlockOffset(_cursor.getBlockOffset() + length);
175: } else if (!_cursor.atFirstItem() && _cursor.prevItem().isGap())
176: //already pointing to next item
177: _cursor.prevItem().grow(length);
178: else
179: //between two braces
180: _insertNewGap(length); //inserts a gap and goes to the next item
181: return;
182: }
183:
184: /**
185: * Inserts a gap between a multiple character brace.
186: * Because ReducedModelBrace does not keep track of multiple character
187: * braces, only (),{}, and [], it differed in its implementation of
188: * inserGap(int) from ReducedModelComment's. To pull out the otherwise
189: * identical code and place it here, we created this function to do
190: * something meaningful in ReducedModelComment and to throw an exception
191: * in ReducedModelBrace.
192: */
193: protected abstract void insertGapBetweenMultiCharBrace(int length);
194:
195: /**
196: * Make a copy of the token list's iterator.
197: */
198: public TokenList.Iterator makeCopyCursor() {
199: return _cursor._copy();
200: }
201:
202: /**
203: * Wrapper for TokenList.Iterator.getStateAtCurrent that returns the current
204: * state for some iterator.
205: * Convenience method to return the current state in the cursor iterator.
206: */
207: protected ReducedModelState getStateAtCurrent() {
208: return _cursor.getStateAtCurrent();
209: }
210:
211: /** Determines if there is a Gap immediately to the right of the cursor. */
212: protected boolean _gapToRight() {
213: // Before using, make sure not at last, or tail.
214: return (!_tokens.isEmpty() && !_cursor.atEnd()
215: && !_cursor.atLastItem() && _cursor.nextItem().isGap());
216: }
217:
218: /** Determines if there is a gap immediately to the left of the cursor. */
219: protected boolean _gapToLeft() {
220: // Before using, make sure not at first or head.
221: return (!_tokens.isEmpty() && !_cursor.atStart()
222: && !_cursor.atFirstItem() && _cursor.prevItem().isGap());
223: }
224:
225: /** Assuming there is a gap to the left, this function increases the size of that gap.
226: * @param length the amount of increase
227: */
228: protected void _augmentGapToLeft(int length) {
229: _cursor.prevItem().grow(length);
230: }
231:
232: /** Assuming there is a gap to the right, this function increases the size of that gap.
233: * @param length the amount of increase
234: */
235: protected void _augmentCurrentGap(int length) {
236: _cursor.current().grow(length);
237: _cursor.setBlockOffset(length);
238: }
239:
240: /** Helper function for _insertGap. Performs the actual insert and marks the offset appropriately.
241: * @param length size of gap to insert
242: */
243: protected void _insertNewGap(int length) {
244: _cursor.insert(new Gap(length, getStateAtCurrent()));
245: _cursor.next();
246: _cursor.setBlockOffset(0);
247: }
248:
249: /** Returns the state at the relLocation, where relLocation is the location
250: * relative to the walker
251: * @param relLocation distance from walker to get state at.
252: */
253: protected abstract ReducedModelState moveWalkerGetState(
254: int relLocation);
255:
256: /** Resets the walker to the current position in document. */
257: protected abstract void resetWalkerLocationToCursor();
258:
259: /**
260: * Get the ReducedToken currently pointed at by the cursor.
261: * @return the current token
262: */
263: protected ReducedToken current() {
264: return _cursor.current();
265: }
266:
267: /** Move to the token immediately right. This function forwards its responsibilities to the cursor.
268: * If the cursor is at the end, it will throw an exception.
269: */
270: protected void next() {
271: _cursor.next();
272: }
273:
274: /**
275: * Move to the token immediately left.
276: * This function forwards its responsibilities to the TokenList
277: * iterator. If the cursor is at the start, it will throw an
278: * exception.
279: */
280: protected void prev() {
281: _cursor.prev();
282: }
283:
284: }
|