001: /*
002: * Copyright 2004 by Paulo Soares.
003: *
004: * The contents of this file are subject to the Mozilla Public License Version 1.1
005: * (the "License"); you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at http://www.mozilla.org/MPL/
007: *
008: * Software distributed under the License is distributed on an "AS IS" basis,
009: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
010: * for the specific language governing rights and limitations under the License.
011: *
012: * The Original Code is 'iText, a free JAVA-PDF library'.
013: *
014: * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
015: * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
016: * All Rights Reserved.
017: * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
018: * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
019: *
020: * Contributor(s): all the names of the contributors are added in the source code
021: * where applicable.
022: *
023: * Alternatively, the contents of this file may be used under the terms of the
024: * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
025: * provisions of LGPL are applicable instead of those above. If you wish to
026: * allow use of your version of this file only under the terms of the LGPL
027: * License and not to allow others to use your version of this file under
028: * the MPL, indicate your decision by deleting the provisions above and
029: * replace them with the notice and other provisions required by the LGPL.
030: * If you do not delete the provisions above, a recipient may use your version
031: * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
032: *
033: * This library is free software; you can redistribute it and/or modify it
034: * under the terms of the MPL as stated above or under the terms of the GNU
035: * Library General Public License as published by the Free Software Foundation;
036: * either version 2 of the License, or any later version.
037: *
038: * This library is distributed in the hope that it will be useful, but WITHOUT
039: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
040: * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
041: * details.
042: *
043: * If you didn't download this code from the following link, you should check if
044: * you aren't using an obsolete version:
045: * http://www.lowagie.com/iText/
046: */
047: package com.lowagie.text.pdf;
048:
049: import java.util.LinkedList;
050: import java.util.List;
051: import java.util.ListIterator;
052:
053: /**
054: * This class expands a string into a list of numbers. The main use is to select a
055: * range of pages.
056: * <p>
057: * The general systax is:<br>
058: * [!][o][odd][e][even]start-end
059: * <p>
060: * You can have multiple ranges separated by commas ','. The '!' modifier removes the
061: * range from what is already selected. The range changes are incremental, that is,
062: * numbers are added or deleted as the range appears. The start or the end, but not both, can be ommited.
063: */
064: public class SequenceList {
065: protected static final int COMMA = 1;
066: protected static final int MINUS = 2;
067: protected static final int NOT = 3;
068: protected static final int TEXT = 4;
069: protected static final int NUMBER = 5;
070: protected static final int END = 6;
071: protected static final char EOT = '\uffff';
072:
073: private static final int FIRST = 0;
074: private static final int DIGIT = 1;
075: private static final int OTHER = 2;
076: private static final int DIGIT2 = 3;
077: private static final String NOT_OTHER = "-,!0123456789";
078:
079: protected char text[];
080: protected int ptr;
081: protected int number;
082: protected String other;
083:
084: protected int low;
085: protected int high;
086: protected boolean odd;
087: protected boolean even;
088: protected boolean inverse;
089:
090: protected SequenceList(String range) {
091: ptr = 0;
092: text = range.toCharArray();
093: }
094:
095: protected char nextChar() {
096: while (true) {
097: if (ptr >= text.length)
098: return EOT;
099: char c = text[ptr++];
100: if (c > ' ')
101: return c;
102: }
103: }
104:
105: protected void putBack() {
106: --ptr;
107: if (ptr < 0)
108: ptr = 0;
109: }
110:
111: protected int getType() {
112: StringBuffer buf = new StringBuffer();
113: int state = FIRST;
114: while (true) {
115: char c = nextChar();
116: if (c == EOT) {
117: if (state == DIGIT) {
118: number = Integer.parseInt(other = buf.toString());
119: return NUMBER;
120: } else if (state == OTHER) {
121: other = buf.toString().toLowerCase();
122: return TEXT;
123: }
124: return END;
125: }
126: switch (state) {
127: case FIRST:
128: switch (c) {
129: case '!':
130: return NOT;
131: case '-':
132: return MINUS;
133: case ',':
134: return COMMA;
135: }
136: buf.append(c);
137: if (c >= '0' && c <= '9')
138: state = DIGIT;
139: else
140: state = OTHER;
141: break;
142: case DIGIT:
143: if (c >= '0' && c <= '9')
144: buf.append(c);
145: else {
146: putBack();
147: number = Integer.parseInt(other = buf.toString());
148: return NUMBER;
149: }
150: break;
151: case OTHER:
152: if (NOT_OTHER.indexOf(c) < 0)
153: buf.append(c);
154: else {
155: putBack();
156: other = buf.toString().toLowerCase();
157: return TEXT;
158: }
159: break;
160: }
161: }
162: }
163:
164: private void otherProc() {
165: if (other.equals("odd") || other.equals("o")) {
166: odd = true;
167: even = false;
168: } else if (other.equals("even") || other.equals("e")) {
169: odd = false;
170: even = true;
171: }
172: }
173:
174: protected boolean getAttributes() {
175: low = -1;
176: high = -1;
177: odd = even = inverse = false;
178: int state = OTHER;
179: while (true) {
180: int type = getType();
181: if (type == END || type == COMMA) {
182: if (state == DIGIT)
183: high = low;
184: return (type == END);
185: }
186: switch (state) {
187: case OTHER:
188: switch (type) {
189: case NOT:
190: inverse = true;
191: break;
192: case MINUS:
193: state = DIGIT2;
194: break;
195: default:
196: if (type == NUMBER) {
197: low = number;
198: state = DIGIT;
199: } else
200: otherProc();
201: break;
202: }
203: break;
204: case DIGIT:
205: switch (type) {
206: case NOT:
207: inverse = true;
208: state = OTHER;
209: high = low;
210: break;
211: case MINUS:
212: state = DIGIT2;
213: break;
214: default:
215: high = low;
216: state = OTHER;
217: otherProc();
218: break;
219: }
220: break;
221: case DIGIT2:
222: switch (type) {
223: case NOT:
224: inverse = true;
225: state = OTHER;
226: break;
227: case MINUS:
228: break;
229: case NUMBER:
230: high = number;
231: state = OTHER;
232: break;
233: default:
234: state = OTHER;
235: otherProc();
236: break;
237: }
238: break;
239: }
240: }
241: }
242:
243: /**
244: * Generates a list of numbers from a string.
245: * @param ranges the comma separated ranges
246: * @param maxNumber the maximum number in the range
247: * @return a list with the numbers as <CODE>Integer</CODE>
248: */
249: public static List expand(String ranges, int maxNumber) {
250: SequenceList parse = new SequenceList(ranges);
251: LinkedList list = new LinkedList();
252: boolean sair = false;
253: while (!sair) {
254: sair = parse.getAttributes();
255: if (parse.low == -1 && parse.high == -1 && !parse.even
256: && !parse.odd)
257: continue;
258: if (parse.low < 1)
259: parse.low = 1;
260: if (parse.high < 1 || parse.high > maxNumber)
261: parse.high = maxNumber;
262: if (parse.low > maxNumber)
263: parse.low = maxNumber;
264:
265: //System.out.println("low="+parse.low+",high="+parse.high+",odd="+parse.odd+",even="+parse.even+",inverse="+parse.inverse);
266: int inc = 1;
267: if (parse.inverse) {
268: if (parse.low > parse.high) {
269: int t = parse.low;
270: parse.low = parse.high;
271: parse.high = t;
272: }
273: for (ListIterator it = list.listIterator(); it
274: .hasNext();) {
275: int n = ((Integer) it.next()).intValue();
276: if (parse.even && (n & 1) == 1)
277: continue;
278: if (parse.odd && (n & 1) == 0)
279: continue;
280: if (n >= parse.low && n <= parse.high)
281: it.remove();
282: }
283: } else {
284: if (parse.low > parse.high) {
285: inc = -1;
286: if (parse.odd || parse.even) {
287: --inc;
288: if (parse.even)
289: parse.low &= ~1;
290: else
291: parse.low -= ((parse.low & 1) == 1 ? 0 : 1);
292: }
293: for (int k = parse.low; k >= parse.high; k += inc)
294: list.add(new Integer(k));
295: } else {
296: if (parse.odd || parse.even) {
297: ++inc;
298: if (parse.odd)
299: parse.low |= 1;
300: else
301: parse.low += ((parse.low & 1) == 1 ? 1 : 0);
302: }
303: for (int k = parse.low; k <= parse.high; k += inc) {
304: list.add(new Integer(k));
305: }
306: }
307: }
308: // for (int k = 0; k < list.size(); ++k)
309: // System.out.print(((Integer)list.get(k)).intValue() + ",");
310: // System.out.println();
311: }
312: return list;
313: }
314: }
|