KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > rice > cs > drjava > model > definitions > reducedmodel > ReducedModelControl


1 /*BEGIN_COPYRIGHT_BLOCK
2  *
3  * This file is part of DrJava. Download the current version of this project from http://www.drjava.org/
4  * or http://sourceforge.net/projects/drjava/
5  *
6  * DrJava Open Source License
7  *
8  * Copyright (C) 2001-2005 JavaPLT group at Rice University (javaplt@rice.edu). All rights reserved.
9  *
10  * Developed by: Java Programming Languages Team, Rice University, http://www.cs.rice.edu/~javaplt/
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
13  * documentation files (the "Software"), to deal with the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
15  * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
16  *
17  * - Redistributions of source code must retain the above copyright notice, this list of conditions and the
18  * following disclaimers.
19  * - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
20  * following disclaimers in the documentation and/or other materials provided with the distribution.
21  * - Neither the names of DrJava, the JavaPLT, Rice University, nor the names of its contributors may be used to
22  * endorse or promote products derived from this Software without specific prior written permission.
23  * - Products derived from this software may not be called "DrJava" nor use the term "DrJava" as part of their
24  * names without prior written permission from the JavaPLT group. For permission, write to javaplt@rice.edu.
25  *
26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
27  * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28  * CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
29  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
30  * WITH THE SOFTWARE.
31  *
32  *END_COPYRIGHT_BLOCK*/

33
34 package edu.rice.cs.drjava.model.definitions.reducedmodel;
35
36 import java.util.Vector JavaDoc;
37
38 import edu.rice.cs.util.UnexpectedException;
39
40 /** This class provides an implementation of the BraceReduction interface for brace matching. In order to correctly
41   * match, this class keeps track of what is commented (line and block) and what is inside double quotes and keeps
42   * this in mind when matching. To avoid unnecessary complication, this class maintains a few invariants for its
43   * consistent states, i.e., between top-level function calls.
44   * <ol>
45   * <li> The cursor offset is never at the end of a brace. If movement or insertion puts it there, the cursor is
46   * updated to point to the 0 offset of the next brace.
47   * <li> Quoting information is invalid inside valid comments. When part of the document becomes uncommented, the
48   * reduced model must update the quoting information linearly in the newly revealed code.
49   * <li> Quote shadowing and comment shadowing are mutually exclusive.
50   * <li> There is no nesting of comment open characters. If // is encountered in the middle of a comment, it is
51   * treated as two separate slashes. Similar for /*.
52   * </ol>
53   * All of the code in the class assumes that a lock on this is held.
54   * @author JavaPLT
55   * @version $Id: ReducedModelControl.java 4026 2006-10-31 15:50:16Z rcartwright $
56   */

57 public class ReducedModelControl implements BraceReduction {
58   /* private fields; default visibility for testing purposes only. */
59   volatile ReducedModelBrace _rmb;
60   volatile ReducedModelComment _rmc;
61   volatile int _offset;
62
63   // Relying on default constructor
64
// public ReducedModelControl() {
65
// rmb = new ReducedModelBrace(this);
66
// rmc = new ReducedModelComment();
67
// }
68

69   private ReducedModelBrace getRMB() {
70     if (_rmb == null) _rmb = new ReducedModelBrace(this);
71     return _rmb;
72   }
73   
74   private ReducedModelComment getRMC() {
75     if (_rmc == null) _rmc = new ReducedModelComment();
76     return _rmc;
77   }
78   
79   public void insertChar(char ch) {
80     getRMB().insertChar(ch);
81     getRMC().insertChar(ch);
82   }
83
84   /** <P>Updates the BraceReduction to reflect cursor movement. Negative values move left from the cursor,
85    * positive values move right. </P>
86    * @param count indicates the direction and magnitude of cursor movement
87    */

88   public void move(int count) {
89     try {
90       getRMB().move(count);
91       getRMC().move(count);
92     }
93     catch(IllegalArgumentException JavaDoc e) {
94       resetLocation();
95       throw new UnexpectedException(e);
96     }
97   }
98
99   /** <P>Update the BraceReduction to reflect text deletion.</P>
100    * @param count indicates the size and direction of text deletion. Negative values delete text to the left of the
101    * cursor, positive values delete text to the right.
102    */

103   public void delete(int count) {
104     getRMB().delete(count);
105     getRMC().delete(count);
106   }
107
108   /** <P>Finds the closing brace that matches the next significant brace iff that brace is an open brace.</P>
109    * @return the distance until the matching closing brace. On failure, returns -1.
110    * @see #balanceBackward()
111    */

112   public int balanceForward() { return getRMB().balanceForward(); }
113   
114   /** <P>Finds the open brace that matches the previous significant brace iff that brace is an closing brace.</P>
115    * @return the distance until the matching open brace. On failure, returns -1.
116    * @see #balanceForward()
117    */

118   public int balanceBackward() { return getRMB().balanceBackward(); }
119
120   /** This function returns the state at the relDistance, where relDistance is relative to the last time it was called.
121    * You can reset the last call to the current offset using resetLocation.
122    */

123   public ReducedModelState moveWalkerGetState(int relDistance) { return getRMC().moveWalkerGetState(relDistance); }
124
125   /** This function resets the location of the walker in the comment list to
126    * where the current cursor is. This allows the walker to keep walking and
127    * using relative distance instead of having to rewalk the same distance
128    * every call to stateAtRelLocation. It is an optimization.
129    */

130   public void resetLocation() {
131     getRMC().resetWalkerLocationToCursor();
132   }
133
134   /** Get the token currently pointed at by the cursor. Because the reduced model is split into two reduced sub-models,
135     * we have to check each sub-model first as each one has unique information. If we find a non-gap token in either
136     * sub-model we want to return that. Otherwise, we want to return a sort of hybrid Gap of the two, i.e., a Gap where
137     * there are neither special comment/quote tokens nor parens/squigglies/brackets.
138     * @return a ReducedToken representative of the unified reduced model
139     */

140   public ReducedToken currentToken() {
141     // check the reduced comment model for specials
142
ReducedToken rmcToken = getRMC().current();
143     if (! rmcToken.isGap()) return rmcToken;
144     // check the reduced brace model for braces
145
ReducedToken rmbToken = getRMB().current();
146     if (! rmbToken.isGap()) {
147       rmbToken.setState(getRMC().getStateAtCurrent());
148       return rmbToken;
149     }
150     // otherwise, we have a gap.
151
int size = getSize(rmbToken,rmcToken);
152     return new Gap(size, getRMC().getStateAtCurrent());
153   }
154
155   /** Get the shadowing state at the current caret position.
156     * @return FREE|INSIDE_LINE_COMMENT|INSIDE_BLOCK_COMMENT|
157     * INSIDE_SINGLE_QUOTE|INSIDE_DOUBLE_QUOTE
158     */

159   public ReducedModelState getStateAtCurrent() {
160       return getRMC().getStateAtCurrent();
161   }
162
163   /**
164    * Get a string representation of the current token's type.
165    * @return "" if current is a Gap, otherwise, use ReducedToken.getType()
166    */

167   String JavaDoc getType() {
168     ReducedToken rmcToken = getRMC().current();
169     if (! rmcToken.isGap())
170       return rmcToken.getType();
171
172     ReducedToken rmbToken = getRMB().current();
173     if (! rmbToken.isGap()) {
174       return rmbToken.getType();
175     }
176     return ""; //a gap
177
}
178
179   /**
180    * Gets the size of the current token.
181    * It checks both the brace and comment sub-models to find the size
182    * of the current token. If the current token is a Gap, we have to reconcile
183    * the information of both sub-models in order to get the correct size
184    * of the current token as seen by the outside world.
185    * @return the number of characters represented by the current token
186    */

187   int getSize() {
188     return getSize(getRMB().current(),getRMC().current());
189   }
190
191   int getSize(ReducedToken rmbToken, ReducedToken rmcToken) {
192     int rmb_offset = getRMB().getBlockOffset();
193     int rmc_offset = getRMC().getBlockOffset();
194     int rmb_size = rmbToken.getSize();
195     int rmc_size = rmcToken.getSize();
196     int size;
197     if (rmb_offset < rmc_offset) {
198       size = rmb_offset;
199       _offset = size;
200     }
201     else {
202       size = rmc_offset;
203       _offset = size;
204     }
205
206     if (rmb_size - rmb_offset < rmc_size - rmc_offset) {
207       size += (rmb_size - rmb_offset);
208     }
209     else {
210       size += (rmc_size - rmc_offset);
211     }
212     return size;
213   }
214
215   /**
216    * Move the reduced model to the next token and update the cursor information.
217    */

218   void next() {
219     if (getRMC()._cursor.atStart()) {
220       getRMC().next();
221       getRMB().next();
222       return;
223     }
224     int size = getSize(getRMB().current(), getRMC().current());
225     getRMC().move(size - _offset);
226     getRMB().move(size - _offset);
227   }
228
229   /**
230    * Move the reduced model to the previous token and update the cursor information.
231    */

232   void prev() {
233     int size;
234     if (getRMC()._cursor.atEnd()) {
235       getRMC().prev();
236       getRMB().prev();
237       if (getRMC()._cursor.atStart()) {
238         return; // because in place now.
239
}
240
241       if (getRMC().current().getSize() < getRMB().current().getSize()) {
242         size = -getRMC().current().getSize();
243       }
244       else {
245         size = -getRMB().current().getSize();
246       }
247       getRMC().next();
248       getRMB().next();
249       move(size);
250     }
251     else if (getRMB().getBlockOffset() < getRMC().getBlockOffset()) {
252       getRMB().prev();
253       size = getRMB().current().getSize() + getRMB().getBlockOffset();
254       getRMB().next();
255       if (size < getRMC().getBlockOffset()) {
256         move(-size);
257       }
258       else {
259         move(-getRMC().getBlockOffset());
260       }
261     }
262     else if (getRMB().getBlockOffset() == getRMC().getBlockOffset()) {
263       getRMB().prev();
264       getRMC().prev();
265       getRMB().setBlockOffset(0);
266       getRMC().setBlockOffset(0);
267     }
268     else {
269       getRMC().prev();
270       size = getRMC().current().getSize() + getRMC().getBlockOffset();
271       getRMC().next();
272       if (size < getRMB().getBlockOffset()) {
273         move(-size);
274       }
275       else {
276         move(-getRMB().getBlockOffset());
277       }
278     }
279   }
280
281   /**
282    * Get the previous token.
283    */

284   public ReducedToken prevItem() {
285     int rmbOffset = getRMB().getBlockOffset();
286     int rmcOffset = getRMC().getBlockOffset();
287
288     prev();
289     ReducedToken temp = currentToken();
290     next();
291
292     getRMB().setBlockOffset(rmbOffset);
293     getRMC().setBlockOffset(rmcOffset);
294     return temp;
295   }
296
297   /**
298    * Get the next token.
299    */

300   public ReducedToken nextItem() {
301     int rmbOffset = getRMB().getBlockOffset();
302     int rmcOffset = getRMC().getBlockOffset();
303     next();
304     ReducedToken temp = currentToken();
305     prev();
306     getRMB().setBlockOffset(rmbOffset);
307     getRMC().setBlockOffset(rmcOffset);
308     return temp;
309   }
310
311   /** Determines if the cursor is at the end of the reduced model. */
312   boolean atEnd() { return (getRMB()._cursor.atEnd() || getRMC()._cursor.atEnd()); }
313
314   /** Determines if the cursor is at the start of the reduced model. */
315   boolean atStart() { return (getRMB()._cursor.atStart() || getRMC()._cursor.atStart()); }
316
317   /** Gets the offset within the current token. */
318   int getBlockOffset() {
319     if (getRMB().getBlockOffset() < getRMC().getBlockOffset()) return getRMB().getBlockOffset();
320     return getRMC().getBlockOffset();
321   }
322
323   /** Gets the absolute character offset into the document represented by the reduced model. */
324   public int absOffset() { return getRMC().absOffset(); }
325
326
327   /** A toString() substitute. */
328   public String JavaDoc simpleString() {
329     return "\n********\n" + getRMB().simpleString() + "\n________\n" + getRMC().simpleString();
330   }
331
332   /** Returns an IndentInfo containing the following information:
333     * - distance to the previous newline ( start of this line)
334     * - distance to the brace enclosing the beginning of the current line
335     * - distance to the beginning of the line containing that brace
336     */

337   public IndentInfo getIndentInformation() {
338     IndentInfo braceInfo = new IndentInfo();
339     //get distance to the previous newline (in braceInfo.distToNewline)
340
getRMC().getDistToPreviousNewline(braceInfo);
341     //get distance to the closing brace before that new line.
342
getRMB().getDistToEnclosingBrace(braceInfo);
343     //get distance to newline before the previous, just mentioned, brace.
344
getRMC().getDistToIndentNewline(braceInfo);
345     // get distance to the brace enclosing the current location
346
getRMB().getDistToEnclosingBraceCurrent(braceInfo);
347     // get distance to the beginning of that brace's line
348
getRMC().getDistToCurrentBraceNewline(braceInfo);
349     return braceInfo;
350   }
351
352   /** Gets distance to end of line on the line previous. */
353   public int getDistToPreviousNewline(int relLoc) {
354     return getRMC().getDistToPreviousNewline(relLoc);
355   }
356
357   public int getDistToNextNewline() {
358     return getRMC().getDistToNextNewline();
359   }
360
361   /** Return all highlight status info for text between the current location and current location + length. This should
362     * collapse adjoining blocks with the same status into one.
363     * @param start The start location of the area for which we want the status. The reduced model is already at this
364     * position, but this value is needed to determine the absolute positions in HighlightStatus objects we return.
365     * @param length The length of the text segment for which status information must be generated.
366     */

367   public Vector JavaDoc<HighlightStatus> getHighlightStatus(final int start, final int length) {
368     Vector JavaDoc<HighlightStatus> vec = new Vector JavaDoc<HighlightStatus>();
369
370     int curState;
371     int curLocation;
372     int curLength;
373
374     TokenList.Iterator cursor = getRMC()._cursor._copy();
375 // int ct = rmc._tokens.listenerCount();
376
curLocation = start;
377     // NOTE: old code threw an exception if cursor.atStart(); it used wrong value for curLength atEnd too
378
// curLength = ! cursor.atEnd() ? cursor.current().getSize() - rmc.getBlockOffset() : start + length;
379
// curState = ! cursor.atEnd() ? cursor.current().getHighlightState() : 0;
380
if (cursor.atEnd() || cursor.atStart()) { // cursor is not inside a reduced model token
381
curLength = length;
382       curState = 0;
383     }
384     else {
385       curLength = cursor.current().getSize() - getRMC().getBlockOffset();
386       curState = cursor.current().getHighlightState();
387     }
388
389     while ((curLocation + curLength) < (start + length)) {
390       cursor.next();
391       //TODO: figure out why this function is iterating past the end of the collection
392
//when it gets called from the ColoringGlyphPainter after deleting the last character
393
if (cursor.atEnd()) break;
394       int nextState = cursor.current().getHighlightState();
395
396       if (nextState == curState) {
397         // add on and keep building
398
curLength += cursor.current().getSize();
399       }
400       else {
401         // add old one to the vector and start new one
402
vec.add(new HighlightStatus(curLocation, curLength, curState));
403         curLocation += curLength; // new block starts after previous one
404
curLength = cursor.current().getSize();
405         curState = nextState;
406       }
407     }
408
409     // Make sure this token length doesn't extend past start+length.
410
// This is because we guarantee that the returned vector only refers
411
// to chars on [start, start+length).
412
int requestEnd = start + length;
413     if ((curLocation + curLength) > requestEnd) {
414       curLength = requestEnd - curLocation;
415     }
416
417     // Add the last one, which has not been added yet
418
vec.add(new HighlightStatus(curLocation, curLength, curState));
419
420     cursor.dispose();
421
422     return vec;
423   }
424 }
425
Popular Tags