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.ext;
015:
016: import java.util.ArrayList;
017: import java.util.HashMap;
018:
019: import javax.swing.text.Position;
020:
021: import org.netbeans.editor.TokenItem;
022:
023: /**
024: * Support class for mapping the token-positions to the tokens and providing
025: * additional operations.
026: *
027: * @author Miloslav Metelka
028: * @version 1.00
029: */
030:
031: class FormatTokenPositionSupport {
032:
033: private final FormatWriter formatWriter;
034:
035: /** First save set in the chain */
036: private SaveSet firstSet;
037:
038: /** Last save set in the chain */
039: private SaveSet lastSet;
040:
041: /** Map holding the [token, token-position-list] pairs. */
042: private final HashMap tokens2positionLists = new HashMap();
043:
044: FormatTokenPositionSupport(FormatWriter formatWriter) {
045: this .formatWriter = formatWriter;
046: }
047:
048: private ArrayList getPosList(TokenItem token) {
049: ArrayList ret = (ArrayList) tokens2positionLists.get(token);
050: if (ret == null) {
051: ret = new ArrayList(3);
052: tokens2positionLists.put(token, ret);
053: }
054: return ret;
055: }
056:
057: /**
058: * Get the token-position for the given token and offset.
059: *
060: * @param token
061: * token for which the token-position is being created.
062: * @param offset
063: * offset inside the token at which the position is being
064: * created.
065: */
066: synchronized ExtTokenPosition getTokenPosition(TokenItem token,
067: int offset, Position.Bias bias) {
068: // Check offset correctness
069: if (token == null) {
070: if (offset != 0) {
071: throw new IllegalArgumentException(
072: "Ending token position has non-zero offset="
073: + offset);
074: }
075:
076: } else if (offset >= token.getImage().length()) {
077: throw new IllegalArgumentException("Offset=" + offset
078: + " >= tokenLength=" + token.getImage().length());
079: }
080:
081: ArrayList posList = getPosList(token);
082: int cnt = posList.size();
083: ExtTokenPosition etp;
084: for (int i = 0; i < cnt; i++) {
085: etp = (ExtTokenPosition) posList.get(i);
086: if (etp.getOffset() == offset && etp.getBias() == bias) {
087: return etp;
088: }
089: }
090:
091: etp = new ExtTokenPosition(token, offset, bias);
092: posList.add(etp);
093: return etp;
094: }
095:
096: /**
097: * Notify that the previous token was created with the appropriate text
098: * taken from the start of this token. It's now necessary to split the marks
099: * according
100: *
101: * @param token
102: * token that was split
103: * @param startLength
104: * initial length of the token-text that was cut and inserted
105: * into the previous token in the chain.
106: */
107: synchronized void splitStartTokenPositions(TokenItem token,
108: int startLength) {
109: TokenItem prevToken = token.getPrevious();
110: if (prevToken != null) {
111: prevToken = formatWriter.findNonEmptyToken(prevToken, true);
112: }
113: ArrayList posList = getPosList(token);
114: int len = posList.size();
115: ArrayList prevPosList = getPosList(prevToken);
116: for (int i = 0; i < len; i++) {
117: ExtTokenPosition etp = (ExtTokenPosition) posList.get(i);
118: if (etp.offset < startLength) { // move to prevToken
119: etp.token = prevToken;
120: posList.remove(i);
121: prevPosList.add(etp);
122: i--;
123: len--;
124: }
125: }
126: }
127:
128: /**
129: * Notify that the previous token was created with the appropriate text
130: * taken from the start of this token. It's now necessary to split the marks
131: * according
132: *
133: * @param token
134: * token that was split
135: * @param endLength
136: * initial length of the token-text that was cut and inserted
137: * into the previous token in the chain.
138: */
139: synchronized void splitEndTokenPositions(TokenItem token,
140: int endLength) {
141: TokenItem nextToken = token.getNext();
142: if (nextToken != null) {
143: nextToken = formatWriter
144: .findNonEmptyToken(nextToken, false);
145: }
146: ArrayList nextPosList = getPosList(nextToken);
147:
148: ArrayList posList = getPosList(token);
149: int len = posList.size();
150: int offset = token.getImage().length() - endLength;
151: for (int i = 0; i < len; i++) {
152: ExtTokenPosition etp = (ExtTokenPosition) posList.get(i);
153: if (etp.offset >= offset) { // move to nextToken
154: etp.token = nextToken;
155: etp.offset -= offset;
156: posList.remove(i);
157: nextPosList.add(etp);
158: i--;
159: len--;
160: }
161: }
162: }
163:
164: /** Text in the token will be inserted. */
165: synchronized void tokenTextInsert(TokenItem token, int offset,
166: int length) {
167: ArrayList posList = getPosList(token);
168: int len = posList.size();
169: // Add length to all positions after insertion point
170: for (int i = 0; i < len; i++) {
171: ExtTokenPosition etp = (ExtTokenPosition) posList.get(i);
172: if ((etp.bias == Position.Bias.Backward) ? (etp.offset > offset)
173: : (etp.offset >= offset)) {
174: etp.offset += length;
175: }
176: }
177:
178: // Move bwd-bias marks from the next token if insert at end
179: if (token.getImage().length() == offset) {
180: TokenItem nextToken = token.getNext();
181: if (nextToken != null) {
182: nextToken = formatWriter.findNonEmptyToken(nextToken,
183: false);
184: }
185: posList = getPosList(nextToken);
186: len = posList.size();
187: for (int i = 0; i < len; i++) {
188: ExtTokenPosition etp = (ExtTokenPosition) posList
189: .get(i);
190: if (etp.bias == Position.Bias.Backward
191: && etp.offset == 0) {
192: etp.token = token;
193: etp.offset = offset;
194: }
195: }
196: }
197:
198: }
199:
200: /** Text in the token will be removed. */
201: synchronized void tokenTextRemove(TokenItem token, int offset,
202: int length) {
203: ArrayList posList = getPosList(token);
204: int len = posList.size();
205: int newLen = token.getImage().length() - length;
206: ArrayList nextList = getPosList(token.getNext());
207: for (int i = 0; i < len; i++) {
208: ExtTokenPosition etp = (ExtTokenPosition) posList.get(i);
209: if (etp.offset >= offset + length) { // move to nextToken
210: etp.offset -= length;
211:
212: } else if (etp.offset >= offset) {
213: etp.offset = offset;
214: }
215:
216: // Check if pos right at the end of token and therefore invalid
217: if (etp.offset >= newLen) { // need to move to begining of next
218: // token
219: etp.token = token.getNext();
220: etp.offset = 0;
221: posList.remove(i);
222: nextList.add(etp);
223: i--;
224: len--;
225: }
226: }
227: }
228:
229: /** Whole token being removed. */
230: synchronized void tokenRemove(TokenItem token) {
231: TokenItem nextToken = token.getNext();
232: if (nextToken != null) {
233: nextToken = formatWriter
234: .findNonEmptyToken(nextToken, false);
235: }
236: ArrayList nextPosList = getPosList(nextToken);
237:
238: ArrayList posList = getPosList(token);
239: int len = posList.size();
240: for (int i = 0; i < len; i++) {
241: ExtTokenPosition etp = (ExtTokenPosition) posList.get(i);
242: etp.token = nextToken;
243: etp.offset = 0;
244: nextPosList.add(etp);
245: }
246: posList.clear();
247:
248: // Remove the token from registry
249: tokens2positionLists.remove(token);
250: }
251:
252: /** Given token was inserted into the chain */
253: synchronized void tokenInsert(TokenItem token) {
254: if (token.getImage().length() > 0) { // only for non-zero size
255: ArrayList posList = getPosList(token);
256:
257: TokenItem nextToken = token.getNext();
258: if (nextToken != null) {
259: nextToken = formatWriter.findNonEmptyToken(nextToken,
260: false);
261: }
262: ArrayList nextPosList = getPosList(nextToken);
263:
264: int nextLen = nextPosList.size();
265: for (int i = 0; i < nextLen; i++) {
266: ExtTokenPosition etp = (ExtTokenPosition) nextPosList
267: .get(i);
268: if (etp.offset == 0
269: && etp.getBias() == Position.Bias.Backward) {
270: etp.token = token; // offset will stay equal to zero
271: nextPosList.remove(i);
272: i--;
273: nextLen--;
274: posList.add(etp);
275: }
276: }
277: }
278: }
279:
280: /** Clear all the save-sets. */
281: synchronized void clearSaveSets() {
282: firstSet = null;
283: lastSet = null;
284: }
285:
286: /**
287: * Add the save-set to the registry and perform the checking whether the
288: * offsets are OK.
289: */
290: synchronized void addSaveSet(int baseOffset, int writtenLen,
291: int[] offsets, Position.Bias[] biases) {
292: // Check whether the offsets are OK
293: for (int i = 0; i < offsets.length; i++) {
294: if (offsets[i] < 0 || offsets[i] > writtenLen) {
295: throw new IllegalArgumentException(
296: "Invalid save-offset=" + offsets[i]
297: + " at index=" + i // NOI18N
298: + ". Written length is " + writtenLen); // NOI18N
299: }
300: }
301:
302: SaveSet newSet = new SaveSet(baseOffset, offsets, biases);
303:
304: if (firstSet != null) {
305: lastSet.next = newSet;
306: lastSet = newSet;
307:
308: } else { // first set
309: firstSet = lastSet = newSet;
310: }
311: }
312:
313: /** Create the token-positions for all the save sets */
314: synchronized void createPositions(
315: FormatTokenPosition formatStartPosition) {
316: updateSaveOffsets(formatStartPosition);
317:
318: SaveSet curSet = firstSet;
319: FormatWriter.FormatTokenItem token = (FormatWriter.FormatTokenItem) formatStartPosition
320: .getToken();
321: boolean noText = (token == null);
322:
323: while (curSet != null) {
324: int len = curSet.offsets.length;
325: for (int i = 0; i < len; i++) {
326: if (noText) {
327: curSet.positions[i] = getTokenPosition(null, 0,
328: curSet.biases[i]);
329:
330: } else { // there's some text to be formatted
331:
332: // Find the covering token and create the position
333: int offset = curSet.offsets[i];
334: while (token != null) {
335: if (offset < token.getSaveOffset()) {
336: token = (FormatWriter.FormatTokenItem) token
337: .getPrevious();
338:
339: } else if ((offset > token.getSaveOffset()
340: + token.getImage().length())
341: || token.getImage().length() == 0) {
342: token = (FormatWriter.FormatTokenItem) token
343: .getNext();
344:
345: } else { // the right token
346: curSet.positions[i] = getTokenPosition(
347: token, offset
348: - token.getSaveOffset(),
349: curSet.biases[i]);
350: break; // break the loop
351: }
352: }
353:
354: if (token == null) { // It is right at the end
355: curSet.positions[i] = getTokenPosition(null, 0,
356: curSet.biases[i]);
357: token = (FormatWriter.FormatTokenItem) formatWriter
358: .getLastToken();
359: }
360: }
361: }
362:
363: curSet = curSet.next;
364: }
365: }
366:
367: synchronized void updateSaveSets(
368: FormatTokenPosition formatStartPosition) {
369: updateSaveOffsets(formatStartPosition);
370:
371: SaveSet curSet = firstSet;
372: int endOffset = 0; // offset of the null token
373: if (formatStartPosition.getToken() != null) {
374: endOffset = ((FormatWriter.FormatTokenItem) formatWriter
375: .getLastToken()).getSaveOffset()
376: + formatWriter.getLastToken().getImage().length();
377: }
378:
379: while (curSet != null) {
380: int len = curSet.offsets.length;
381: for (int i = 0; i < len; i++) {
382: FormatWriter.FormatTokenItem token = (FormatWriter.FormatTokenItem) curSet.positions[i]
383: .getToken();
384: if (token == null) {
385: curSet.offsets[i] = endOffset;
386:
387: } else { // non-null token
388: curSet.offsets[i] = token.getSaveOffset()
389: + curSet.positions[i].getOffset();
390: }
391: }
392: }
393: }
394:
395: /**
396: * Number the tokens so that they are OK for finding out the offsets.
397: */
398: private void updateSaveOffsets(
399: FormatTokenPosition formatStartPosition) {
400: if (firstSet != null) { // it has only sense if there are any save-sets
401: FormatWriter.FormatTokenItem ti = (FormatWriter.FormatTokenItem) formatStartPosition
402: .getToken();
403: int offset = -formatStartPosition.getOffset();
404:
405: while (ti != null) {
406: ti.setSaveOffset(offset);
407: offset += ti.getImage().length();
408:
409: ti = (FormatWriter.FormatTokenItem) ti.getNext();
410: }
411: }
412: }
413:
414: /**
415: * Implementation of the extended-token-position that allows modification of
416: * its token and offset fields.
417: */
418: class ExtTokenPosition implements FormatTokenPosition {
419:
420: TokenItem token;
421:
422: int offset;
423:
424: /** Whether the position should stay the same if inserted right at it. */
425: Position.Bias bias;
426:
427: ExtTokenPosition(TokenItem token, int offset) {
428: this (token, offset, Position.Bias.Forward);
429: }
430:
431: ExtTokenPosition(TokenItem token, int offset, Position.Bias bias) {
432: this .token = token;
433: this .offset = offset;
434: this .bias = bias;
435: }
436:
437: public TokenItem getToken() {
438: return token;
439: }
440:
441: public int getOffset() {
442: return (token != null) ? offset : 0;
443: }
444:
445: public Position.Bias getBias() {
446: return bias;
447: }
448:
449: public boolean equals(Object o) {
450: return equals(o, true); // ignore bias in comparison
451: }
452:
453: public boolean equals(Object o, boolean ignoreBias) {
454: if (o instanceof FormatTokenPosition) {
455: FormatTokenPosition tp = (FormatTokenPosition) o;
456:
457: return token == tp.getToken()
458: && offset == tp.getOffset()
459: && (ignoreBias || bias == tp.getBias());
460: }
461:
462: return false;
463: }
464:
465: public String toString() {
466: return "<" + getToken() + ", " + getOffset() + ", "
467: + getBias() + ">";
468: }
469:
470: }
471:
472: /**
473: * Class holding the info about the set of the offsets to save during the
474: * formatting.
475: */
476: static class SaveSet {
477:
478: /** Next set in the chain. */
479: SaveSet next;
480:
481: /** Base offset of the buffer corresponding to the offsets */
482: int baseOffset;
483:
484: /** Offsets to save */
485: int[] offsets;
486:
487: /** Biases for the positions */
488: Position.Bias[] biases;
489:
490: /** Token positions corresponding to the offsets */
491: FormatTokenPosition[] positions;
492:
493: SaveSet(int baseOffset, int[] offsets, Position.Bias[] biases) {
494: this.baseOffset = baseOffset;
495: this.offsets = offsets;
496: this.biases = biases;
497: }
498:
499: }
500:
501: }
|