All Downloads are FREE. Search and download functionalities are using the official Maven repository.

leap.lang.http.HeaderParser Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2012 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package leap.lang.http;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import leap.lang.Arrays2;
import leap.lang.Chars;
import leap.lang.http.Header.HeaderElement;
import leap.lang.value.ImmutableNamedValue;
import leap.lang.value.NamedValue;

final class HeaderParser {

	private final static char	PARAM_DELIMITER	= ';';
	private final static char	ELEM_DELIMITER	= ',';
	private final static char[]	ALL_DELIMITERS	= new char[] { PARAM_DELIMITER, ELEM_DELIMITER };
	
	static List parseElements(String value){
		return parseElements(value.toCharArray(),new Cursor(0,value.length()));
	}
	
	static List parseElements(final char[] buffer, final Cursor cursor) {

		if (buffer == null) {
			throw new IllegalArgumentException("Char array buffer may not be null");
		}
		if (cursor == null) {
			throw new IllegalArgumentException("Parser cursor may not be null");
		}

		List elements = new ArrayList();
		while (!cursor.atEnd()) {
			HeaderElement element = parseHeaderElement(buffer, cursor);
			if (!(element.getName().length() == 0 && element.getValue() == null)) {
				elements.add(element);
			}
		}
		return elements;
	}
	
	private static HeaderElement parseHeaderElement(final char[] buffer, final Cursor cursor) {

		if (buffer == null) {
			throw new IllegalArgumentException("Char array buffer may not be null");
		}
		if (cursor == null) {
			throw new IllegalArgumentException("Parser cursor may not be null");
		}

		NamedValue nvp = parseNameValuePair(buffer, cursor,ALL_DELIMITERS);
		
		Map params = null;
		
		if (!cursor.atEnd()) {
			char ch = buffer[cursor.pos - 1];
			if (ch != ELEM_DELIMITER) {
				params = parseParameters(buffer, cursor);
			}
		}
		return new HeaderElement(nvp.getName(), nvp.getValue(), params);
	}

	private static NamedValue parseNameValuePair(final char[] buffer, final Cursor cursor, final char[] delimiters) {

		if (buffer == null) {
			throw new IllegalArgumentException("Char array buffer may not be null");
		}
		if (cursor == null) {
			throw new IllegalArgumentException("Parser cursor may not be null");
		}

		boolean terminated = false;

		int pos = cursor.pos;
		int indexFrom = cursor.pos;
		int indexTo = cursor.upperBound;

		// Find name
		String name = null;
		while (pos < indexTo) {
			char ch = buffer[pos];
			if (ch == '=') {
				break;
			}
			if (Arrays2.contains(delimiters, ch)) {
				terminated = true;
				break;
			}
			pos++;
		}

		if (pos == indexTo) {
			terminated = true;
			name = Chars.substring(buffer,indexFrom, indexTo).trim();
		} else {
			name = Chars.substring(buffer,indexFrom, pos).trim();
			pos++;
		}

		if (terminated) {
			cursor.updatePos(pos);
			return ImmutableNamedValue.of(name, null);
		}

		// Find value
		String value = null;
		int i1 = pos;

		boolean qouted = false;
		boolean escaped = false;
		while (pos < indexTo) {
			char ch = buffer[pos];
			if (ch == '"' && !escaped) {
				qouted = !qouted;
			}
			if (!qouted && !escaped && Arrays2.contains(delimiters,ch)) {
				terminated = true;
				break;
			}
			if (escaped) {
				escaped = false;
			} else {
				escaped = qouted && ch == '\\';
			}
			pos++;
		}

		int i2 = pos;
		// Trim leading white spaces
		while (i1 < i2 && (Character.isWhitespace(buffer[i1]))) {
			i1++;
		}
		// Trim trailing white spaces
		while ((i2 > i1) && (Character.isWhitespace(buffer[i2 - 1]))) {
			i2--;
		}
		// Strip away quotes if necessary
		if (((i2 - i1) >= 2) && (buffer[i1] == '"') && (buffer[i2 - 1] == '"')) {
			i1++;
			i2--;
		}
		value = Chars.substring(buffer,i1, i2);
		if (terminated) {
			pos++;
		}
		cursor.updatePos(pos);
		return ImmutableNamedValue.of(name, value);
	}
	
	private static Map parseParameters(final char[] buffer, final Cursor cursor) {

		int pos = cursor.pos;
		int indexTo = cursor.upperBound;

		while (pos < indexTo) {
			char ch = buffer[pos];
			if (Character.isWhitespace(ch)) {
				pos++;
			} else {
				break;
			}
		}
		
		cursor.updatePos(pos);
		if (cursor.atEnd()) {
			return new HashMap();
		}

		Map params = HeaderElement.createParametersMap(null);
		
		while (!cursor.atEnd()) {
			NamedValue param = parseNameValuePair(buffer, cursor,ALL_DELIMITERS);
			params.put(param.getName(), param.getValue());
			char ch = buffer[cursor.pos - 1];
			if (ch == ELEM_DELIMITER) {
				break;
			}
		}

		return params;
	}
	
	private static class Cursor {
	    private final int lowerBound;
	    private final int upperBound;
	    private int pos;

	    public Cursor(int lowerBound, int upperBound) {
	        if (lowerBound < 0) {
	            throw new IndexOutOfBoundsException("Lower bound cannot be negative");
	        }
	        if (lowerBound > upperBound) {
	            throw new IndexOutOfBoundsException("Lower bound cannot be greater then upper bound");
	        }
	        this.lowerBound = lowerBound;
	        this.upperBound = upperBound;
	        this.pos = lowerBound;
	    }

	    public void updatePos(int pos) {
	        if (pos < this.lowerBound) {
	            throw new IndexOutOfBoundsException("pos: "+pos+" < lowerBound: "+this.lowerBound);
	        }
	        if (pos > this.upperBound) {
	            throw new IndexOutOfBoundsException("pos: "+pos+" > upperBound: "+this.upperBound);
	        }
	        this.pos = pos;
	    }

	    public boolean atEnd() {
	        return this.pos >= this.upperBound;
	    }
	}	
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy