001: /*
002: * Sun Public License Notice
003: *
004: * The contents of this file are subject to the Sun Public License
005: * Version 1.0 (the "License"). You may not use this file except in
006: * compliance with the License. A copy of the License is available at
007: * http://www.sun.com/
008: *
009: * The Original Code is NetBeans. The Initial Developer of the Original
010: * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
011: * Microsystems, Inc. All Rights Reserved.
012: */
013:
014: package org.netbeans.editor;
015:
016: import javax.swing.text.BadLocationException;
017: import javax.swing.text.Document;
018:
019: /**
020: * Block of text created using two marks. These blocks can be chained. Existing
021: * block can be compared to other block (better pair of positions) and there's
022: * extensive list of result values of such comparison.
023: *
024: * @author Miloslav Metelka
025: * @version 1.00
026: */
027:
028: public class MarkBlock {
029:
030: /**
031: * This value is not used directly in this class but can be used by other
032: * classes to report that the comparison of blocks has no sense for some
033: * reason.
034: */
035: public static final int INVALID = 0;
036:
037: /**
038: * Single bit value that means that tested block and THIS block partially or
039: * fully overlap. If this bit is not set then the blocks don't overlap at
040: * all. The values for which this bit is set are: OVERLAP_BEGIN,
041: * OVERLAP_END, EXTEND_BEGIN, EXTEND_END, INCLUDE, INSIDE_BEGIN, INSIDE_END,
042: * SAME, INNER. The values for which this bit is not set are: BEFORE, AFTER,
043: * CONTINUE_BEGIN, CONTINUE_END.
044: */
045: public static final int OVERLAP = 1;
046:
047: /**
048: * Single bit value that means that the tested block doesn't overlap with
049: * THIS block, but either its start position is equal to end position of
050: * THIS block or its end position is equal to the start position of THIS
051: * block. Simply they together make one continuous block. The values for
052: * which this bit is set are: CONTINUE_BEGIN, CONTINUE_END.
053: */
054: public static final int CONTINUE = 2;
055:
056: /**
057: * Single bit value meaning that the tested block has zero size.
058: */
059: public static final int EMPTY = 4;
060:
061: /**
062: * Single bit value meaning that THIS block has zero size.
063: */
064: public static final int THIS_EMPTY = 8;
065:
066: /**
067: * Two bit value meaning that the tested block fully includes THIS block.
068: * The block must be extended at least by one character, otherwise the
069: * 'inside' values will be used. It is included in the following values:
070: * EXTEND_BEGIN, INCLUDE, EXTEND_END. The value includes OVERLAP.
071: */
072: public static final int EXTEND = 16 | OVERLAP;
073:
074: /**
075: * Two bit value meaning that the tested block is fully inside THIS block.
076: * It is included in the following values: INSIDE_BEGIN, SAME, INSIDE_END.
077: * The value includes OVERLAP.
078: */
079: public static final int INSIDE = 32 | OVERLAP;
080:
081: /**
082: * Tested block completely before THIS mark block.
083: */
084: public static final int BEFORE = 64;
085:
086: /**
087: * Tested block completely after THIS mark block.
088: */
089: public static final int AFTER = 128;
090:
091: /**
092: * Tested block completely before THIS mark block but its end position
093: * equals to the start position of THIS block. They together make one
094: * continuous block. The value is BEFORE | CONTINUE.
095: */
096: public static final int CONTINUE_BEGIN = BEFORE | CONTINUE;
097:
098: /**
099: * Tested block completely after THIS mark block but its start position
100: * equals to the end position of THIS block. They together make one
101: * continuous block. The value is AFTER | CONTINUE.
102: */
103: public static final int CONTINUE_END = AFTER | CONTINUE;
104:
105: /**
106: * Tested block partly covers begining of THIS mark block. The value
107: * includes OVERLAP.
108: */
109: public static final int OVERLAP_BEGIN = 256 | OVERLAP;
110:
111: /**
112: * Tested block partly covers end of THIS mark block. The value includes
113: * OVERLAP.
114: */
115: public static final int OVERLAP_END = 512 | OVERLAP;
116:
117: /**
118: * Start position of the tested block is lower than the start position of
119: * THIS block and both end positions are the same. The value is
120: * OVERLAP_BEGIN | EXTEND.
121: */
122: public static final int EXTEND_BEGIN = OVERLAP_BEGIN | EXTEND;
123:
124: /**
125: * End position of the tested block is greater than the end position of THIS
126: * block and both start positions are the same. The value is OVERLAP_END |
127: * EXTEND.
128: */
129: public static final int EXTEND_END = OVERLAP_END | EXTEND;
130:
131: /**
132: * Tested block fully includes THIS block and extends it by at least one
133: * character in both directions. The value is EXTEND_BEGIN | EXTEND_END.
134: */
135: public static final int INCLUDE = EXTEND_BEGIN | EXTEND_END;
136:
137: /**
138: * Tested block completely inside THIS block and its end position is lower
139: * than end position of THIS block and start positions are the same. The
140: * value includes INSIDE.
141: */
142: public static final int INSIDE_BEGIN = 1024 | INSIDE;
143:
144: /**
145: * Tested block completely inside THIS block and its start position is
146: * greater than THIS block start position and end positions are the same.
147: * The value includes INSIDE.
148: */
149: public static final int INSIDE_END = 2048 | INSIDE;
150:
151: /**
152: * Tested block is fully inside THIS block and there is at least one more
153: * character left in THIS block after the end of the tested block in both
154: * directions. The value includes INSIDE.
155: */
156: public static final int INNER = 4096 | INSIDE;
157:
158: /**
159: * The blocks have exactly the same start and end positions. They simply
160: * cover exactly the same area. The value is INSIDE_BEGIN | INSIDE_END.
161: */
162: public static final int SAME = INSIDE_BEGIN | INSIDE_END;
163:
164: /**
165: * This value can be used to clear the two bits saying if the tested or THIS
166: * block are empty (The EMPTY and THIS_EMPTY are cleared). To do that, use
167: * value ANDed by IGNORE_EMPTY expression.
168: */
169: public static final int IGNORE_EMPTY = ~(EMPTY | THIS_EMPTY);
170:
171: /** Next block in the chain */
172: protected MarkBlock next;
173:
174: /** Previous block in the chain */
175: protected MarkBlock prev;
176:
177: public Mark startMark;
178:
179: public Mark endMark;
180:
181: protected BaseDocument doc;
182:
183: public MarkBlock(BaseDocument doc, Mark startMark, Mark endMark) {
184: this .doc = doc;
185: this .startMark = startMark;
186: this .endMark = endMark;
187: }
188:
189: /** Construct block with given marks */
190: public MarkBlock(BaseDocument doc, int startPos, int endPos)
191: throws BadLocationException {
192: this (doc, new Mark(), new Mark(), startPos, endPos);
193: }
194:
195: /** Construct block from positions on some document */
196: public MarkBlock(BaseDocument doc, Mark startMark, Mark endMark,
197: int startPos, int endPos) throws BadLocationException {
198: this (doc, startMark, endMark);
199: try {
200: doc.op.insertMark(startMark, startPos);
201: try {
202: doc.op.insertMark(endMark, endPos);
203: } catch (BadLocationException e) {
204: try {
205: startMark.remove();
206: } catch (InvalidMarkException e2) {
207: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
208: e2.printStackTrace();
209: }
210: }
211: throw e;
212: } catch (InvalidMarkException e) {
213: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
214: e.printStackTrace();
215: }
216: }
217: } catch (InvalidMarkException e) {
218: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
219: e.printStackTrace();
220: }
221: }
222: }
223:
224: /**
225: * Insert block before this one
226: *
227: * @return inserted block
228: */
229: public MarkBlock insertChain(MarkBlock blk) {
230: MarkBlock this Prev = this .prev;
231: blk.prev = this Prev;
232: blk.next = this ;
233: if (this Prev != null) {
234: this Prev.next = blk;
235: }
236: this .prev = blk;
237: return blk;
238: }
239:
240: /**
241: * Add block after this one
242: *
243: * @return added block
244: */
245: public MarkBlock addChain(MarkBlock blk) {
246: if (next != null) {
247: next.insertChain(blk);
248: } else {
249: setNextChain(blk);
250: }
251: return blk;
252: }
253:
254: /**
255: * Remove this block from the chain
256: *
257: * @return next chain member or null for end of chain
258: */
259: public MarkBlock removeChain() {
260: MarkBlock this Next = this .next;
261: MarkBlock this Prev = this .prev;
262: if (this Prev != null) { // not the first
263: this Prev.next = this Next;
264: this .prev = null;
265: }
266: if (this Next != null) {
267: this Next.prev = this Prev;
268: this .next = null;
269: }
270: destroyMarks();
271: return this Next;
272: }
273:
274: /**
275: * Compares the position of the given block against current block.
276: *
277: * @param startPos
278: * starting position of the compared block
279: * @param endPos
280: * ending position of the compared block or it is the same as
281: * startPos when testing just for insert
282: * @return relation of compared block against this guarded block
283: */
284: public int compare(int startPos, int endPos) {
285: try {
286: int startThis = startMark.getOffset();
287: int endThis = endMark.getOffset();
288: if (startThis > endThis) { // swap if necessary
289: int tmp = startThis;
290: startThis = endThis;
291: endThis = tmp;
292: }
293: int ret = 0;
294: if (startPos == endPos) { // tested empty
295: if (startThis == endThis) { // both empty
296: if (startPos < startThis) {
297: return EMPTY | THIS_EMPTY | BEFORE;
298: } else if (startPos > startThis) {
299: return EMPTY | THIS_EMPTY | AFTER;
300: } else {
301: return EMPTY | THIS_EMPTY | SAME;
302: }
303: } else { // tested empty, this non-empty
304: if (startPos <= startThis) {
305: return (startPos < startThis) ? (EMPTY | BEFORE)
306: : (EMPTY | INSIDE_BEGIN);
307: } else if (startPos >= endThis) {
308: return (startPos > endThis) ? (EMPTY | AFTER)
309: : (EMPTY | INSIDE_END);
310: } else {
311: return EMPTY | INNER;
312: }
313: }
314:
315: }
316: if (startThis == endThis) { // this empty, tested non-empty
317: if (startPos >= startThis) {
318: return (startPos > startThis) ? (THIS_EMPTY | AFTER)
319: : (THIS_EMPTY | EXTEND_END);
320: } else if (endPos >= startThis) {
321: return (endPos > startThis) ? (THIS_EMPTY | BEFORE)
322: : (THIS_EMPTY | EXTEND_BEGIN);
323: } else {
324: return THIS_EMPTY | INCLUDE;
325: }
326: }
327: // both non-empty
328: if (endPos <= startThis) {
329: return (endPos < startThis) ? BEFORE : CONTINUE_BEGIN;
330: } else if (startPos >= endThis) {
331: return (startPos > endThis) ? AFTER : CONTINUE_END;
332: } else {
333: if (endPos < endThis) {
334: if (startPos > startThis) {
335: return INNER;
336: } else if (startPos == startThis) {
337: return INSIDE_BEGIN;
338: } else { // startPos < startThis
339: return OVERLAP_BEGIN;
340: }
341: } else if (endPos == endThis) {
342: if (startPos > startThis) {
343: return INSIDE_END;
344: } else if (startPos == startThis) {
345: return SAME;
346: } else { // startPos < startThis
347: return EXTEND_BEGIN;
348: }
349: } else { // endPos > endThis
350: if (startPos > startThis) {
351: return OVERLAP_END;
352: } else if (startPos == startThis) {
353: return EXTEND_END;
354: } else { // startPos < startThis
355: return INCLUDE;
356: }
357: }
358: }
359: } catch (InvalidMarkException e) {
360: return INVALID;
361: }
362: }
363:
364: public final MarkBlock getNext() {
365: return next;
366: }
367:
368: public final void setNext(MarkBlock b) {
369: next = b;
370: }
371:
372: /** Set the next block of this one in the chain. */
373: public void setNextChain(MarkBlock b) {
374: this .next = b;
375: if (b != null) {
376: b.prev = this ;
377: }
378: }
379:
380: public final MarkBlock getPrev() {
381: return prev;
382: }
383:
384: public final void setPrev(MarkBlock b) {
385: prev = b;
386: }
387:
388: /** Set the previous block of this one in the chain */
389: public void setPrevChain(MarkBlock b) {
390: this .prev = b;
391: if (b != null) {
392: b.next = this ;
393: }
394: }
395:
396: public boolean isReverse() {
397: try {
398: return (startMark.getOffset() > endMark.getOffset());
399: } catch (InvalidMarkException e) {
400: return false;
401: }
402: }
403:
404: public void reverse() {
405: Mark tmp = startMark;
406: startMark = endMark;
407: endMark = tmp;
408: }
409:
410: public boolean checkReverse() {
411: if (isReverse()) {
412: reverse();
413: return true;
414: }
415: return false;
416: }
417:
418: /**
419: * Possibly move start mark if its position is above the given number.
420: *
421: * @return new position
422: */
423: public int extendStart(int startPos) throws BadLocationException {
424: try {
425: int markPos = startMark.getOffset();
426: startPos = Math.min(startPos, markPos);
427: if (startPos != markPos) {
428: doc.op.moveMark(startMark, startPos);
429: }
430: return startPos;
431: } catch (InvalidMarkException e) {
432: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
433: e.printStackTrace();
434: }
435: return 0;
436: }
437: }
438:
439: /**
440: * Possibly move end mark if its position is above the given number.
441: *
442: * @return new position
443: */
444: public int extendEnd(int endPos) throws BadLocationException {
445: try {
446: int markPos = endMark.getOffset();
447: endPos = Math.max(endPos, markPos);
448: if (endPos != markPos) {
449: doc.op.moveMark(endMark, endPos);
450: }
451: return endPos;
452: } catch (InvalidMarkException e) {
453: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
454: e.printStackTrace();
455: }
456: return 0;
457: }
458: }
459:
460: /**
461: * Extend this mark block by start and end positions. First test whether the
462: * given block intersects with this mark block. If not nothing is done.
463: *
464: * @return whether this mark block has been extended
465: */
466: public boolean extend(int startPos, int endPos, boolean concat)
467: throws BadLocationException {
468: try {
469: boolean extended = false;
470: int rel = compare(startPos, endPos);
471: if ((rel & OVERLAP_BEGIN) == OVERLAP_BEGIN
472: || ((rel & CONTINUE_BEGIN) == CONTINUE_BEGIN && concat)) {
473: extended = true;
474: doc.op.moveMark(startMark, startPos);
475: }
476: if ((rel & OVERLAP_END) == OVERLAP_END
477: || ((rel & CONTINUE_END) == CONTINUE_END && concat)) {
478: extended = true;
479: doc.op.moveMark(endMark, endPos);
480: }
481: return extended;
482: } catch (InvalidMarkException e) {
483: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
484: e.printStackTrace();
485: }
486: return false;
487: }
488: }
489:
490: /**
491: * Extend this mark block by some other block.
492: *
493: * @return whether the block was extended. If it was, the caller is
494: * responsible for possibly removing blk from the chain
495: */
496: public boolean extend(MarkBlock blk, boolean concat) {
497: try {
498: return extend(blk.startMark.getOffset(), blk.endMark
499: .getOffset(), concat);
500: } catch (BadLocationException e) {
501: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
502: e.printStackTrace();
503: }
504: } catch (InvalidMarkException e) {
505: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
506: e.printStackTrace();
507: }
508: }
509: return false;
510: }
511:
512: /**
513: * Shrink this mark block by the block specified. startMark is moved to the
514: * endPos if OVERLAP_BEGIN or INSIDE_BEGIN is returned from compare().
515: * endMark is moved to the startPos if OVERLAP_END or INSIDE_END is returned
516: * from compare(). If other status is returned or either block is empty,
517: * then no action is taken. It's up to caller to handle these situations.
518: *
519: * @return relation of tested block to mark block
520: */
521: public int shrink(int startPos, int endPos)
522: throws BadLocationException {
523: try {
524: int rel = compare(startPos, endPos);
525: switch (rel) {
526: case OVERLAP_BEGIN:
527: case INSIDE_BEGIN:
528: doc.op.moveMark(startMark, endPos);
529: break;
530: case OVERLAP_END:
531: case INSIDE_END:
532: doc.op.moveMark(endMark, startPos);
533: break;
534: }
535: return rel;
536: } catch (InvalidMarkException e) {
537: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
538: e.printStackTrace();
539: }
540: return INVALID;
541: }
542: }
543:
544: public Document getDocument() {
545: return doc;
546: }
547:
548: public int getStartOffset() {
549: try {
550: return startMark.getOffset();
551: } catch (InvalidMarkException e) {
552: return 0;
553: }
554: }
555:
556: public int getEndOffset() {
557: try {
558: return endMark.getOffset();
559: } catch (InvalidMarkException e) {
560: return 0;
561: }
562: }
563:
564: /** Remove the marks if they were not removed yet */
565: void destroyMarks() {
566: // now remove the marks
567: try {
568: if (startMark != null) {
569: startMark.remove();
570: startMark = null;
571: }
572: } catch (InvalidMarkException e) {
573: // already removed
574: }
575: try {
576: if (endMark != null) {
577: endMark.remove();
578: endMark = null;
579: }
580: } catch (InvalidMarkException e) {
581: // already removed
582: }
583: }
584:
585: /** Destroy the marks if necessary */
586: protected void finalize() throws Throwable {
587: destroyMarks();
588: super .finalize();
589: }
590:
591: /** Debugs this mark block */
592: public String toString() {
593: try {
594: return "startPos="
595: + ((startMark != null) // NOI18N
596: ? Utilities.debugPosition(doc, startMark
597: .getOffset())
598: : "null")
599: + ", endPos=" // NOI18N
600: + ((endMark != null) ? Utilities.debugPosition(doc,
601: endMark.getOffset()) : "null") // NOI18N
602: + ", "
603: + ((prev != null) ? ((next != null) ? "chain member" // NOI18N
604: : "last member")
605: : ((next != null) ? "first member" // NOI18N
606: : "standalone member")); // NOI18N
607: } catch (InvalidMarkException e) {
608: return ""; // NOI18N
609: }
610: }
611:
612: /** Debug possibly whole chain of marks */
613: public String toStringChain() {
614: return toString()
615: + ((next != null) ? ("\n" + next.toStringChain()) : ""); // NOI18N
616: }
617:
618: public static String debugRelation(int rel) {
619: String s = ((rel & EMPTY) != 0) ? "EMPTY | " : ""; // NOI18N
620: s += ((rel & THIS_EMPTY) != 0) ? "THIS_EMPTY | " : ""; // NOI18N
621: rel &= IGNORE_EMPTY;
622: switch (rel) {
623: case BEFORE:
624: return s + "BEFORE"; // NOI18N
625: case AFTER:
626: return s + "AFTER"; // NOI18N
627: case CONTINUE_BEGIN:
628: return s + "CONTINUE_BEGIN"; // NOI18N
629: case CONTINUE_END:
630: return s + "CONTINUE_END"; // NOI18N
631: case OVERLAP_BEGIN:
632: return s + "OVERLAP_BEGIN"; // NOI18N
633: case OVERLAP_END:
634: return s + "OVERLAP_END"; // NOI18N
635: case EXTEND_BEGIN:
636: return s + "EXTEND_BEGIN"; // NOI18N
637: case EXTEND_END:
638: return s + "EXTEND_END"; // NOI18N
639: case INCLUDE:
640: return s + "INCLUDE"; // NOI18N
641: case INSIDE_BEGIN:
642: return s + "INSIDE_BEGIN"; // NOI18N
643: case INSIDE_END:
644: return s + "INSIDE_END"; // NOI18N
645: case INNER:
646: return s + "INNER"; // NOI18N
647: case SAME:
648: return s + "SAME"; // NOI18N
649: case INVALID:
650: return s + "INVALID"; // NOI18N
651: default:
652: return s + "UNKNOWN_STATE " + rel; // NOI18N
653: }
654: }
655:
656: }
|