/*
* Copyright (C) 2005 Luca Veltri - University of Parma - Italy
*
* This file is part of MjSip (http://www.mjsip.org)
*
* MjSip is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* MjSip 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with MjSip; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author(s):
* Luca Veltri (luca.veltri@unipr.it)
*/
package org.zoolu.tools;
import java.util.*;
import com.sesca.misc.Logger;
/** Class Parser allows the parsing of String objects.
* <BR> An object Parser is costructed from a String object and provides various methods for parsing the String in a stream oriented manner.
* The class Parser also collects different <b>static</b> methods for parsing non-pre-associated strings.<BR>
* Parser uses the following definitions:<PRE>
* <BR><i>string</i> = any string of chars included between ' ' and '~'
* <BR><i>word</i> = any string of chars without separators
* <BR><i>separators</i> = a vector of chars; e.g. ( ) < > @ , ; : \ " / | [ ] ? = { } HT SP
* <BR><i>alpha</i> = a-z, A-Z
* <BR><i>digit</i> = 0-9
* <BR><i>integer</i> = any <i>digit word</i> parsed by {@link java.lang.Integer Integer.parseInt(String)}
* </PRE>
*/
public class Parser
{
/** The string that is being parsed. */
protected String str;
/** The the current pointer to the next char within the string. */
protected int index;
/** Creates the Parser from the String <i>s</i> and point to the beginning of the string.*/
public Parser(String s)
{
Logger.paranoia("Parser.Parser: s="+s);
if (s==null) throw (new RuntimeException("Tried to costruct a new Parser with a null String"));
str=s;
index=0;
}
/** Creates the Parser from the String <i>s</i> and point to the position <i>i</i>. */
public Parser(String s, int i)
{ if (s==null) throw (new RuntimeException("Tried to costruct a new Parser with a null String"));
str=s;
index=i;
}
/** Creates the Parser from the StringBuffer <i>sb</i> and point to the beginning of the string.*/
public Parser(StringBuffer sb)
{ if (sb==null) throw (new RuntimeException("Tried to costruct a new Parser with a null StringBuffer"));
str=sb.toString();
index=0;
}
/** Creates the Parser from the StringBuffer <i>sb</i> and point to the position <i>i</i>. */
public Parser(StringBuffer sb, int i)
{ if (sb==null) throw (new RuntimeException("Tried to costruct a new Parser with a null StringBuffer"));
str=sb.toString();
index=i;
}
/** Gets the current index position. */
public int getPos() { return index; }
/** Gets the entire string */
public String getWholeString() { return str; }
/** Gets the rest of the (unparsed) string. */
public String getRemainingString() { return str.substring(index); }
/** Returns a new the Parser of <i>len</i> chars statirng from the current position. */
public Parser subParser(int len) { return new Parser(str.substring(index,index+len)); }
/** Length of unparsed string. */
public int length() { return (str.length()-index); }
/** Whether there are more chars to parse. */
public boolean hasMore() { return length()>0; }
/** Gets the next char and go over */
public char getChar() { return str.charAt(index++); }
/** Gets the char at distance <i>n</i> WITHOUT going over */
public char charAt(int n) { return str.charAt(index+n); }
/** Gets the next char WITHOUT going over */
public char nextChar() { return charAt(0); }
/** Goes to position <i>i</i> */
public Parser setPos(int i) { index= i; return this; }
/** Goes to the next occurence of <i>char c</i> */
public Parser goTo(char c) { index=str.indexOf(c,index); if (index<0) index=str.length(); return this; }
/** Goes to the next occurence of any char of array <i>cc</i> */
public Parser goTo(char[] cc) { index=indexOf(cc); if (index<0) index=str.length(); return this; }
/** Goes to the next occurence of <i>String s</i> */
public Parser goTo(String s) { index=str.indexOf(s,index); if (index<0) index=str.length(); return this; }
/** Goes to the next occurence of any string of array <i>ss</i> */
public Parser goTo(String[] ss) { index=indexOf(ss); if (index<0) index=str.length(); return this; }
/** Goes to the next occurence of <i>String s</i> */
public Parser goToIgnoreCase(String s) { index=indexOfIgnoreCase(s); if (index<0) index=str.length(); return this; }
/** Goes to the next occurence of any string of array <i>ss</i> */
public Parser goToIgnoreCase(String[] ss) { index=indexOfIgnoreCase(ss); if (index<0) index=str.length(); return this; }
/** Goes to the begin of the new line */
public Parser goToNextLine()
{ while (index<str.length() && !isCRLF(str.charAt(index))) index++;
// skip the end of the line (i.e. '\r' OR '\n' OR '\r\n')
if (index<str.length()) { if (str.startsWith("\r\n",index)) index+=2; else index++; }
return this;
}
/** Characters space (SP) and tab (HT). */
public static char[] WSP={' ','\t'};
/** The same as WSP (for legacy) */
public static char[] SPACE=WSP;
/** Characters CR and LF.*/
public static char[] CRLF={'\r','\n'};
/** Characters white-space, tab, CR, and LF. */
public static char[] WSPCRLF={' ','\t','\r','\n'};
/** True if char <i>ch</i> is any char of array <i>ca</i> */
public static boolean isAnyOf(char[] ca, char ch)
{ boolean found=false;
for (int i=0; i<ca.length; i++) if (ca[i]==ch) { found=true; break; }
return found;
}
/** Up alpha */
public static boolean isUpAlpha(char c) { return (c>='A' && c<='Z'); }
/** Low alpha */
public static boolean isLowAlpha(char c) { return (c>='a' && c<='z'); }
/** Alpha */
public static boolean isAlpha(char c) { return (isUpAlpha(c) || isLowAlpha(c)); }
/** Alphanum */
public static boolean isAlphanum(char c){ return (isAlpha(c) || isDigit(c)); }
/** Digit */
public static boolean isDigit(char c) { return (c>='0' && c<='9'); }
/** Valid ASCII char */
public static boolean isChar(char c) { return (c>' ' && c<='~'); }
/** CR */
public static boolean isCR(char c) { return (c=='\r'); }
/** LF */
public static boolean isLF(char c) { return (c=='\n'); }
/** CR or LF */
public static boolean isCRLF(char c) { return isAnyOf(CRLF,c); }
/** HT */
public static boolean isHT(char c) { return (c=='\t'); }
/** SP */
public static boolean isSP(char c) { return (c==' '); }
/** SP or tab */
public static boolean isWSP(char c){ return isAnyOf(WSP,c); }
/** SP, tab, CR, or LF */
public static boolean isWSPCRLF(char c){ return isAnyOf(WSPCRLF,c); }
/** Compares two chars ignoring case */
public static int compareIgnoreCase(char c1, char c2)
{ if (isUpAlpha(c1)) c1+=32;
if (isUpAlpha(c2)) c2+=32;
return c1-c2;
}
/*
private boolean isUpAlpha(int i) { return isUpAlpha(str.charAt(i)); }
private boolean isLowAlpha(int i) { return isLowAlpha(str.charAt(i)); }
private boolean isAlpha(int i) { return isAlpha(str.charAt(i)); }
private boolean isDigit(int i) { return isDigit(str.charAt(i)); }
private boolean isChar(int i) { return isChar(str.charAt(i)); }
private boolean isCR(int i) { return isCR(str.charAt(i)); }
private boolean isLF(int i) { return isLF(str.charAt(i)); }
private boolean isHT(int i) { return isHT(str.charAt(i)); }
private boolean isSP(int i) { return isSP(str.charAt(i)); }
private boolean isCRLF(int i) { return (isCR(str.charAt(i)) && isLF(str.charAt(i+1))); }
private boolean isSeparator(int i) { return isSeparator(str.charAt(i)); }
*/
// ************************ Indexes ************************
/** Gets the index of the first occurence of char <i>c</i> */
public int indexOf(char c)
{ return str.indexOf(c,index);
}
/** Gets the index of the first occurence of any char of array <i>cc</i> within string <i>str</i> starting form <i>begin</i>; return -1 if no occurence is found*/
public int indexOf(char[] cc)
{ boolean found=false;
int begin=index;
while (begin<str.length() && !found)
{ for (int i=0; i<cc.length; i++)
if (str.charAt(begin)==cc[i]) { found=true; break; }
begin++;
}
return (found)? (begin-1) : -1;
}
/** Gets the index of the first occurence of String <i>s</i> */
public int indexOf(String s)
{ return str.indexOf(s,index);
}
/** Gets the index of the first occurence of any string of array <i>ss</i> within string <i>str</i>; return -1 if no occurence is found. */
public int indexOf(String[] ss)
{ boolean found=false;
int begin=index;
while (begin<str.length() && !found)
{ for (int i=0; i<ss.length; i++)
if (str.startsWith(ss[i],begin)) { found=true; break; }
begin++;
}
return (found)? (begin-1) : -1;
}
/** Gets the index of the first occurence of String <i>s</i> ignoring case. */
public int indexOfIgnoreCase(String s)
{ Parser par=new Parser(str,index);
while (par.hasMore())
{ if (par.startsWithIgnoreCase(s)) return par.getPos();
else par.skipChar();
}
return -1;
}
/** Gets the index of the first occurence of any string of array <i>ss</i> ignoring case. */
public int indexOfIgnoreCase(String[] ss)
{ Parser par=new Parser(str,index);
while (par.hasMore())
{ if (par.startsWithIgnoreCase(ss)) return par.getPos();
else par.skipChar();
}
return -1;
}
/** Gets the begin of next line */
public int indexOfNextLine()
{ Parser par=new Parser(str,index);
par.goToNextLine();
int i=par.getPos();
return (i<str.length())? i : -1;
}
// ********************* Starts with *********************
/** Whether next chars equal to a specific String <i>s</i>. */
public boolean startsWith(String s)
{ return str.startsWith(s,index);
}
/** Whether next chars equal to any string of array <i>ss</i>. */
public boolean startsWith(String[] ss)
{ for (int i=0; i<ss.length; i++)
if (str.startsWith(ss[i],index)) return true;
return false;
}
/** Whether next chars equal to a specific String <i>s</i> ignoring case. */
public boolean startsWithIgnoreCase(String s)
{ for (int k=0; k<s.length() && (index+k)<str.length(); k++)
{ if (compareIgnoreCase(s.charAt(k),str.charAt(index+k))!=0) return false;
}
return true;
}
/** Whether next chars equal to any string of array <i>ss</i> ignoring case. */
public boolean startsWithIgnoreCase(String[] ss)
{ for (int i=0; i<ss.length; i++)
{ boolean equal=true;
for (int k=0; k<ss[i].length() && (index+k)<str.length(); k++)
{ if (!(equal=(compareIgnoreCase(ss[i].charAt(k),str.charAt(index+k))==0))) break;
}
if (equal) return true;
}
return false;
}
// ************************ Skips ************************
/** Skips one char */
public Parser skipChar()
{ if(index<str.length()) index++;
return this;
}
/** Skips N chars */
public Parser skipN(int n)
{ index+=n;
if(index>str.length()) index=str.length();
return this;
}
/** Skips all spaces */
public Parser skipWSP()
{ while (index<str.length() && isSP(str.charAt(index))) index++;
return this;
}
/** The same as skipWSP() (for legacy) */
/*public Parser skipSP()
{ return skipWSP();
}*/
/** Skips return lines */
public Parser skipCRLF()
{ while (index<str.length() && isCRLF(str.charAt(index))) index++;
return this;
}
/** Skips white spaces or return lines */
public Parser skipWSPCRLF()
{ while (index<str.length() && isWSPCRLF(str.charAt(index))) index++;
return this;
}
/** Skips any selected chars */
public Parser skipChars(char[] cc)
{ while (index<str.length() && isAnyOf(cc,nextChar())) index++;
return this;
}
/** Skips a continuous string of char and go to the next "blank" char */
public Parser skipString()
{ getString();
return this;
}
// ************************ Gets ************************
/** Gets a continuous string of char and go to the next char */
public String getString()
{ int begin=index;
while (begin<str.length() && !isChar(str.charAt(begin))) begin++;
int end=begin;
while (end<str.length() && isChar(str.charAt(end))) end++;
index=end;
return str.substring(begin,end);
}
/** Gets a string of length <i>len</i> and move over. */
public String getString(int len)
{ int start=index;
index=start+len;
return str.substring(start,index);
}
/** Gets a string of chars separated by any of chars of <i>separators</i> */
public String getWord(char[] separators)
{ int begin=index;
while (begin<str.length() && isAnyOf(separators,str.charAt(begin))) begin++;
int end=begin;
while (end<str.length() && !isAnyOf(separators,str.charAt(end))) end++;
index=end;
return str.substring(begin,end);
}
/** Gets an integer and point to the next char */
public int getInt()
{ return Integer.parseInt(this.getString());
}
/** Gets a double and point to the next char */
public double getDouble()
{ return Double.parseDouble(this.getString());
}
/** Gets all chars until the end of the line (or the end of the parser)
* and go to the next line. */
public String getLine()
{ int end=index;
while (end<str.length() && !isCRLF(str.charAt(end))) end++;
String line=str.substring(index,end);
index=end;
// skip the end of the line (i.e. '\r' OR '\n' OR '\r\n')
if (index<str.length())
{ if (str.startsWith("\r\n",index)) index+=2;
else index++;
}
return line;
}
//********************** Vectors/arrays **********************
/** Gets all string of chars separated by any char belonging to <i>separators</i> */
public Vector getWordVector(char[] separators)
{ Vector list=new Vector();
do { list.addElement(getWord(separators)); } while (hasMore());
return list;
}
/** Gets all string of chars separated by any char belonging to <i>separators</i> */
public String[] getWordArray(char[] separators)
{ Vector list=getWordVector(separators);
String[] array=new String[list.size()];
for (int i=0; i<list.size(); i++) array[i]=(String)list.elementAt(i);
return array;
}
/** Gets all strings */
public Vector getStringVector()
{ Vector list=new Vector();
do { list.addElement(getString()); } while (hasMore());
return list;
}
/** Gets all string */
public String[] getStringArray()
{ Vector list=getStringVector();
String[] array=new String[list.size()];
for (int i=0; i<list.size(); i++) array[i]=(String)list.elementAt(i);
return array;
}
//********************** Quoted Strings **********************
/** Gets a string of chars separated by any of chars in <i>separators</i>
* , skipping any separator inside possible quoted texts. */
public String getWordSkippingQuoted(char[] separators)
{ int begin=index;
while (begin<str.length() && isAnyOf(separators,str.charAt(begin))) begin++;
boolean inside_quoted_string=false;
int end=begin;
while (end<str.length() && (!isAnyOf(separators,str.charAt(end)) || inside_quoted_string))
{ if (str.charAt(end)=='"') inside_quoted_string=!inside_quoted_string;
end++;
}
index=end;
return str.substring(begin,end);
}
/** Gets the first quatable string, that is a normal string, or text in quotes.
* <br>In the latter case, quotes are dropped. */
public String getStringUnquoted()
{ // jump possible "non-chars"
while (index<str.length() && !isChar(str.charAt(index))) index++;
if (index==str.length()) return str.substring(index,index);
// check whether is a quoted string
int next_qmark;
if (str.charAt(index)=='"' && (next_qmark=str.indexOf("\"",index+1))>0)
{ // is quoted text
String qtext=str.substring(index+1,next_qmark);
index=next_qmark+1;
return qtext;
}
else
{ // is not a quoted text
return getString();
}
}
/** Points to the next occurence of <i>char c</i> not in quotes. */
public Parser goToSkippingQuoted(char c)
{ boolean inside_quotes=false;
try
{ while (index<str.length() && (!(nextChar()==c) || inside_quotes))
{ if (nextChar()=='"') inside_quotes=!inside_quotes;
index++;
}
}
catch (RuntimeException e)
{
throw e;
}
return this;
}
//************************* toString *************************
/** convert the rest of the unparsed chars into a string */
public String toString()
{ return getRemainingString();
}
}