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: import java.util.Stack;
040:
041: /** Keeps track of the true braces (i.e., "() {}[]"). This reduced sub-model is used to balance braces for both
042: * indenting and highlighting purposes. For example, when the user's caret is immediately after a closing brace,
043: * this allows the DefinitionsPane to produced a highlight extending from the closing brace to its match.
044: * @version $Id: ReducedModelBrace.java 4255 2007-08-28 19:17:37Z mgricken $
045: * @author JavaPLT
046: */
047: public class ReducedModelBrace extends AbstractReducedModel {
048:
049: private ReducedModelControl _parent; // contains the walker which is moved by moveWalkerGetState
050:
051: public ReducedModelBrace(ReducedModelControl parent) {
052: super ();
053: _parent = parent;
054: }
055:
056: public void insertChar(char ch) {
057: switch (ch) {
058: case '{':
059: case '}':
060: case '[':
061: case ']':
062: case '(':
063: case ')':
064: _insertBrace(String.valueOf(ch));
065: break;
066: default:
067: _insertGap(1);
068: break;
069: }
070: }
071:
072: /**
073: * Helper function for top level brace insert functions.
074: *
075: * <OL>
076: * <li> at Head: not special case
077: * <li> at Tail: not special case
078: * <li> between two things (offset is 0):
079: * <ol>
080: * <li> insert brace
081: * <li> move next
082: * <li> offset = 0
083: * </ol>
084: * <li> inside gap:
085: * <ol>
086: * <li> shrink gap to size of gap - offset.
087: * <li> insert brace
088: * <li> insert gap the size of offset.
089: * <li> move next twice
090: * <li> offset = 0
091: * </ol>
092: * <li> inside multiple char brace:
093: * <ol>
094: * <li> break
095: * <li> insert brace
096: * </ol>
097: * </OL>
098: * @param text the String type of the brace to insert
099: */
100: private void _insertBrace(String text) {
101: if (_cursor.atStart() || _cursor.atEnd()) {
102: _cursor.insertNewBrace(text); // inserts brace and goes to next
103: } else if (_cursor.current().isGap()) {
104: _cursor.insertBraceToGap(text);
105: }
106:
107: else {
108: _cursor.insertNewBrace(text);
109: }
110: }
111:
112: /**Inserts a gap between the characters in a multiple character brace.
113: * However, since ReducedModelBrace doesn't keep track of the comment
114: * braces and escape sequences, we just throw an exception since the
115: * condition in insertGap that spawns this method doesn't arise.
116: */
117: protected void insertGapBetweenMultiCharBrace(int length) {
118: throw new RuntimeException(
119: "ReducedModelBrace does not keep track of multi-character braces.");
120: }
121:
122: /** Updates ReducedModelBrace to reflect cursor movement.
123: * Negative values move left from the cursor, positive values move
124: * right. All functionality has been refactored into TokenList.
125: * @param count indicates the direction and magnitude of cursor movement
126: */
127: public void move(int count) {
128: _cursor.move(count);
129: }
130:
131: /** Updates ReducedModelBrace to reflect text deletion.
132: * Negative values mean text left of the cursor, positive values mean
133: * text to the right. All functionality has been refactored into TokenList.
134: */
135: public void delete(int count) {
136: if (count == 0)
137: return;
138: _cursor.delete(count);
139: return;
140: }
141:
142: /** If the current brace is a /, a *, a // or a \n, it's not matchable.
143: * This means it is ignored on balancing and on next/prev brace finding.
144: * All other braces are matchable.
145: */
146: private boolean _isCurrentBraceMatchable() {
147: return _cursor.current().isMatchable();
148: }
149:
150: /**
151: *Returns distance from current location of cursor to the location of the
152: *previous significant brace.
153: *ex. (...|) where | signifies the cursor. previousBrace returns 4 because
154: *it goes to the spot behind the (.
155: * /|* returns this brace since you're in the middle of it and going
156: *backward can find it.
157: */
158: public int previousBrace() {
159: int relDistance;
160: int dist = 0;
161: resetWalkerLocationToCursor();//reset the interface to the comment model
162:
163: TokenList.Iterator copyCursor = _cursor._copy();
164: if (!copyCursor.atStart()) {
165: copyCursor.prev();
166: }
167: if (copyCursor.atStart()) {
168: copyCursor.dispose();
169: return -1;
170: }
171: //initialize the size.
172: dist += _cursor.getBlockOffset();
173: relDistance = dist;
174:
175: // if we're in the middle of the first brace element, we're
176: // not going to find any previous braces
177:
178: while (!copyCursor.atStart()) {
179: if (!copyCursor.current().isGap()) {
180: if (moveWalkerGetState(-relDistance) == FREE) {
181: copyCursor.dispose();
182: return dist + copyCursor.current().getSize();
183: }
184: relDistance = 0;
185: }
186:
187: dist += copyCursor.current().getSize();
188: relDistance += copyCursor.current().getSize();
189: copyCursor.prev();
190: }
191: copyCursor.dispose();
192: return -1;
193: }
194:
195: /**
196: *Goes to the location before the brace. |...( where | is the cursor,
197: *returns three since it is three moves to the location of the (
198: *NOTE: /|* returns the next brace. It does not return this brace because
199: *you are past it.
200: */
201: public int nextBrace() {
202: int relDistance = 0;
203: int dist = 0;
204: TokenList.Iterator copyCursor = _cursor._copy();
205:
206: resetWalkerLocationToCursor();
207:
208: if (copyCursor.atStart())
209: copyCursor.next();
210: if (_cursor.getBlockOffset() > 0) {
211: dist = copyCursor.current().getSize()
212: - _cursor.getBlockOffset();
213: relDistance = dist;
214: copyCursor.next();
215: }
216: // there are no braces on the last brace element - it's empty
217: while (!copyCursor.atEnd()) {
218: if (!copyCursor.current().isGap()) {
219: if (moveWalkerGetState(relDistance) == FREE) {
220: copyCursor.dispose();
221: return dist;
222: }
223: relDistance = 0;
224: }
225: relDistance += copyCursor.current().getSize();
226: dist += copyCursor.current().getSize();
227: copyCursor.next();
228: }
229: copyCursor.dispose();
230: return -1;
231: }
232:
233: /** If the current ReducedToken is an open significant brace and the offset is 0 (i.e., if we're immediately left of
234: * said brace), push the current Brace onto a Stack and iterate forwards, keeping track of the distance covered.
235: * - For every closed significant Brace, if it matches the top of the Stack, pop the Stack. Increase the distance
236: * by the size of the Brace. If the Stack is Empty, we have a balance. Return distance. If the closed Brace does
237: * not match the top of the Stack, return -1; We have an unmatched open Brace at the top of the Stack.
238: * - For every open significant Brace, push onto the Stack. Increase distance by size of the Brace, continue.
239: * - Anything else, increase distance by size of the ReducedToken, continue.
240: */
241: public int balanceForward() {
242: //System.out.println("-------------------------------------------");
243: Stack<Brace> braceStack = new Stack<Brace>();
244: TokenList.Iterator iter = _cursor._copy();
245: resetWalkerLocationToCursor();
246:
247: if (iter.atStart() || iter.atFirstItem()
248: || !openBraceImmediatelyLeft()) {
249: // System.out.println("openBraceImmediatelyLeft(): "+openBraceImmediatelyLeft());
250: iter.dispose();
251: // System.out.println("! atStart, atFirstItem, or no closed brace");
252: return -1;
253: }
254:
255: iter.prev();
256: ReducedToken curToken = iter.current();
257:
258: assert curToken instanceof Brace; // In fact, it is a significant matchable open brace.
259:
260: int openBraceDistance = -curToken.getSize();
261:
262: moveWalkerGetState(openBraceDistance);
263: braceStack.push((Brace) curToken);
264: iter.next();
265: moveWalkerGetState(-openBraceDistance);
266:
267: int relDistance = 0; // distance to closest preceding Brace (non-gap)
268: int distance = 0; // distance to end of original open Brace (immediately left of cursor on entry)
269:
270: /* Loop until either:
271: * (i) we get a match and the stack is empty (success);
272: * (ii) we reach the end of a file and haven't found a match and abort; or
273: * (iii) we reach a close brace that doesn't have a match and abort.
274: */
275: while (!iter.atEnd() && !braceStack.isEmpty()) {
276: curToken = iter.current(); // a ReducedToken is either a Gap or a Brace
277: if (!curToken.isGap()) { // curToken is a Brace
278: Brace curBrace = (Brace) curToken;
279: if (moveWalkerGetState(relDistance) == FREE) {
280: // check for closed brace
281: if (curBrace.isClosedBrace()) {
282: Brace popped = braceStack.pop();
283: if (!curBrace.isMatch(popped)) {
284: iter.dispose();
285: // System.out.println("! encountered closed brace that didn't match");
286: return -1;
287: }
288: }
289: // otherwise, this must be an open brace
290: else
291: braceStack.push(curBrace);
292: }
293: relDistance = 0; // we moved the walker back to the right edge of the curBrace
294: }
295: // increment distances of size of current token
296: int size = curToken.getSize();
297: distance += size;
298: relDistance += size;
299: iter.next();
300: }
301:
302: // check if we exited because of failure
303: if (!braceStack.isEmpty()) {
304: iter.dispose();
305: // System.out.println("! ran to end of file. distance: " + distance);
306: return -1;
307: }
308: // success
309: else {
310: iter.dispose();
311: return distance;
312: }
313: }
314:
315: // /**
316: // * This is no longer used internally -- highlight is always started on left.
317: // */
318: // public boolean openBraceImmediatelyRight() {
319: // if (_cursor.atEnd()) {
320: // return false;
321: // }
322: // else {
323: // return ((_cursor.getBlockOffset() == 0) && _cursor.current().isOpen() &&
324: // _isCurrentBraceMatchable());
325: // }
326: // }
327:
328: public boolean openBraceImmediatelyLeft() {
329: if (_cursor.atStart() || _cursor.atFirstItem())
330: return false;
331: else {
332: _cursor.prev();
333: /*
334: System.out.println("+ closedBraceImmediatelyLeft() {");
335: System.out.println(" _cursor.getBlockOffset(): "+_cursor.getBlockOffset());
336: System.out.println(" _cursor.current().isClosed(): "+_cursor.current().isClosed());
337: System.out.println(" _isCurrentBraceMatchable(): "+_isCurrentBraceMatchable());
338: System.out.println(" }");
339: */
340: boolean isLeft = ((_cursor.getBlockOffset() == 0)
341: && _cursor.current().isOpen() && _isCurrentBraceMatchable());
342: //System.out.println("= token to left: " + _cursor);
343: _cursor.next();
344: //String output = (_cursor.atEnd()) ? "<end>": _cursor.toString();
345: //System.out.println("= current token: " + output);
346: return isLeft;
347: }
348: }
349:
350: public boolean closedBraceImmediatelyLeft() {
351: if (_cursor.atStart() || _cursor.atFirstItem()) {
352: return false;
353: } else {
354: _cursor.prev();
355: /*
356: System.out.println("+ closedBraceImmediatelyLeft() {");
357: System.out.println(" _cursor.getBlockOffset(): "+_cursor.getBlockOffset());
358: System.out.println(" _cursor.current().isClosed(): "+_cursor.current().isClosed());
359: System.out.println(" _isCurrentBraceMatchable(): "+_isCurrentBraceMatchable());
360: System.out.println(" }");
361: */
362: boolean isLeft = ((_cursor.getBlockOffset() == 0)
363: && _cursor.current().isClosed() && _isCurrentBraceMatchable());
364: //System.out.println("= token to left: " + _cursor);
365: _cursor.next();
366: //String output = (_cursor.atEnd()) ? "<end>": _cursor.toString();
367: //System.out.println("= current token: " + output);
368: return isLeft;
369: }
370: }
371:
372: /*
373: * If the previous ReducedToken is a closed significant brace,
374: * offset is 0 (i.e., if we're immediately right of said brace),
375: * push the previous Brace onto a Stack and iterate backwards,
376: * keeping track of the distance covered.
377: * - For every open significant Brace, if it matches the top of the Stack,
378: * pop the Stack. Increase the distance by the size of the Brace.
379: * If the Stack is Empty, we have a balance. Return distance.
380: * If the open Brace does not match the top of the Stack, return -1;
381: * We have an unmatched closed Brace at the top of the Stack.
382: * - For every closed significant Brace, push onto the Stack.
383: * Increase distance by size of the Brace, continue.
384: * - Anything else, increase distance by size of the ReducedToken, continue.
385: */
386: public int balanceBackward() {
387: //System.out.println("-------------------------------------------");
388: Stack<Brace> braceStack = new Stack<Brace>();
389: TokenList.Iterator iter = _cursor._copy();
390: resetWalkerLocationToCursor();
391:
392: if (iter.atStart() || iter.atFirstItem()
393: || !closedBraceImmediatelyLeft()) {
394: //System.out.println("closedBraceImmediatelyLeft(): "+closedBraceImmediatelyLeft());
395: iter.dispose();
396: //System.out.println("! atStart, atFirstItem, or no closed brace");
397: return -1;
398: }
399:
400: iter.prev();
401: assert iter.current() instanceof Brace; // In fact, it is a significant closed brace.
402:
403: int relDistance = 0; // distance to right edge of nearest brace
404: int distance = 0; // distance to original cursor
405:
406: /* We loop until:
407: * (i) we get a match and the stack is empty and report success
408: * (ii) we reach the start of a file and haven't found a match and aborrt
409: * (iii) we reach an open brace that doesn't have a match and abort
410: */
411: do {
412: ReducedToken curToken = iter.current();
413: int size = curToken.getSize();
414: distance += size;
415: relDistance += size;
416:
417: if (!curToken.isGap()) { // curToken is a Brace
418: Brace curBrace = (Brace) curToken;
419: if (moveWalkerGetState(-relDistance) == FREE) {
420: if (curBrace.isOpenBrace()) {
421: Brace popped = braceStack.pop();
422: if (!curBrace.isMatch(popped)) {
423: iter.dispose();
424: //System.out.println("! encountered open brace that didn't match");
425: return -1;
426: }
427: }
428: // closed
429: else
430: braceStack.push(curBrace);
431: }
432: relDistance = 0;
433: }
434:
435: iter.prev();
436: } while (!iter.atStart() && !braceStack.isEmpty());
437:
438: // test to see if we exited without a match
439: if (!braceStack.isEmpty()) {
440: iter.dispose();
441: //System.out.println("! ran to end of brace stack");
442: return -1;
443: }
444: // success
445: else {
446: iter.dispose();
447: return distance;
448: }
449: }
450:
451: protected ReducedModelState moveWalkerGetState(int relDistance) {
452: return _parent.moveWalkerGetState(relDistance);
453: }
454:
455: protected void resetWalkerLocationToCursor() {
456: _parent.resetLocation();
457: }
458:
459: /** Finds distance to enclosing brace on a preceding line. The field braceInfo.distToNewline holds the distance to
460: * the previous newline. To find the enclosing brace one must first move past this newline. The distance held in
461: * this variable is only to the space in front of the newline hence you must move back that distance + 1.
462: */
463: protected void getDistToEnclosingBrace(IndentInfo braceInfo) {
464: Stack<Brace> braceStack = new Stack<Brace>();
465: TokenList.Iterator iter = _cursor._copy();
466: resetWalkerLocationToCursor();
467: // this is the distance to in front of the previous newline.
468: int relDistance = braceInfo.distToNewline + 1;
469: int distance = relDistance;
470:
471: if (braceInfo.distToNewline == -1) {
472: iter.dispose();
473: return;
474: }
475: // move to the proper location, then add the rest of the block and go to the previous.
476: iter.move(-braceInfo.distToNewline - 1);
477: relDistance += iter.getBlockOffset();
478: distance += iter.getBlockOffset();
479:
480: //reset the value of braceInfo signiling the necessary newline has
481: //not been found.
482: braceInfo.distToNewline = -1;
483:
484: if (iter.atStart() || iter.atFirstItem()) {
485: iter.dispose();
486: return;
487: }
488:
489: iter.prev();
490:
491: // either we get a match and the stack is empty
492: // or we reach the start of a file and haven't found a match
493: // or we have a open brace that doesn't have a match,
494: // so we abort
495: while (!iter.atStart()) {
496:
497: ReducedToken curToken = iter.current();
498: int size = curToken.getSize();
499: distance += size;
500: relDistance += size;
501:
502: if (!curToken.isGap()) {
503:
504: Brace curBrace = (Brace) curToken;
505:
506: if (moveWalkerGetState(-relDistance) == FREE) {
507: // open
508: if (curBrace.isOpenBrace()) {
509: if (braceStack.isEmpty()) {
510: braceInfo.braceType = curBrace.getType();
511: braceInfo.distToBrace = distance;
512: iter.dispose();
513: return;
514: }
515: Brace popped = braceStack.pop();
516: if (!curBrace.isMatch(popped)) {
517: iter.dispose();
518: return;
519: }
520: }
521: // closed
522: else
523: braceStack.push(curBrace);
524: }
525: relDistance = 0;
526: }
527: // no matter what, we always want to increase the distance
528: // by the size of the token we have just gone over
529: iter.prev();
530: }
531:
532: iter.dispose();
533: return;
534: }
535:
536: /**
537: * Find the enclosing brace enclosing our current location.
538: */
539: protected void getDistToEnclosingBraceCurrent(IndentInfo braceInfo) {
540: Stack<Brace> braceStack = new Stack<Brace>();
541: TokenList.Iterator iter = _cursor._copy();
542: resetWalkerLocationToCursor();
543: int relDistance = 0;
544: int distance = relDistance;
545:
546: //move to the proper location, then add the rest of the block
547: // and go to the previous.
548:
549: relDistance += iter.getBlockOffset();
550: distance += iter.getBlockOffset();
551:
552: //reset the value of braceInfo signiling the necessary newline has
553: //not been found.
554: braceInfo.distToNewlineCurrent = -1;
555:
556: if (iter.atStart() || iter.atFirstItem()) {
557: iter.dispose();
558: return;
559: }
560:
561: iter.prev();
562:
563: // either we get a match and the stack is empty
564: // or we reach the start of a file and haven't found a match
565: // or we have a open brace that doesn't have a match,
566: // so we abort
567: while (!iter.atStart()) {
568:
569: ReducedToken curToken = iter.current();
570: int size = curToken.getSize();
571: distance += size;
572: relDistance += size;
573:
574: if (!curToken.isGap()) {
575: Brace curBrace = (Brace) curToken;
576: if (moveWalkerGetState(-relDistance) == FREE) {
577: // open
578: if (curBrace.isOpenBrace()) {
579: if (braceStack.isEmpty()) {
580: braceInfo.braceTypeCurrent = curBrace
581: .getType();
582: braceInfo.distToBraceCurrent = distance;
583: iter.dispose();
584: return;
585: }
586: Brace popped = braceStack.pop();
587: if (!curBrace.isMatch(popped)) {
588: iter.dispose();
589: return;
590: }
591: }
592: // closed
593: else
594: braceStack.push(curBrace);
595: }
596: relDistance = 0;
597: }
598: // no matter what, we always want to increase the distance
599: // by the size of the token we have just gone over
600: iter.prev();
601: }
602:
603: iter.dispose();
604: return;
605: }
606: }
|