001: /*
002: * (C) Copyright IBM Corp. 1998-2004. All Rights Reserved.
003: *
004: * The program is provided "as is" without any warranty express or
005: * implied, including the warranty of non-infringement and the implied
006: * warranties of merchantibility and fitness for a particular purpose.
007: * IBM will not be liable for any damages suffered by you as a result
008: * of using the Program. In no event will IBM be liable for any
009: * special, indirect or consequential damages or lost profits even if
010: * IBM has been advised of the possibility of their occurrence. IBM
011: * will not be liable for any third party claims against you.
012: */
013: package com.ibm.richtext.styledtext;
014:
015: import java.util.Vector;
016:
017: import java.io.Externalizable;
018: import java.io.ObjectInput;
019: import java.io.ObjectOutput;
020: import java.io.IOException;
021:
022: /**
023: * This class is a standard implementation of MTabRuler.
024: * It can have a finite number of client-specified TabStops. After
025: * the client-specified TabStops, all TabStops have type
026: * <code>TabStop.kAuto</code> and are at the autospace intervals.
027: * @see TabStop
028: */
029: public final class StandardTabRuler extends MTabRuler implements
030: Externalizable {
031: static final String COPYRIGHT = "(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
032: private static final int CURRENT_VERSION = 1;
033: private static final long serialVersionUID = 22356934;
034:
035: private static final TabStop AUTO_ZERO = new TabStop(0,
036: TabStop.kAuto);
037:
038: private TabStop[] fTabs = null;
039: private int fAutoSpacing = 36; // every 1/2 inch.
040:
041: /**
042: * Create a StandardTabRuler with only auto tabs, with spacing of 36.
043: */
044: public StandardTabRuler() {
045: }
046:
047: /**
048: * Create a StandardTabRuler with only auto tabs, with the
049: * given autoSpacing.
050: * @param autoSpacing the autoSpacing for this tab ruler
051: */
052: public StandardTabRuler(int autoSpacing) {
053: fAutoSpacing = autoSpacing;
054: }
055:
056: /**
057: * Create a StandardTabRuler. The first TabStops on the ruler will be
058: * the TabStops in the <code>tabs</code> array. After these tabs all
059: * tabs are auto tabs.
060: * @param tabs an array of TabStops. The TabStops in the array must
061: * be in strictly increasing order (of positions), and cannot have
062: * type <code>TabStop.kAuto</code>.
063: * @param autoSpacing the autoSpacing interval to use after the last
064: * client-specified tab.
065: */
066: public StandardTabRuler(TabStop[] tabs, int autoSpacing) {
067: if (tabs.length > 0) {
068: validateTabArray(tabs);
069: fTabs = (TabStop[]) tabs.clone();
070: } else {
071: fTabs = null;
072: }
073: fAutoSpacing = autoSpacing;
074: }
075:
076: /** Tabs as provided, then autoSpacing after the last tab to eternity. Use this constructor when
077: munging a ruler, it does no validation on the tabs in the vector. Vector may not be null. */
078:
079: /*public*/StandardTabRuler(Vector v, int autoSpacing) {
080: fTabs = tabArrayFromVector(v);
081: fAutoSpacing = autoSpacing;
082: }
083:
084: /** Construct from another ruler. No validation. Ruler may not be null. */
085:
086: /*public*/StandardTabRuler(MTabRuler ruler) {
087: if (ruler == null) {
088: throw new IllegalArgumentException("ruler may not be null");
089: }
090:
091: fTabs = tabArrayFromVector(vectorFromTabRuler(ruler));
092: fAutoSpacing = ruler.autoSpacing();
093: }
094:
095: public void readExternal(ObjectInput in) throws IOException,
096: ClassNotFoundException {
097:
098: int version = in.readInt();
099: if (version != CURRENT_VERSION) {
100: throw new IOException("Invalid version of StyledText: "
101: + version);
102: }
103: fTabs = (TabStop[]) in.readObject();
104: fAutoSpacing = in.readInt();
105: }
106:
107: public void writeExternal(ObjectOutput out) throws IOException {
108:
109: out.writeInt(CURRENT_VERSION);
110: out.writeObject(fTabs);
111: out.writeInt(fAutoSpacing);
112: }
113:
114: /**
115: * Return first tab in the ruler. If an autoTab, it is at position zero, and
116: * all subsequent tabs will be autotabs at autoSpacing intervals.
117: */
118: public TabStop firstTab() {
119: if (fTabs != null && fTabs.length > 0) {
120: return fTabs[0];
121: }
122:
123: return AUTO_ZERO;
124: }
125:
126: /**
127: * Return the first tab in the ruler with fPosition > position. If it is an
128: * autotab, it is at an increment of autoSpacing, and all subsequent tabs will be
129: * autotabs at autoSpacing intervals.
130: * @param position the position of the TabStop returned will be greater than this parameter
131: */
132: public TabStop nextTab(int position) {
133: if (fTabs != null) {
134: for (int i = 0; i < fTabs.length; ++i) {
135: if (position < fTabs[i].getPosition())
136: return fTabs[i];
137: }
138: }
139:
140: if (position >= 4000) { // debug: sanity check
141: System.out.println("auto tab past 4000");
142: }
143:
144: return new TabStop(((position / fAutoSpacing) + 1)
145: * fAutoSpacing, TabStop.kAuto);
146: }
147:
148: /**
149: * Return the interval for autotabs.
150: */
151: public int autoSpacing() {
152: return fAutoSpacing;
153: }
154:
155: /**
156: * Compare this to another Object. Returns true if the object
157: * is an MTabRuler with the same autoSpacing and tabs.
158: */
159: public boolean equals(Object o) {
160: if (o == this ) {
161: return true;
162: } else if (o == null) {
163: return false;
164: }
165:
166: MTabRuler rhs;
167: try {
168: rhs = (MTabRuler) o;
169: } catch (ClassCastException e) {
170: return false;
171: }
172:
173: if (fAutoSpacing != rhs.autoSpacing())
174: return false;
175:
176: TabStop rhsTab = rhs.firstTab();
177:
178: if (fTabs != null) {
179: for (int i = 0; i < fTabs.length; ++i) {
180: if (!fTabs[i].equals(rhsTab))
181: return false;
182:
183: rhsTab = rhs.nextTab(rhsTab.getPosition());
184: }
185: }
186:
187: return rhsTab.getType() == TabStop.kAuto;
188: }
189:
190: /**
191: * Return debug information about this tab ruler.
192: */
193: public String toString() {
194: StringBuffer buffer = new StringBuffer(super .toString());
195: buffer.append(" auto: ");
196: buffer.append(Integer.toString(fAutoSpacing));
197:
198: if (fTabs != null) {
199: for (int i = 0; i < fTabs.length; ++i) {
200: buffer.append(fTabs[i].toString());
201: }
202: }
203:
204: return buffer.toString();
205: }
206:
207: /** Utility to convert a vector of tabs to an array. */
208:
209: private static TabStop[] tabArrayFromVector(Vector v) {
210: int count = v.size();
211: TabStop[] tabs = new TabStop[count];
212: for (int i = 0; i < count; ++i) {
213: tabs[i] = (TabStop) v.elementAt(i);
214: }
215:
216: return tabs;
217: }
218:
219: /** Utility to convert a ruler to a vector of tabs, for munging. */
220:
221: private static Vector vectorFromTabRuler(MTabRuler ruler) {
222: Vector v = new Vector();
223: for (TabStop tab = ruler.firstTab(); tab != null
224: && tab.getType() != TabStop.kAuto; tab = ruler
225: .nextTab(tab.getPosition())) {
226: v.addElement(tab);
227: }
228:
229: return v;
230: }
231:
232: /** Utility to validate an array of tabs. The array must not be null, must not contain null
233: entries, must not be kAuto, and positions must in increasing order. */
234:
235: private static void validateTabArray(TabStop[] tabs) {
236: int pos = Integer.MIN_VALUE;
237: for (int i = 0; i < tabs.length; ++i) {
238: if (tabs[i].getType() == TabStop.kAuto) {
239: throw new IllegalArgumentException(
240: "can't explicitly specify an auto tab.");
241: }
242: int nextpos = tabs[i].getPosition();
243: if (nextpos <= pos) {
244: throw new IllegalArgumentException(
245: "tab positions must be in increasing order.");
246: }
247: pos = nextpos;
248: }
249: }
250:
251: /**
252: * Return a tab ruler identical to the given ruler, except with the
253: * given tab added.
254: * @param ruler the original ruler. The MTabRuler will be the same as
255: * this except for the additional tab. <code>ruler</code> is not modified.
256: * @param tabToAdd the tab to add to the new tab ruler
257: * @return an MTabRuler resulting from this operation
258: */
259: /*public*/static MTabRuler addTabToRuler(MTabRuler ruler,
260: TabStop tabToAdd) {
261: if (ruler == null || tabToAdd == null)
262: throw new IllegalArgumentException(
263: "ruler and tabToAdd may not be null");
264:
265: Vector vector = new Vector();
266:
267: int pos = 0;
268: boolean added = false;
269: for (TabStop tab = ruler.firstTab(); tab.getType() != TabStop.kAuto; tab = ruler
270: .nextTab(pos)) {
271: pos = tab.getPosition();
272:
273: if (!added && pos >= tabToAdd.getPosition()) {
274: if (pos == tabToAdd.getPosition())
275: tab = null;
276: vector.addElement(tabToAdd);
277: added = true;
278: }
279:
280: if (tab != null)
281: vector.addElement(tab);
282: }
283: if (!added)
284: vector.addElement(tabToAdd);
285:
286: return new StandardTabRuler(vector, ruler.autoSpacing());
287: }
288:
289: /**
290: * Return a tab ruler identical to the given ruler, except with the
291: * given tab removed.
292: * @param ruler the original ruler. The MTabRuler will be the same as
293: * this except for the removed tab. <code>ruler</code> is not modified.
294: * @param position the position of the tab to remove from the new tab ruler
295: * @return an MTabRuler resulting from this operation
296: */
297: /*public*/static MTabRuler removeTabFromRuler(MTabRuler ruler,
298: int position) {
299: if (ruler == null)
300: throw new IllegalArgumentException("ruler may not be null");
301:
302: Vector vector = new Vector();
303:
304: int pos = 0;
305: boolean removed = false;
306: for (TabStop tab = ruler.firstTab(); tab.getType() != TabStop.kAuto; tab = ruler
307: .nextTab(pos)) {
308: pos = tab.getPosition();
309:
310: if (!removed && pos >= position) {
311: if (pos == position) {
312: removed = true;
313: continue; // skip this tab and continue with the remainder
314: }
315: break; // we didn't remove a tab, but skipped position, so don't bother with the rest
316: }
317:
318: vector.addElement(tab);
319: }
320: if (!removed) // no change
321: return ruler;
322:
323: if (vector.size() == 0)
324: return new StandardTabRuler(ruler.autoSpacing());
325:
326: return new StandardTabRuler(vector, ruler.autoSpacing());
327: }
328:
329: /**
330: * Return a tab ruler identical to the given ruler, except with the
331: * tab at position <code>fromPosition</code> moved to position
332: * <code>toPosition</code>.
333: * @param ruler the original ruler. The MTabRuler will be the same as
334: * this except for the moved tab. <code>ruler</code> is not modified.
335: * @param fromPosition the position of the tab to move
336: * @param toPosition the new position of the tab
337: * @return an MTabRuler resulting from this operation
338: */
339: /*public*/static MTabRuler moveTabOnRuler(MTabRuler ruler,
340: int fromPosition, int toPosition) {
341: if (ruler == null)
342: throw new IllegalArgumentException("ruler may not be null");
343:
344: Vector vector = new Vector();
345:
346: int pos = 0;
347: boolean moved = false;
348: for (TabStop tab = ruler.firstTab(); tab.getType() != TabStop.kAuto; tab = ruler
349: .nextTab(pos)) {
350: pos = tab.getPosition();
351:
352: if (!moved && pos == fromPosition) {
353: moved = true;
354: tab = new TabStop(toPosition, tab.getType()); // copy it
355: }
356:
357: vector.addElement(tab);
358: }
359: if (!moved) // no change
360: return ruler;
361:
362: return new StandardTabRuler(vector, ruler.autoSpacing());
363: }
364:
365: }
|