001: /*
002: * Copyright 2001-2004 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package net.myvietnam.mvncore.web.fileupload;
017:
018: import java.util.HashMap;
019: import java.util.Map;
020:
021: /**
022: * A simple parser intended to parse sequences of name/value pairs.
023: * Parameter values are exptected to be enclosed in quotes if they
024: * contain unsafe characters, such as '=' characters or separators.
025: * Parameter values are optional and can be omitted.
026: *
027: * <p>
028: * <code>param1 = value; param2 = "anything goes; really"; param3</code>
029: * </p>
030: *
031: * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
032: *
033: * @version $Id: ParameterParser.java,v 1.2 2006/02/12 04:43:11 minhnn Exp $
034: */
035:
036: public class ParameterParser {
037: /**
038: * String to be parsed.
039: */
040: private char[] chars = null;
041:
042: /**
043: * Current position in the string.
044: */
045: private int pos = 0;
046:
047: /**
048: * Maximum position in the string.
049: */
050: private int len = 0;
051:
052: /**
053: * Start of a token.
054: */
055: private int i1 = 0;
056:
057: /**
058: * End of a token.
059: */
060: private int i2 = 0;
061:
062: /**
063: * Whether names stored in the map should be converted to lower case.
064: */
065: private boolean lowerCaseNames = false;
066:
067: /**
068: * Default ParameterParser constructor.
069: */
070: public ParameterParser() {
071: super ();
072: }
073:
074: /**
075: * Are there any characters left to parse?
076: *
077: * @return <tt>true</tt> if there are unparsed characters,
078: * <tt>false</tt> otherwise.
079: */
080: private boolean hasChar() {
081: return this .pos < this .len;
082: }
083:
084: /**
085: * A helper method to process the parsed token. This method removes
086: * leading and trailing blanks as well as enclosing quotation marks,
087: * when necessary.
088: *
089: * @param quoted <tt>true</tt> if quotation marks are expected,
090: * <tt>false</tt> otherwise.
091: * @return the token
092: */
093: private String getToken(boolean quoted) {
094: // Trim leading white spaces
095: while ((i1 < i2) && (Character.isWhitespace(chars[i1]))) {
096: i1++;
097: }
098: // Trim trailing white spaces
099: while ((i2 > i1) && (Character.isWhitespace(chars[i2 - 1]))) {
100: i2--;
101: }
102: // Strip away quotation marks if necessary
103: if (quoted) {
104: if (((i2 - i1) >= 2) && (chars[i1] == '"')
105: && (chars[i2 - 1] == '"')) {
106: i1++;
107: i2--;
108: }
109: }
110: String result = null;
111: if (i2 > i1) {
112: result = new String(chars, i1, i2 - i1);
113: }
114: return result;
115: }
116:
117: /**
118: * Tests if the given character is present in the array of characters.
119: *
120: * @param ch the character to test for presense in the array of characters
121: * @param charray the array of characters to test against
122: *
123: * @return <tt>true</tt> if the character is present in the array of
124: * characters, <tt>false</tt> otherwise.
125: */
126: private boolean isOneOf(char ch, final char[] charray) {
127: boolean result = false;
128: for (int i = 0; i < charray.length; i++) {
129: if (ch == charray[i]) {
130: result = true;
131: break;
132: }
133: }
134: return result;
135: }
136:
137: /**
138: * Parses out a token until any of the given terminators
139: * is encountered.
140: *
141: * @param terminators the array of terminating characters. Any of these
142: * characters when encountered signify the end of the token
143: *
144: * @return the token
145: */
146: private String parseToken(final char[] terminators) {
147: char ch;
148: i1 = pos;
149: i2 = pos;
150: while (hasChar()) {
151: ch = chars[pos];
152: if (isOneOf(ch, terminators)) {
153: break;
154: }
155: i2++;
156: pos++;
157: }
158: return getToken(false);
159: }
160:
161: /**
162: * Parses out a token until any of the given terminators
163: * is encountered outside the quotation marks.
164: *
165: * @param terminators the array of terminating characters. Any of these
166: * characters when encountered outside the quotation marks signify the end
167: * of the token
168: *
169: * @return the token
170: */
171: private String parseQuotedToken(final char[] terminators) {
172: char ch;
173: i1 = pos;
174: i2 = pos;
175: boolean quoted = false;
176: boolean charEscaped = false;
177: while (hasChar()) {
178: ch = chars[pos];
179: if (!quoted && isOneOf(ch, terminators)) {
180: break;
181: }
182: if (!charEscaped && ch == '"') {
183: quoted = !quoted;
184: }
185: charEscaped = (!charEscaped && ch == '\\');
186: i2++;
187: pos++;
188:
189: }
190: return getToken(true);
191: }
192:
193: /**
194: * Returns <tt>true</tt> if parameter names are to be converted to lower
195: * case when name/value pairs are parsed.
196: *
197: * @return <tt>true</tt> if parameter names are to be
198: * converted to lower case when name/value pairs are parsed.
199: * Otherwise returns <tt>false</tt>
200: */
201: public boolean isLowerCaseNames() {
202: return this .lowerCaseNames;
203: }
204:
205: /**
206: * Sets the flag if parameter names are to be converted to lower case when
207: * name/value pairs are parsed.
208: *
209: * @param b <tt>true</tt> if parameter names are to be
210: * converted to lower case when name/value pairs are parsed.
211: * <tt>false</tt> otherwise.
212: */
213: public void setLowerCaseNames(boolean b) {
214: this .lowerCaseNames = b;
215: }
216:
217: /**
218: * Extracts a map of name/value pairs from the given string. Names are
219: * expected to be unique.
220: *
221: * @param str the string that contains a sequence of name/value pairs
222: * @param separator the name/value pairs separator
223: *
224: * @return a map of name/value pairs
225: */
226: public Map parse(final String str, char separator) {
227: if (str == null) {
228: return new HashMap();
229: }
230: return parse(str.toCharArray(), separator);
231: }
232:
233: /**
234: * Extracts a map of name/value pairs from the given array of
235: * characters. Names are expected to be unique.
236: *
237: * @param chars the array of characters that contains a sequence of
238: * name/value pairs
239: * @param separator the name/value pairs separator
240: *
241: * @return a map of name/value pairs
242: */
243: public Map parse(final char[] chars, char separator) {
244: if (chars == null) {
245: return new HashMap();
246: }
247: return parse(chars, 0, chars.length, separator);
248: }
249:
250: /**
251: * Extracts a map of name/value pairs from the given array of
252: * characters. Names are expected to be unique.
253: *
254: * @param chars the array of characters that contains a sequence of
255: * name/value pairs
256: * @param offset - the initial offset.
257: * @param length - the length.
258: * @param separator the name/value pairs separator
259: *
260: * @return a map of name/value pairs
261: */
262: public Map parse(final char[] chars, int offset, int length,
263: char separator) {
264:
265: if (chars == null) {
266: return new HashMap();
267: }
268: HashMap params = new HashMap();
269: this .chars = chars;
270: this .pos = offset;
271: this .len = length;
272:
273: String paramName = null;
274: String paramValue = null;
275: while (hasChar()) {
276: paramName = parseToken(new char[] { '=', separator });
277: paramValue = null;
278: if (hasChar() && (chars[pos] == '=')) {
279: pos++; // skip '='
280: paramValue = parseQuotedToken(new char[] { separator });
281: }
282: if (hasChar() && (chars[pos] == separator)) {
283: pos++; // skip separator
284: }
285: if ((paramName != null) && (paramName.length() > 0)) {
286: if (this.lowerCaseNames) {
287: paramName = paramName.toLowerCase();
288: }
289: params.put(paramName, paramValue);
290: }
291: }
292: return params;
293: }
294: }
|