View Javadoc

1   /*
2    *  XNap Commons
3    *
4    *  Copyright (C) 2005  Felix Berger
5    *  Copyright (C) 2005  Steffen Pingel
6    *
7    *  This library is free software; you can redistribute it and/or
8    *  modify it under the terms of the GNU Lesser General Public
9    *  License as published by the Free Software Foundation; either
10   *  version 2.1 of the License, or (at your option) any later version.
11   *
12   *  This library is distributed in the hope that it will be useful,
13   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15   *  Lesser General Public License for more details.
16   *
17   *  You should have received a copy of the GNU Lesser General Public
18   *  License along with this library; if not, write to the Free Software
19   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20   */
21  package org.xnap.commons.util;
22  
23  import java.util.NoSuchElementException;
24  
25  /***
26   * Provides a string tokenizer that respects quotes. If a separator is followed
27   * by a quote the next token will extend to the next quote even if there are
28   * separators in between.
29   */
30  public class QuotedStringTokenizer 
31  {
32      public static final String QUOTE = "\"";
33  	public static final char QUOTE_CHAR = '"';
34  	
35      int index;
36  	boolean returnSeparators;
37  	private String separators;
38  	private String text;
39  	private int maxIndex;
40  	
41      /***
42       * Constructs a <code>QuotedStringTokenizer</code>.
43       *
44       * @see java.util.StringTokenizer
45       */
46      public QuotedStringTokenizer(String text, String separators, boolean returnSeparators) 
47      {
48  		this.text = text;
49  		this.maxIndex = text.length() - 1;
50          this.separators = separators;
51  		this.returnSeparators = returnSeparators;
52      }
53  
54      /***
55       * Constructs a <code>QuotedStringTokenizer</code>.
56       *
57       * @see java.util.StringTokenizer
58       */
59      public QuotedStringTokenizer(String text, String separators)
60      {
61  		this(text, separators, false);
62      }
63  
64      /***
65       * Constructs a <code>QuotedStringTokenizer</code>.
66       *
67       * @see java.util.StringTokenizer
68       */
69      public QuotedStringTokenizer(String text)
70      {
71  		this(text, " ", false);
72      }
73      
74      public int countTokens() 
75      {
76  		int count = 0;
77          int i = index;
78  		Token token;
79  		while ((token = nextToken(i)) != null) {
80  			count++;
81  			i = token.nextIndex;
82  		}
83  		return count;
84      }
85  
86      public boolean hasMoreTokens() 
87      {
88  		return nextToken(index) != null;
89      }
90  	
91  	public String nextToken(String separators) {
92  		this.separators = separators;
93  		return nextToken();
94  	}
95  	
96      public String nextToken() 
97      {
98  		Token token = nextToken(index);
99  		if (token != null) {
100 			index = token.nextIndex;
101 			return token.text;
102 		}
103 		throw new NoSuchElementException();
104     }
105 	
106 	protected Token nextToken(int index) 
107 	{
108 		if (index > maxIndex) {
109 			return null;
110 		}
111 		
112 		if (returnSeparators && separators.indexOf(text.charAt(index)) != -1) {
113 			return new Token(text.substring(index, index + 1), index + 1);
114 		}
115 
116 		// lets get started
117 		int i = index;
118 		boolean inQuotes = false;
119 		StringBuilder token = new StringBuilder();
120 
121 		// eat separators
122 		while (i <= maxIndex && separators.indexOf(text.charAt(i)) != -1) {
123 			i++;
124 		}
125 		
126 		if (i > maxIndex) {
127 			return null;
128 		}
129 		
130 		while(i <= maxIndex) {
131 			char c = text.charAt(i);
132 			if (separators.indexOf(c) != -1) {
133 				if (inQuotes) {
134 					token.append(c);
135 				}
136 				else {
137 					return new Token(token.toString(), i);
138 				}
139 			} 
140 			else if (c == QUOTE_CHAR) {
141 				// FIXME add escaping support
142 				inQuotes = !inQuotes;
143 			} 
144 			else {
145 				token.append(c);
146 			}
147 			i++;
148 		} 
149 		
150 		return new Token(token.toString(), i);
151 	}
152 	
153 	class Token {
154 		
155 		Token(String text, int nextIndex) {
156 			this.text = text;
157 			this.nextIndex = nextIndex;
158 		}
159 		
160 		String text;
161 		int nextIndex;
162 	}
163 	
164 }