using System;
using System.Text;
namespace LumiSoft.Net
{
/// <summary>
/// String reader.
/// </summary>
public class StringReader
{
private string m_OriginalString = "";
private string m_SourceString = "";
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="source">Source string.</param>
public StringReader(string source)
{
m_OriginalString = source;
m_SourceString = source;
}
// SYNC JAVA
#region method AppenString
/// <summary>
/// Appends specified string to SourceString.
/// </summary>
/// <param name="str">String value to append.</param>
public void AppenString(string str)
{
m_SourceString += str;
}
#endregion
#region method ReadToFirstChar
/// <summary>
/// Reads to first char, skips SP,VTAB,HTAB,CR,LF from the beginning of source string.
/// </summary>
public void ReadToFirstChar()
{
m_SourceString = m_SourceString.TrimStart();
}
#endregion
// public string ReadToDelimiter(char delimiter)
// {
// }
#region method ReadSpecifiedLength
/// <summary>
/// Reads string with specified length. Throws exception if read length is bigger than source string length.
/// </summary>
/// <param name="length">Number of chars to read.</param>
/// <returns></returns>
public string ReadSpecifiedLength(int length)
{
if(m_SourceString.Length >= length){
string retVal = m_SourceString.Substring(0,length);
m_SourceString = m_SourceString.Substring(length);
return retVal;
}
else{
throw new Exception("Read length can't be bigger than source string !");
}
}
#endregion
// sync java
#region method QuotedReadToDelimiter
/// <summary>
/// Reads string to specified delimiter or to end of underlying string. Notes: Delimiter in quoted string is skipped.
/// Delimiter is removed by default.
/// For example: delimiter = ',', text = '"aaaa,eee",qqqq' - then result is '"aaaa,eee"'.
/// </summary>
/// <param name="delimiter">Data delimiter.</param>
/// <returns></returns>
public string QuotedReadToDelimiter(char delimiter)
{
return QuotedReadToDelimiter(new char[]{delimiter});
}
/// <summary>
/// Reads string to specified delimiter or to end of underlying string. Notes: Delimiters in quoted string is skipped.
/// Delimiter is removed by default.
/// For example: delimiter = ',', text = '"aaaa,eee",qqqq' - then result is '"aaaa,eee"'.
/// </summary>
/// <param name="delimiters">Data delimiters.</param>
/// <returns></returns>
public string QuotedReadToDelimiter(char[] delimiters)
{
return QuotedReadToDelimiter(delimiters,true);
}
/// <summary>
/// Reads string to specified delimiter or to end of underlying string. Notes: Delimiters in quoted string is skipped.
/// For example: delimiter = ',', text = '"aaaa,eee",qqqq' - then result is '"aaaa,eee"'.
/// </summary>
/// <param name="delimiters">Data delimiters.</param>
/// <param name="removeDelimiter">Specifies if delimiter is removed from underlying string.</param>
/// <returns></returns>
public string QuotedReadToDelimiter(char[] delimiters,bool removeDelimiter)
{
StringBuilder currentSplitBuffer = new StringBuilder(); // Holds active
bool inQuotedString = false; // Holds flag if position is quoted string or not
char lastChar = (char)0;
for(int i=0;i<m_SourceString.Length;i++){
char c = m_SourceString[i];
// Skip escaped(\) "
if(lastChar != '\\' && c == '\"'){
// Start/end quoted string area
inQuotedString = !inQuotedString;
}
// See if char is delimiter
bool isDelimiter = false;
foreach(char delimiter in delimiters){
if(c == delimiter){
isDelimiter = true;
break;
}
}
// Current char is split char and it isn't in quoted string, do split
if(!inQuotedString && isDelimiter){
string retVal = currentSplitBuffer.ToString();
// Remove readed string + delimiter from source string
if(removeDelimiter){
m_SourceString = m_SourceString.Substring(retVal.Length + 1);
}
// Remove readed string
else{
m_SourceString = m_SourceString.Substring(retVal.Length);
}
return retVal;
}
else{
currentSplitBuffer.Append(c);
}
lastChar = c;
}
// If we reached so far then we are end of string, return it
m_SourceString = "";
return currentSplitBuffer.ToString();
}
#endregion
// SYNC JAVA
#region method ReadWord
/// <summary>
/// Reads word from string. Returns null if no word is available.
/// Word reading begins from first char, for example if SP"text", then space is trimmed.
/// </summary>
/// <returns></returns>
public string ReadWord()
{
return ReadWord(true);
}
/// <summary>
/// Reads word from string. Returns null if no word is available.
/// Word reading begins from first char, for example if SP"text", then space is trimmed.
/// </summary>
/// <param name="unQuote">Specifies if quoted string word is unquoted.</param>
/// <returns></returns>
public string ReadWord(bool unQuote)
{
return ReadWord(unQuote,new char[]{' ',',',';','{','}','(',')','[',']','<','>','\r','\n'},false);
}
/// <summary>
/// Reads word from string. Returns null if no word is available.
/// Word reading begins from first char, for example if SP"text", then space is trimmed.
/// </summary>
/// <param name="unQuote">Specifies if quoted string word is unquoted.</param>
/// <param name="wordTerminatorChars">Specifies chars what terminate word.</param>
/// <param name="removeWordTerminator">Specifies if work terminator is removed.</param>
/// <returns></returns>
public string ReadWord(bool unQuote,char[] wordTerminatorChars,bool removeWordTerminator)
{
// Always start word reading from first char.
this.ReadToFirstChar();
if(this.Available == 0){
return null;
}
// quoted word can contain any char, " must be escaped with \
// unqouted word can conatin any char except: SP VTAB HTAB,{}()[]<>
if(m_SourceString.StartsWith("\"")){
if(unQuote){
return TextUtils.UnQuoteString(QuotedReadToDelimiter(wordTerminatorChars,removeWordTerminator));
}
else{
return QuotedReadToDelimiter(wordTerminatorChars,removeWordTerminator);
}
}
else{
int wordLength = 0;
for(int i=0;i<m_SourceString.Length;i++){
char c = m_SourceString[i];
bool isTerminator = false;
foreach(char terminator in wordTerminatorChars){
if(c == terminator){
isTerminator = true;
break;
}
}
if(isTerminator){
break;
}
wordLength++;
}
string retVal = m_SourceString.Substring(0,wordLength);
if(removeWordTerminator){
if(m_SourceString.Length >= wordLength + 1){
m_SourceString = m_SourceString.Substring(wordLength + 1);
}
}
else{
m_SourceString = m_SourceString.Substring(wordLength);
}
return retVal;
}
}
#endregion
// SYNC JAVA
#region method ReadParenthesized
/// <summary>
/// Reads parenthesized value. Supports {},(),[],<> parenthesis.
/// Throws exception if there isn't parenthesized value or closing parenthesize is missing.
/// </summary>
/// <returns></returns>
public string ReadParenthesized()
{
ReadToFirstChar();
char startingChar = ' ';
char closingChar = ' ';
if(m_SourceString.StartsWith("{")){
startingChar = '{';
closingChar = '}';
}
else if(m_SourceString.StartsWith("(")){
startingChar = '(';
closingChar = ')';
}
else if(m_SourceString.StartsWith("[")){
startingChar = '[';
closingChar = ']';
}
else if(m_SourceString.StartsWith("<")){
startingChar = '<';
closingChar = '>';
}
else{
throw new Exception("No parenthesized value '" + m_SourceString + "' !");
}
bool inQuotedString = false; // Holds flag if position is quoted string or not
char lastChar = (char)0;
int closingCharIndex = -1;
int nestedStartingCharCounter = 0;
for(int i=1;i<m_SourceString.Length;i++){
// Skip escaped(\) "
if(lastChar != '\\' && m_SourceString[i] == '\"'){
// Start/end quoted string area
inQuotedString = !inQuotedString;
}
// We need to skip parenthesis in quoted string
else if(!inQuotedString){
// There is nested parenthesis
if(m_SourceString[i] == startingChar){
nestedStartingCharCounter++;
}
// Closing char
else if(m_SourceString[i] == closingChar){
// There isn't nested parenthesis closing chars left, this is closing char what we want
if(nestedStartingCharCounter == 0){
closingCharIndex = i;
break;
}
// This is nested parenthesis closing char
else{
nestedStartingCharCounter--;
}
}
}
lastChar = m_SourceString[i];
}
if(closingCharIndex == -1){
throw new Exception("There is no closing parenthesize for '" + m_SourceString + "' !");
}
else{
string retVal = m_SourceString.Substring(1,closingCharIndex - 1);
m_SourceString = m_SourceString.Substring(closingCharIndex + 1);
return retVal;
}
}
#endregion
// SYNC JAVA
#region method ReadToEnd
/// <summary>
/// Reads all remaining string, returns null if no chars left to read.
/// </summary>
/// <returns></returns>
public string ReadToEnd()
{
if(this.Available == 0){
return null;
}
string retVal = m_SourceString;
m_SourceString = "";
return retVal;
}
#endregion
#region method StartsWith
/// <summary>
/// Gets if source string starts with specified value. Compare is case-sensitive.
/// </summary>
/// <param name="value">Start string value.</param>
/// <returns></returns>
public bool StartsWith(string value)
{
return m_SourceString.StartsWith(value);
}
/// <summary>
/// Gets if source string starts with specified value.
/// </summary>
/// <param name="value">Start string value.</param>
/// <param name="case_sensitive">Specifies if compare is case-sensitive.</param>
/// <returns></returns>
public bool StartsWith(string value,bool case_sensitive)
{
if(case_sensitive){
return m_SourceString.StartsWith(value);
}
else{
return m_SourceString.ToLower().StartsWith(value.ToLower());
}
}
#endregion
#region Properties Implementation
/// <summary>
/// Gets how many chars are available for reading.
/// </summary>
public long Available
{
get{ return m_SourceString.Length; }
}
/// <summary>
/// Gets original string passed to class constructor.
/// </summary>
public string OriginalString
{
get{ return m_OriginalString; }
}
/// <summary>
/// Gets currently remaining string.
/// </summary>
public string SourceString
{
get{ return m_SourceString; }
}
/// <summary>
/// Gets position in original string.
/// </summary>
public int Position
{
get{ return m_OriginalString.Length - m_SourceString.Length; }
}
#endregion
}
}