/* * 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.util; import java.util.function.Function; /** * @author Santhosh Kumar Tekuri */ public class Parser{ private final boolean foldable; public Parser(boolean foldable, String string){ this.foldable = foldable; reset(string); } private String string; public String string(){ return string; } private int index; public int index(){ return index; } public boolean isEmpty(){ return index>=string.length(); } public void reset(String string){ this.string = string; index = 0; skipWhitespace(); } public void skip(){ ++index; skipWhitespace(); } private void skipWhitespace(){ while(index<string.length() && Character.isWhitespace(string.charAt(index))) ++index; } /*-------------------------------------------------[ Index-Of ]---------------------------------------------------*/ public int indexOf(char... candidates){ int i = index; while(i<string.length()){ char ch = string.charAt(i); if(ch=='"') i = matchingQuote(i); else{ for(char candidate: candidates){ if(ch==candidate) return i; } } ++i; } return -1; } /*-------------------------------------------------[ User-Methods ]---------------------------------------------------*/ private String value(int to){ while(to>index && Character.isWhitespace(string.charAt(to-1))) --to; if(string.charAt(index)=='"' && matchingQuote(index)==to-1) return quotedString(); else return string.substring(index, to); } public String value(char... delimiters){ skipWhitespace(); int delimiter = indexOf(delimiters); if(delimiter==-1) delimiter = string.length(); String value = value(delimiter); index = delimiter; return value; } private static final char COMMA[] = { ',' }; private static final char SEMICOLON[] = { ';' }; private static final char EQUAL_SEMICOLON[] = { '=', ';' }; private static final char EQUAL_SEMICOLON_COMMA[] = { '=', ';', ',' }; private static final char SEMICOLON_COMMA[] = { ';', ',' }; public String value(){ if(foldable){ if(isEmpty()) return null; if(string.charAt(index)==',') skip(); return value(COMMA); }else return value(string.length()); } public String lvalue(){ if(isEmpty() || string.charAt(index)==',') return null; else{ if(string.charAt(index)==';') skip(); return value(foldable ? EQUAL_SEMICOLON_COMMA : EQUAL_SEMICOLON); } } public String rvalue(){ if(isEmpty() || string.charAt(index)!='=') return null; else{ skip(); return value(foldable ? SEMICOLON_COMMA : SEMICOLON); } } public void skipPairs(){ while(lvalue()!=null) rvalue(); } /*-------------------------------------------------[ Helper ]---------------------------------------------------*/ private int matchingQuote(int i){ if(string.charAt(i)!='"') throw new UnsupportedOperationException(); ++i; boolean escape = false; while(i<string.length()){ char ch = string.charAt(i); if(escape) escape = false; else if(ch=='\\') escape = true; else if(ch=='"') return i; ++i; } throw new IllegalArgumentException("unterminated quoted-string"); } public String quotedString(){ if(string.charAt(index)!='"') throw new UnsupportedOperationException(); StringBuilder buffer = null; ++index; int begin = index; boolean escape = false; while(index<string.length()){ char ch = string.charAt(index); if(escape){ buffer.append(ch); escape = false; }else if(ch=='\\'){ escape = true; if(buffer==null) buffer = new StringBuilder().append(string.substring(begin, index)); }else if(ch=='"'){ ++index; return buffer==null ? string.substring(begin, index-1) : buffer.toString(); }else if(buffer!=null) buffer.append(ch); ++index; } throw new IllegalArgumentException("unterminated quoted-string"); } public static StringBuilder appendQuotedValue(StringBuilder buffer, String name, String value){ buffer.append(name); if(value!=null){ buffer.append('='); appendQuotedString(buffer, value); } return buffer; } public static StringBuilder appendQuotedString(StringBuilder buffer, String value){ buffer.append('"'); for(int i=0; i<value.length(); i++){ char ch = value.charAt(i); if(ch=='\\' || ch=='"') buffer.append('\\'); buffer.append(ch); } buffer.append('"'); return buffer; } public static StringBuilder appendValue(StringBuilder buffer, String name, String value){ buffer.append(name); if(value!=null){ buffer.append('='); appendQuotedIfWhitespace(buffer, value); } return buffer; } public static StringBuilder appendQuotedIfWhitespace(StringBuilder buffer, String value){ boolean hasWhitespace = false; for(int i=0; i<value.length(); i++){ if(Character.isWhitespace(value.charAt(i))){ hasWhitespace = true; break; } } if(hasWhitespace) appendQuotedString(buffer, value); else buffer.append(value); return buffer; } public static final Function<Parser, String> LVALUE_FUNCTION = parser -> { String value = parser.lvalue(); parser.rvalue(); parser.skipPairs(); return value; }; public static <T> Function<Parser, T> lvalueDelegate(Function<String, T> delegate){ return parser -> { String value = parser.lvalue(); parser.rvalue(); parser.skipPairs(); return delegate.apply(value); }; } }