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:
018: /**
019: * Support class for chain of MarkBlocks
020: *
021: * @author Miloslav Metelka
022: * @version 1.00
023: */
024:
025: public class MarkBlockChain {
026:
027: /** Chain of all blocks */
028: protected MarkBlock chain;
029:
030: /** Current block to make checks faster */
031: protected MarkBlock currentBlock;
032:
033: /** Document for this block */
034: protected BaseDocument doc;
035:
036: /** Construct chain using regular base marks */
037: public MarkBlockChain(BaseDocument doc) {
038: this .doc = doc;
039: }
040:
041: public final MarkBlock getChain() {
042: return chain;
043: }
044:
045: /**
046: * Tests whether the position range is partly or fully inside some mark
047: * block from the chain.
048: *
049: * @param startPos
050: * starting position of tested area
051: * @param endPos
052: * ending position of tested area for removal or same as startPos
053: * when insert is made
054: * @return relation of currentBlock to the given block
055: */
056: public int compareBlock(int startPos, int endPos) {
057: if (currentBlock == null) {
058: currentBlock = chain;
059: if (currentBlock == null) {
060: return MarkBlock.INVALID;
061: }
062: }
063:
064: int rel; // relation of block to particular mark block
065: boolean afterPrev = false; // blk is after previous block
066: boolean beforeNext = false; // blk is before next block
067: boolean cont = false; // blk continued currentBlock in previous match
068: MarkBlock contBlk = null;
069: int contRel = 0;
070: while (true) {
071: rel = currentBlock.compare(startPos, endPos);
072: if ((rel & MarkBlock.OVERLAP) != 0) {
073: return rel;
074: }
075:
076: if ((rel & MarkBlock.AFTER) != 0) { // after this mark block
077: if (beforeNext) {
078: if (!cont || (rel & MarkBlock.CONTINUE) != 0) {
079: return rel;
080: } else { // continues with contBlk and this relation is pure
081: // after
082: currentBlock = contBlk;
083: return contRel;
084: }
085: } else { // going from begining of chain
086: if (currentBlock.next != null) {
087: afterPrev = true;
088: cont = ((rel & MarkBlock.CONTINUE) != 0);
089: if (cont) {
090: contRel = rel;
091: contBlk = currentBlock;
092: }
093: currentBlock = currentBlock.next;
094: } else { // end of chain
095: return rel;
096: }
097: }
098: } else { // before this mark block
099: if (afterPrev) {
100: if (!cont || (rel & MarkBlock.EXTEND) != 0) {
101: return rel;
102: } else {
103: currentBlock = contBlk;
104: return contRel;
105: }
106: } else { // going from end of chain
107: if (currentBlock.prev != null) {
108: beforeNext = true;
109: cont = ((rel & MarkBlock.CONTINUE) != 0);
110: if (cont) {
111: contRel = rel;
112: contBlk = currentBlock;
113: }
114: currentBlock = currentBlock.prev;
115: } else { // begining of chain
116: return rel;
117: }
118: }
119: }
120: }
121: }
122:
123: public void removeEmptyBlocks() {
124: try {
125: MarkBlock blk = chain;
126: while (blk != null) {
127: if (blk.startMark.getOffset() == blk.endMark
128: .getOffset()) { // empty
129: // block
130: blk = checkedRemove(blk); // remove current block and get
131: // the next one
132: } else {
133: blk = blk.next;
134: }
135: }
136: } catch (InvalidMarkException e) {
137: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
138: e.printStackTrace();
139: }
140: }
141: }
142:
143: protected MarkBlock createBlock(int startPos, int endPos)
144: throws BadLocationException {
145: return new MarkBlock(doc, createBlockStartMark(),
146: createBlockEndMark(), startPos, endPos);
147: }
148:
149: protected Mark createBlockStartMark() {
150: return new Mark();
151: }
152:
153: protected Mark createBlockEndMark() {
154: return new Mark();
155: }
156:
157: /**
158: * Add non-empty block to the chain of blocks
159: *
160: * @param concat
161: * whether concatenate adjacent blocks
162: */
163: public void addBlock(int startPos, int endPos, boolean concat) {
164: if (startPos == endPos) {
165: return;
166: }
167: try {
168: int rel = compareBlock(startPos, endPos)
169: & MarkBlock.IGNORE_EMPTY;
170: if ((rel & MarkBlock.BEFORE) != 0) { // before currentBlock or
171: // continue_begin
172: if (concat && rel == MarkBlock.CONTINUE_BEGIN) { // concatenate
173: doc.op.moveMark(currentBlock.startMark, startPos);
174: } else { // insert new block at begining
175: boolean first = (currentBlock == chain);
176: MarkBlock blk = currentBlock
177: .insertChain(createBlock(startPos, endPos));
178: if (first) {
179: chain = blk;
180: }
181: }
182: } else if ((rel & MarkBlock.AFTER) != 0) { // after currentBlock or
183: // continue_end
184: if (concat && rel == MarkBlock.CONTINUE_END) {
185: doc.op.moveMark(currentBlock.endMark, endPos);
186: } else { // add new block to the chain
187: currentBlock
188: .addChain(createBlock(startPos, endPos));
189: }
190: } else { // overlap or invalid relation
191: if (currentBlock == null) { // no current block
192: chain = createBlock(startPos, endPos);
193: } else { // overlap
194: // the block is partly hit - extend it by positions
195: currentBlock.extendStart(startPos);
196: currentBlock.extendEnd(endPos);
197: // remove the blocks covered by startPos to endPos
198: MarkBlock blk = chain;
199: while (blk != null) {
200: if (blk != currentBlock) { // except self
201: if (currentBlock.extend(blk, concat)) { // if they
202: // overlapped
203: MarkBlock tempCurBlk = currentBlock;
204: blk = checkedRemove(blk); // will clear
205: // currentBlock
206: currentBlock = tempCurBlk;
207: } else { // didn't overlap, go to next
208: blk = blk.next;
209: }
210: } else {
211: blk = blk.next;
212: }
213: }
214: }
215: }
216: } catch (InvalidMarkException e) {
217: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
218: e.printStackTrace();
219: }
220: } catch (BadLocationException e) {
221: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
222: e.printStackTrace();
223: }
224: }
225: }
226:
227: /** Remove non-empty block from area covered by blocks from chain */
228: public void removeBlock(int startPos, int endPos) {
229: if (startPos == endPos) {
230: return;
231: }
232: try {
233: int rel;
234: while (((rel = compareBlock(startPos, endPos)) & MarkBlock.OVERLAP) != 0) {
235: if ((rel & MarkBlock.THIS_EMPTY) != 0) { // currentBlock is
236: // empty
237: checkedRemove(currentBlock);
238: } else {
239: switch (currentBlock.shrink(startPos, endPos)) {
240: case MarkBlock.INNER: // tested block inside currentBlock
241: // -> divide
242: int endMarkPos = currentBlock.endMark
243: .getOffset();
244: doc.op.moveMark(currentBlock.endMark, startPos);
245: currentBlock.addChain(createBlock(endPos,
246: endMarkPos));
247: return;
248: case MarkBlock.INSIDE_BEGIN:
249: case MarkBlock.OVERLAP_BEGIN:
250: doc.op.moveMark(currentBlock.startMark, endPos);
251: return;
252: case MarkBlock.INSIDE_END:
253: case MarkBlock.OVERLAP_END:
254: doc.op.moveMark(currentBlock.endMark, startPos);
255: return;
256: default: // EXTEND
257: checkedRemove(currentBlock);
258: break;
259: }
260: }
261: }
262: } catch (BadLocationException e) {
263: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
264: e.printStackTrace();
265: }
266: } catch (InvalidMarkException e) {
267: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
268: e.printStackTrace();
269: }
270: }
271: }
272:
273: /**
274: * Removes mark block and possibly updates the chain.
275: *
276: * @return next block after removed one
277: */
278: protected MarkBlock checkedRemove(MarkBlock blk) {
279: boolean first = (blk == chain);
280: blk = blk.removeChain();
281: if (first) {
282: chain = blk;
283: }
284: currentBlock = null; // make sure current block is cleared
285: return blk;
286: }
287:
288: public int adjustToBlockEnd(int pos) {
289: int rel = compareBlock(pos, pos) & MarkBlock.IGNORE_EMPTY;
290: if (rel == MarkBlock.INSIDE_BEGIN || rel == MarkBlock.INNER) { // inside
291: // blk
292: pos = currentBlock.getEndOffset();
293: }
294: return pos;
295: }
296:
297: /**
298: * Return the position adjusted to the start of the next mark-block.
299: */
300: public int adjustToNextBlockStart(int pos) {
301: // !!! what about empty blocks
302: int rel = compareBlock(pos, pos) & MarkBlock.IGNORE_EMPTY;
303: if ((rel & MarkBlock.BEFORE) != 0) {
304: pos = currentBlock.getStartOffset();
305: } else { // after the block or inside
306: if (currentBlock != null) {
307: MarkBlock nextBlk = currentBlock.getNext();
308: if (nextBlk != null) {
309: pos = nextBlk.getStartOffset();
310: } else { // no next block
311: pos = -1;
312: }
313: } else { // no current block
314: pos = -1;
315: }
316: }
317: return pos;
318: }
319:
320: public static class LayerChain extends MarkBlockChain {
321:
322: private String layerName;
323:
324: public LayerChain(BaseDocument doc, String layerName) {
325: super (doc);
326: this .layerName = layerName;
327: }
328:
329: public final String getLayerName() {
330: return layerName;
331: }
332:
333: protected Mark createBlockStartMark() {
334: MarkFactory.DrawMark startMark = new MarkFactory.DrawMark(
335: layerName, null);
336: startMark.activateLayer = true;
337: return startMark;
338: }
339:
340: protected Mark createBlockEndMark() {
341: MarkFactory.DrawMark endMark = new MarkFactory.DrawMark(
342: layerName, null);
343: endMark.backwardBias = true;
344: return endMark;
345: }
346:
347: }
348:
349: public String toString() {
350: return "MarkBlockChain: currentBlock="
351: + currentBlock
352: + "\nblock chain: " // NOI18N
353: + (chain != null ? ("\n" + chain.toStringChain())
354: : " Empty"); // NOI18N
355: }
356:
357: }
|