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: /** Keeps track of newlines, comment blocks, and single and double-quoted strings. This reduced sub-model is used for
040: * coloring purposes. Given the information contained here, the DefinitionsEditorKit can paint strings, comments, and
041: * regular code in different colors. DefinitionsEditorKit colors keywords by directly reading DefinitionsDocument,
042: * the "full-scale" model.
043: * @version $Id: ReducedModelComment.java 4255 2007-08-28 19:17:37Z mgricken $
044: */
045:
046: public class ReducedModelComment extends AbstractReducedModel {
047:
048: /** Can be used by other classes to walk through the list of comment chars*/
049: TokenList.Iterator _walker;
050:
051: /** Constructor. Creates a new reduced model with the cursor at the start of a blank "page." */
052: public ReducedModelComment() {
053: super ();
054: _walker = _cursor._copy();
055: }
056:
057: public void insertChar(char ch) {
058: switch (ch) {
059: case '*':
060: insertSpecial("*");
061: break;
062: case '/':
063: insertSpecial("/");
064: break;
065: case '\n':
066: insertNewline();
067: break;
068: case '\\':
069: insertSpecial("\\");
070: break;
071: case '\'':
072: insertQuote("'");
073: break;
074: case '\"':
075: insertQuote("\"");
076: break;
077: default:
078: _insertGap(1);
079: break;
080: }
081: }
082:
083: /**
084: * Inserts one of three special chars, (*),(/), or (\).
085: * <OL>
086: * <li> empty list: insert slash
087: * <li> atEnd: check previous and insert slash
088: * <li> inside multiple character brace:
089: * <ol>
090: * <li> break current brace
091: * <li> move next to make second part current
092: * <li> insert brace between broken parts of former brace
093: * <li> move previous twice to get before the broken first part
094: * <li> walk
095: * <li> current = multiple char brace? move next once<BR>
096: * current = single char brace? move next twice<BR>
097: * We moved two previous, but if the broken part combined with
098: * the insert, there's only one brace where once were two.
099: * </ol>
100: * <li> inside a gap: use helper function
101: * <li> before a multiple char brace:
102: * <ol>
103: * <li> break the current brace
104: * <li> check previous and insert
105: * </ol>
106: * <li>otherwise, check previous and insert
107: * </OL>
108: */
109: private void insertSpecial(String special) {
110: // Check if empty.
111: if (_tokens.isEmpty()) {
112: _cursor.insertNewBrace(special); //now pointing to tail.
113: return;
114: }
115: // Check if at start.
116: if (_cursor.atStart())
117: _cursor.next();
118:
119: // Not empty, not at start, if at end check the previous brace
120: if (_cursor.atEnd())
121: _checkPreviousInsertSpecial(special);
122:
123: // If inside a double character brace, break it.
124: else if (_cursor.getBlockOffset() > 0
125: && _cursor.current().isMultipleCharBrace()) {
126: _cursor._splitCurrentIfCommentBlock(true, true);
127: //leaving us at the start
128: _cursor.next(); //leaving us after first char
129: _cursor.insertNewBrace(special); //leaves us after the insert
130: move(-2);
131: _updateBasedOnCurrentState();
132: move(2);
133: }
134: // inside a gap
135: else if (_cursor.getBlockOffset() > 0
136: && _cursor.current().isGap()) {
137: _cursor.insertBraceToGap(special);
138: _cursor.prev();
139: _cursor.prev();
140: _updateBasedOnCurrentState();
141: // restore cursor state
142: _cursor.next();
143: _cursor.next();
144: // update based on current state
145: }
146: //if at start of double character brace, break it.
147: else if ((_cursor.getBlockOffset() == 0)
148: && _cursor.current().isMultipleCharBrace()) {
149: //if we're free there won't be a block comment close so if there
150: //is then we don't want to break it. If the special character is
151: // a backslash, we want to break the following escape sequence if there
152: // is one.
153: _cursor._splitCurrentIfCommentBlock(false, special
154: .equals("\\"));
155: //leaving us at start
156:
157: _checkPreviousInsertSpecial(special);
158: } else
159: _checkPreviousInsertSpecial(special);
160: }
161:
162: /**
163: * Checks before point of insertion to make sure we don't need to combine.
164: * Delegates work to _checkPreviousInsertBackSlash and _checkPreviousInsertCommentChar,
165: * depending on what's being inserted into the document.
166: */
167: private void _checkPreviousInsertSpecial(String special) {
168: if (special.equals("\\")) {
169: _checkPreviousInsertBackSlash();
170: } else {
171: _checkPreviousInsertCommentChar(special);
172: }
173: }
174:
175: /** Checks before point of insertion to make sure we don't need to combine
176: * backslash with another backslash (yes, they too can be escaped).
177: */
178:
179: private void _checkPreviousInsertBackSlash() {
180: if (!_cursor.atStart() && !_cursor.atFirstItem()) {
181: if (_cursor.prevItem().getType().equals("\\")) {
182: _cursor.prevItem().setType("\\\\");
183: _updateBasedOnCurrentState();
184: return;
185: }
186: }
187: // Here we know the / unites with nothing behind it.
188: _cursor.insertNewBrace("\\"); //leaving us after the brace.
189: _cursor.prev();
190: _updateBasedOnCurrentState();
191: if (_cursor.current().getSize() == 2)
192: _cursor.setBlockOffset(1);
193: else
194: _cursor.next();
195: }
196:
197: /** Checks before the place of insert to make sure there are no preceding
198: * slashes with which the inserted slash must combine. It then performs
199: * the insert of either (/), (/ /), (/ *) or (* /).
200: */
201: private void _checkPreviousInsertCommentChar(String special) {
202: if (!_cursor.atStart() && !_cursor.atFirstItem()) {
203: if ((_cursor.prevItem().getType().equals("/"))
204: && (_cursor.prevItem().getState() == FREE)) {
205: _cursor.prevItem().setType("/" + special);
206: _updateBasedOnCurrentState();
207: return;
208: }
209: // if we're after a star,
210: else if (_cursor.prevItem().getType().equals("*")
211: && getStateAtCurrent() == INSIDE_BLOCK_COMMENT
212: && special.equals("/")) {
213: _cursor.prevItem().setType("*" + special);
214: _cursor.prevItem().setState(FREE);
215: _updateBasedOnCurrentState();
216: return;
217: }
218: }
219: //Here we know the / unites with nothing behind it.
220: _cursor.insertNewBrace(special); //leaving us after the brace.
221: _cursor.prev();
222: _updateBasedOnCurrentState();
223: if (_cursor.current().getSize() == 2)
224: _cursor.setBlockOffset(1);
225: else
226: _cursor.next();
227: }
228:
229: /**
230: * Inserts an end-of-line character.
231: * <OL>
232: * <li> atStart: insert
233: * <li> atEnd: insert
234: * <li> inside multiple character brace:
235: * <ol>
236: * <li> break current brace
237: * <li> move next to make second part current
238: * <li> insert brace between broken parts of former brace
239: * <li> move previous twice to get before the broken first part
240: * <li> walk
241: * <li> move next twice to be after newline insertion
242: * </ol>
243: * <li> inside a gap: use helper function
244: * <li>otherwise, just insert normally
245: * </OL>
246: */
247: public void insertNewline() {
248: if (_cursor.atStart()) {
249: _insertNewEndOfLine();
250: } else if (_cursor.atEnd()) {
251: _insertNewEndOfLine();
252: } else if ((_cursor.getBlockOffset() > 0)
253: && _cursor.current().isMultipleCharBrace()) {
254: _cursor._splitCurrentIfCommentBlock(true, true);
255: _cursor.next();
256: _cursor.insert(Brace.MakeBrace("\n", getStateAtCurrent()));
257: _cursor.prev();
258: _updateBasedOnCurrentState();
259: _cursor.next();
260: _cursor.next();
261: _cursor.setBlockOffset(0);
262: } else if ((_cursor.getBlockOffset() > 0)
263: && _cursor.current().isGap()) {
264: _cursor.insertBraceToGap("\n");
265: _cursor.prev();
266: _cursor.prev();
267: _updateBasedOnCurrentState();
268: // restore cursor state
269: _cursor.next();
270: _cursor.next();
271: } else {
272: _insertNewEndOfLine();
273: }
274: return;
275: }
276:
277: private void _insertNewEndOfLine() {
278: _cursor.insertNewBrace("\n");
279: _cursor.prev();
280: _updateBasedOnCurrentState();
281: _cursor.next();
282: _cursor.setBlockOffset(0);
283: }
284:
285: /**
286: * Inserts the specified quote character.
287: * <OL>
288: * <li> atStart: insert
289: * <li> atEnd: insert
290: * <li> inside multiple character brace:
291: * <ol>
292: * <li> break current brace
293: * <li> move next to make second part current
294: * <li> insert brace between broken parts of former brace
295: * <li> walk
296: * <li> current = multiple char brace? move next once<BR>
297: * current = single char brace? move next twice<BR>
298: * We moved two previous, but if the broken part combined with
299: * the insert, there's only one brace where once were two.
300: * <li> move next twice to be after newline insertion
301: * </ol>
302: * <li> inside a gap: use helper function
303: * <li> before a multiple char brace:
304: * <ol>
305: * <li> break the current brace
306: * <li> check previous and insert
307: * </ol>
308: * <li>otherwise, just insert normally
309: * </OL>
310: * @param quote the type of quote to insert
311: */
312: public void insertQuote(String quote) {
313: if (_cursor.atStart()) {
314: _insertNewQuote(quote);
315: } else if (_cursor.atEnd()) {
316: _insertNewQuote(quote);
317: }
318: // in the middle of a multiple character brace
319: else if ((_cursor.getBlockOffset() > 0)
320: && _cursor.current().isMultipleCharBrace()) {
321: _cursor._splitCurrentIfCommentBlock(true, true);
322: _cursor.next();
323: _cursor.insert(Brace.MakeBrace(quote, getStateAtCurrent()));
324: _cursor.prev();
325: _updateBasedOnCurrentState();
326: if (!_cursor.current().isMultipleCharBrace())
327: _cursor.next();
328: _cursor.next();
329: _cursor.setBlockOffset(0);
330: }
331: // in the middle of a gap
332: else if ((_cursor.getBlockOffset() > 0)
333: && _cursor.current().isGap()) {
334: _cursor.insertBraceToGap(quote);
335: _cursor.prev();
336: _cursor.prev();
337: _updateBasedOnCurrentState();
338: // restore cursor state
339: _cursor.next();
340: _cursor.next();
341:
342: } else
343: _insertNewQuote(quote);
344: return;
345: }
346:
347: /**
348: * Helper function for insertQuote. Creates a new quote Brace and puts it in the
349: * reduced model.
350: * @param quote the quote to insert
351: */
352: private void _insertNewQuote(String quote) {
353: String insert = _getQuoteType(quote);
354: _cursor.insertNewBrace(insert);
355: _cursor.prev();
356: _updateBasedOnCurrentState();
357: _cursor.next();
358: _cursor.setBlockOffset(0);
359: }
360:
361: /**
362: * Helper function for insertNewQuote. In the case where a backslash
363: * precedes the point of insertion, it removes the backslash and returns
364: * the text for an escaped quote. The type of quote depends on the given
365: * argument.
366: * @param quote the type of quote to insert
367: * @return a regular or escaped quote, depending on what was previous
368: */
369: private String _getQuoteType(String quote) {
370: if (_cursor.atStart() || _cursor.atFirstItem())
371: return quote;
372: else if (_cursor.prevItem().getType().equals("\\")) {
373: _cursor.prev();
374: _cursor.remove();
375: return "\\" + quote;
376: } else
377: return quote;
378: }
379:
380: /** Inserts a gap between the characters in a multiple character brace. This function is called by
381: * AbstractReducedModel's method insertGap when a Gap is inserted between the characters in a comment brace or an
382: * escape sequence. It splits up the multiple character brace into its component parts and inserts a Gap of size
383: * length in between the resulting split parts.
384: * @param length the size of the Gap to be inserted in characters
385: */
386: protected void insertGapBetweenMultiCharBrace(int length) {
387: if (_cursor.getBlockOffset() > 1)
388: throw new IllegalArgumentException("OFFSET TOO BIG: "
389: + _cursor.getBlockOffset());
390:
391: _cursor._splitCurrentIfCommentBlock(true, true);
392: _cursor.next();
393: _insertNewGap(length); //inserts gap and goes to next item
394: // we have to go back two tokens; we don't want to use move because it could
395: // throw us past start if there was only one character before us and we went
396: // the usual 2 spaces before. There would have to be a check and a branch
397: // depending on conditions that way.
398: _cursor.prev();
399: _cursor.prev();
400: _updateBasedOnCurrentState();
401: // restore cursor state
402: _cursor.next();
403: _cursor.next();
404: return;
405: }
406:
407: /** USE RULES:
408: * Inserting between braces: This should be called from between the two
409: * characters of the broken double comment.
410: * Deleting special chars: Start from previous char if it exists.
411: * Begins updating at current character. /./ would not become // because current is in the middle.
412: * Double character comments inside of a quote or a comment are broken.
413: */
414:
415: private void _updateBasedOnCurrentState() {
416: TokenList.Iterator copyCursor = _cursor._copy();
417: copyCursor.updateBasedOnCurrentState();
418: copyCursor.dispose();
419: }
420:
421: /** Updates the BraceReduction to reflect cursor movement. Negative values move left from the cursor, positive values
422: * move right.
423: * @param count indicates the direction and magnitude of cursor movement
424: */
425: public void move(int count) {
426: _cursor.move(count);
427: }
428:
429: /** <P>Update the BraceReduction to reflect text deletion.</P>
430: * @param count indicates the size and direction of text deletion.
431: * Negative values delete text to the left of the cursor, positive values delete text to the right.
432: * Always move count spaces to make sure we can delete.
433: */
434: public void delete(int count) {
435: if (count == 0)
436: return;
437:
438: _cursor.delete(count);
439:
440: // Changes in ReducedModelComment can entail state changes in the
441: // document. For this reason, we have to call
442: // _updateBasedOnCurrentState because there is no need to call it
443: // in ReducedModelBrace, and factoring it out would be stupid and
444: // wasteful.
445:
446: // Move back 2 or as far back as the document will allow
447: int absOff = this .absOffset();
448: int movement;
449: if (absOff < 2)
450: movement = absOff;
451: else
452: movement = 2;
453: _cursor.move(-movement);
454: // update state information
455: _updateBasedOnCurrentState();
456: // restore the cursor
457: _cursor.move(movement);
458: return;
459: }
460:
461: /* In order to interface with the ReducedModelComment two functions are
462: provided. One resets the walker and the other will both move the cursor
463: by x and return the state at that new location.
464: Once the new value has returned all new calculations will be relative to
465: that spot until the walker is reset to the _cursor. */
466:
467: /** Returns the state at the relLocation, where relLocation is the location relative to the walker
468: * @param relLocation distance from walker to get state at.
469: */
470: protected ReducedModelState moveWalkerGetState(int relLocation) {
471: _walker.move(relLocation);
472: return _walker.getStateAtCurrent();
473: }
474:
475: /** Resets the walker to the current position in document */
476: protected void resetWalkerLocationToCursor() {
477: _walker.dispose();
478: _walker = _cursor._copy();
479: }
480:
481: /** Dist to Previous newline will be -1 if no newline. */
482: void getDistToPreviousNewline(IndentInfo braceInfo) {
483: braceInfo.distToPrevNewline = _getDistToPreviousNewline(_cursor
484: ._copy());
485: braceInfo.distToNewline = braceInfo.distToPrevNewline;
486: return;
487: }
488:
489: /** Returns distance to after newline. */
490: private int _getDistToPreviousNewline(TokenList.Iterator copyCursor) {
491: int walkcount = copyCursor.getBlockOffset();
492: if (!copyCursor.atStart())
493: copyCursor.prev();
494: while ((!copyCursor.atStart())
495: && (!(copyCursor.current().getType().equals("\n")))) {
496: // copyCursor.current().getState() == FREE))) {
497: walkcount += copyCursor.current().getSize();
498: copyCursor.prev();
499: }
500:
501: if (copyCursor.atStart())
502: return -1;
503: return walkcount;
504: }
505:
506: void getDistToIndentNewline(IndentInfo braceInfo) {
507: TokenList.Iterator copyCursor = _cursor._copy();
508:
509: if (braceInfo.distToBrace == -1 || copyCursor.atStart())
510: return; // no brace
511:
512: copyCursor.move(-braceInfo.distToBrace);
513: int walkcount = _getDistToPreviousNewline(copyCursor);
514:
515: if (walkcount == -1) {
516: braceInfo.distToNewline = -1;
517: } else {
518: braceInfo.distToNewline = walkcount + braceInfo.distToBrace;
519: }
520: return;
521: }
522:
523: /**
524: * Computes the distance to the beginning of the line containing the brace enclosing
525: * the current location. Stores this info in the IndentInfo field distToNewlineCurrent.
526: */
527: void getDistToCurrentBraceNewline(IndentInfo braceInfo) {
528: TokenList.Iterator copyCursor = _cursor._copy();
529:
530: if (braceInfo.distToBraceCurrent == -1 || copyCursor.atStart()) { // no brace
531: return;
532: }
533:
534: copyCursor.move(-braceInfo.distToBraceCurrent);
535: int walkcount = _getDistToPreviousNewline(copyCursor);
536:
537: if (walkcount == -1) {
538: braceInfo.distToNewlineCurrent = -1;
539: } else {
540: braceInfo.distToNewlineCurrent = walkcount
541: + braceInfo.distToBraceCurrent;
542: }
543: return;
544: }
545:
546: /**
547: * Gets distance to previous newline, relLoc is the distance
548: * back from the cursor that we want to start searching.
549: */
550: public int getDistToPreviousNewline(int relLoc) {
551: TokenList.Iterator copyCursor = _cursor._copy();
552: copyCursor.move(-relLoc);
553: int dist = _getDistToPreviousNewline(copyCursor);
554: copyCursor.dispose();
555: if (dist == -1) {
556: return -1;
557: }
558: return dist + relLoc;
559: }
560:
561: /** Returns the distance to the gap before the next newline (end of document if no newline) */
562: public int getDistToNextNewline() {
563: TokenList.Iterator copyCursor = _cursor._copy();
564: if (copyCursor.atStart()) {
565: copyCursor.next();
566: }
567: if (copyCursor.atEnd()
568: || copyCursor.current().getType().equals("\n")) {
569: return 0;
570: }
571: int walkcount = copyCursor.current().getSize()
572: - _cursor.getBlockOffset();
573: copyCursor.next();
574:
575: while ((!copyCursor.atEnd())
576: && (!(copyCursor.current().getType().equals("\n")))) {
577: //copyCursor.current().getState() == FREE))) {
578: walkcount += copyCursor.current().getSize();
579: copyCursor.next();
580: }
581: return walkcount;
582: }
583: }
|