/*
* JLibs: Common Utilities for Java
* Copyright (C) 2009 Santhosh Kumar T <santhosh.tekuri@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package jlibs.nio.http.msg.parser;
import jlibs.nio.Reactor;
import jlibs.nio.http.msg.FormPayload;
import jlibs.nio.util.BytesDecoder;
import jlibs.nio.util.Parser;
import java.io.EOFException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import static jlibs.nio.http.msg.parser.FormParser.State.*;
/**
* @author Santhosh Kumar Tekuri
*/
public class FormParser implements Parser{
public final FormPayload payload = new FormPayload();
private Charset charset;
public FormParser(Charset charset){
this.charset = charset;
}
private String name;
enum State{ NORMAL, HEX1, HEX2 }
private State state = NORMAL;
private int hex;
@Override
public boolean parse(ByteBuffer buffer, boolean eof) throws IOException{
char ch;
while(buffer.hasRemaining()){
switch(state){
case NORMAL:
while(buffer.hasRemaining()){
ch = (char)buffer.get();
if(ch=='+')
builder().append(' ');
else if(ch=='='){
if(name==null){
name = builder().toString();
builder.setLength(0);
}else
builder().append('=');
}else if(ch=='&')
addParam();
else if(ch=='%'){
state = HEX1;
break;
}else
builder().append(ch);
}
if(!buffer.hasRemaining())
break;
case HEX1:
ch = (char)buffer.get();
if(ch>='0' && ch<='9')
hex = ch-'0';
else if(ch>='a' && ch<='f')
hex = 10 + (ch-'a');
else if(ch>='A' && ch<='F')
hex = 10 + (ch-'A');
else
throw new IOException("Bad Hex Char");
hex *= 16;
state = HEX2;
if(!buffer.hasRemaining())
break;
case HEX2:
ch = (char)buffer.get();
if(ch>='0' && ch<='9')
hex += ch-'0';
else if(ch>='a' && ch<='f')
hex += 10 + (ch-'a');
else if(ch>='A' && ch<='F')
hex += 10 + (ch-'A');
else
throw new IOException("Bad Hex Char");
if(hex<0)
throw new IOException("Bad Hex Code");
if(hexBuffer==null){
hexBuffer = Reactor.current().allocator.allocate();
decoder = new BytesDecoder(builder, charset.newDecoder(), 100);
}else if(!hexBuffer.hasRemaining()){
hexBuffer.flip();
decoder.write(hexBuffer, false);
hexBuffer.compact();
}
hexBuffer.put((byte)hex);
state = NORMAL;
}
}
if(eof){
if(state!=NORMAL)
throw new EOFException("Malformed Form Payload");
addParam();
}
return eof;
}
private ByteBuffer hexBuffer;
private BytesDecoder decoder;
private StringBuilder builder = new StringBuilder();
private StringBuilder builder() throws IOException{
if(hexBuffer!=null && hexBuffer.position()>0){
hexBuffer.flip();
decoder.write(hexBuffer, true);
hexBuffer.clear();
}
return builder;
}
private void addParam() throws IOException{
String value = builder().toString();
builder.setLength(0);
if(name==null)
payload.addParam(value, "");
else{
payload.addParam(name, value);
name = null;
}
}
@Override
public void cleanup(){
if(hexBuffer!=null){
Reactor.current().allocator.free(hexBuffer);
hexBuffer = null;
}
}
}