2009-04-19 18:07:22 +00:00
#region = = = = = = = = = = = = = = = = = = Copyright ( c ) 2007 Pascal vd Heiden
/ *
* Copyright ( c ) 2007 Pascal vd Heiden , www . codeimp . com
* This program is released under GNU General Public License
*
* This program 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 .
*
* /
#endregion
#region = = = = = = = = = = = = = = = = = = CFG file structure syntax
/ *
' = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
' CONFIGURATION FILE STRUCTURE SYNTAX
' = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
'
' Whitepace is always allowed . This includes spaces , tabs
' linefeeds ( 10 ) and carriage returns ( 13 )
'
' Keys may not have spaces or assignment operator = in them .
'
' Comments start with // (unless used within strings)
' and count as comment for the rest of the line . Or use /* and */ / *
' to mark the beginning and end of a comment .
'
' Simple setting :
'
' key = value ;
'
' Example : speed = 345 ;
' cars = 8 ;
'
' Strings must be in quotes .
'
' Example : nickname = "Gherkin" ;
' altnick = "Gherk inn" ;
'
' String Escape Sequences :
' \ n New line ( 10 )
' \ r Carriage return ( 13 )
' \ t Tab ( 9 )
' \ " Double-quotation mark
' \ \ Backslash
' \ 000 Any ASCII character ( MUST be 3 digits ! So for 13 you use \ 013 )
'
' Decimals ALWAYS use a dot , NEVER comma !
'
' Example : pressure = 15.29 ;
' acceleration = 1.0023 ;
'
' true , false and null are valid keywords .
' null values can be left out .
'
' In this example , both items are null .
' Example : myitem = null ;
' myotheritem ;
'
' Structures must use brackets .
'
' Structure Example :
'
' key
' {
' key = value ;
' key = value ;
'
' key
' {
' key = value ;
' key = value ;
' key = value ;
' }
'
' key = value ;
' key = value ;
' key = value ;
' key = value ;
' key = value ;
' }
'
' As you can see , structures inside structures are allowed
' and you may go as deep as you want . Note that only the root structures
' can be readed from config using ReadSetting. ReadSetting will return a
' Dictionary object containing everything in that root structure .
'
' Key names must be unique within their scope .
'
' This is NOT allowed , it may not have ' father ' more
' than once in the same scope :
'
' mother = 45 ;
' father = 52 ;
'
' father
' {
' length = 1.87 ;
' }
'
' This however is allowed , because father
' now exists in a different scope :
'
' mother = 45 ;
' father = 52 ;
'
' parents
' {
' father = 52 ;
' }
'
' This too is allowed , both ' age ' are in a different scope :
'
' mother
' {
' age = 45 ;
' }
'
' father
' {
' age = 52 ;
' }
* /
#endregion
#region = = = = = = = = = = = = = = = = = = Namespaces
using System ;
2014-02-18 14:04:14 +00:00
using System.Collections.Generic ;
2009-04-19 18:07:22 +00:00
using System.IO ;
using System.Text ;
using System.Globalization ;
using System.Collections ;
using System.Collections.Specialized ;
#endregion
namespace CodeImp.DoomBuilder.IO
{
public sealed class Configuration
{
#region = = = = = = = = = = = = = = = = = = Constants
// Path seperator
public const string DEFAULT_SEPERATOR = "." ;
// Error strings
private const string ERROR_KEYMISSING = "Missing key name in assignment or scope." ;
private const string ERROR_KEYSPACES = "Spaces not allowed in key names." ;
private const string ERROR_ASSIGNINVALID = "Invalid assignment. Missing a previous terminator symbol?" ;
private const string ERROR_VALUEINVALID = "Invalid value in assignment. Missing a previous terminator symbol?" ;
private const string ERROR_VALUETOOBIG = "Value too big." ;
2013-07-19 15:30:58 +00:00
//private const string ERROR_KEYNOTUNQIUE = "Key is not unique within scope.";
2009-04-19 18:07:22 +00:00
private const string ERROR_KEYWORDUNKNOWN = "Unknown keyword in assignment. Missing a previous terminator symbol?" ;
2009-07-28 13:40:30 +00:00
private const string ERROR_UNEXPECTED_END = "Unexpected end of data. Missing a previous terminator symbol?" ;
private const string ERROR_UNKNOWN_FUNCTION = "Unknown function call." ;
private const string ERROR_INVALID_ARGS = "Invalid function arguments." ;
private const string ERROR_INCLUDE_UNSUPPORTED = "Include function is not supported in data parsed from stream." ;
2013-12-18 09:11:04 +00:00
public const string NUMBERS = "0123456789" ;
public const string NUMBERS2 = "0123456789-.&" ;
2009-04-19 18:07:22 +00:00
#endregion
#region = = = = = = = = = = = = = = = = = = Variables
// Error result
2014-02-18 14:04:14 +00:00
private bool cpErrorResult ;
2009-04-19 18:07:22 +00:00
private string cpErrorDescription = "" ;
2014-02-18 14:04:14 +00:00
private int cpErrorLine ;
2009-07-28 13:40:30 +00:00
private string cpErrorFile = "" ;
2014-02-18 14:04:14 +00:00
private static readonly char [ ] space = new [ ] { ' ' } ; //mxd
private static readonly char [ ] newline = new [ ] { '\n' } ; //mxd
2009-04-19 18:07:22 +00:00
// Configuration root
2014-02-18 14:04:14 +00:00
private IDictionary root ;
//mxd. Cache
2014-02-26 14:11:06 +00:00
private static Dictionary < string , IDictionary > cfgcache = new Dictionary < string , IDictionary > ( StringComparer . Ordinal ) ;
2009-04-19 18:07:22 +00:00
#endregion
#region = = = = = = = = = = = = = = = = = = Properties
// Properties
2009-07-28 13:40:30 +00:00
public bool ErrorResult { get { return cpErrorResult ; } }
2009-04-19 18:07:22 +00:00
public string ErrorDescription { get { return cpErrorDescription ; } }
public int ErrorLine { get { return cpErrorLine ; } }
2009-07-28 13:40:30 +00:00
public string ErrorFile { get { return cpErrorFile ; } }
2009-04-19 18:07:22 +00:00
public IDictionary Root { get { return root ; } set { root = value ; } }
public bool Sorted { get { return ( root is ListDictionary ) ; } }
#endregion
#region = = = = = = = = = = = = = = = = = = Constructor / Destructor
// Constructor
public Configuration ( )
{
// Standard new configuration
NewConfiguration ( ) ;
// We have no destructor
GC . SuppressFinalize ( this ) ;
}
// Constructor
public Configuration ( bool sorted )
{
// Standard new configuration
NewConfiguration ( sorted ) ;
// We have no destructor
GC . SuppressFinalize ( this ) ;
}
// Constructor to load a file immediately
public Configuration ( string filename )
{
// Load configuration from file
LoadConfiguration ( filename ) ;
// We have no destructor
GC . SuppressFinalize ( this ) ;
}
// Constructor to load a file immediately
public Configuration ( string filename , bool sorted )
{
// Load configuration from file
LoadConfiguration ( filename , sorted ) ;
// We have no destructor
GC . SuppressFinalize ( this ) ;
}
#endregion
#region = = = = = = = = = = = = = = = = = = Private Methods
2009-07-28 13:40:30 +00:00
// This merges two structures
private static IDictionary Combine ( IDictionary d1 , IDictionary d2 , bool sorted )
{
// Create new dictionary
IDictionary result ;
if ( sorted ) result = new ListDictionary ( ) ; else result = new Hashtable ( ) ;
// Copy all items from d1 to result
IDictionaryEnumerator d1e = d1 . GetEnumerator ( ) ;
while ( d1e . MoveNext ( ) ) result . Add ( d1e . Key , d1e . Value ) ;
// Go for all items in d2
IDictionaryEnumerator d2e = d2 . GetEnumerator ( ) ;
while ( d2e . MoveNext ( ) )
{
// Check if this is another Hashtable
if ( d2e . Value is IDictionary )
{
// Check if already in result
2009-07-31 12:54:39 +00:00
if ( result . Contains ( d2e . Key ) & & ( result [ d2e . Key ] is IDictionary ) )
2009-07-28 13:40:30 +00:00
{
// Modify result
result [ d2e . Key ] = Combine ( ( IDictionary ) result [ d2e . Key ] , ( IDictionary ) d2e . Value , sorted ) ;
}
else
{
// Copy from d2
if ( sorted )
{
// Sorted combine
2009-07-31 12:54:39 +00:00
result [ d2e . Key ] = Combine ( new ListDictionary ( ) , ( IDictionary ) d2e . Value , sorted ) ;
2009-07-28 13:40:30 +00:00
}
else
{
// Unsorted combine
2009-07-31 12:54:39 +00:00
result [ d2e . Key ] = Combine ( new Hashtable ( ) , ( IDictionary ) d2e . Value , sorted ) ;
2009-07-28 13:40:30 +00:00
}
}
}
else
{
// Check if also in d1
2009-07-31 12:54:39 +00:00
if ( result . Contains ( d2e . Key ) )
2009-07-28 13:40:30 +00:00
{
// Modify result
result [ d2e . Key ] = d2e . Value ;
}
else
{
// Copy
result . Add ( d2e . Key , d2e . Value ) ;
}
}
}
// Return result
return result ;
}
2009-04-19 18:07:22 +00:00
// This is called by all the ReadSetting overloads to perform the read
2009-07-30 18:10:57 +00:00
private bool CheckSetting ( IDictionary dic , string setting , string pathseperator )
2009-04-19 18:07:22 +00:00
{
2013-12-13 09:31:18 +00:00
IDictionary cs ;
2009-04-19 18:07:22 +00:00
// Split the path in an array
string [ ] keys = setting . Split ( pathseperator . ToCharArray ( ) ) ;
// Get the root item
2009-07-30 18:10:57 +00:00
object item = dic ;
2009-04-19 18:07:22 +00:00
// Go for each item
for ( int i = 0 ; i < keys . Length ; i + + )
{
// Check if the current item is of ConfigStruct type
if ( item is IDictionary )
{
// Check if the key is valid
2014-02-18 14:04:14 +00:00
if ( ValidateKey ( keys [ i ] . Trim ( ) , "" , - 1 ) )
2009-04-19 18:07:22 +00:00
{
// Cast to ConfigStruct
cs = ( IDictionary ) item ;
2009-07-28 13:40:30 +00:00
2009-04-19 18:07:22 +00:00
// Check if the requested item exists
2013-12-13 09:31:18 +00:00
if ( cs . Contains ( keys [ i ] ) )
2009-04-19 18:07:22 +00:00
{
// Set the item to the next item
item = cs [ keys [ i ] ] ;
}
else
{
// Key not found
return false ;
}
}
else
{
// Invalid key in path
return false ;
}
}
else
{
// Unable to go any further
return false ;
}
}
// Return result
return true ;
}
// This is called by all the ReadSetting overloads to perform the read
2009-07-28 13:40:30 +00:00
private object ReadAnySetting ( string setting , object defaultsetting , string pathseperator ) { return ReadAnySetting ( root , setting , defaultsetting , pathseperator ) ; }
private object ReadAnySetting ( IDictionary dic , string setting , object defaultsetting , string pathseperator ) { return ReadAnySetting ( dic , "" , - 1 , setting , defaultsetting , pathseperator ) ; }
private object ReadAnySetting ( IDictionary dic , string file , int line , string setting , object defaultsetting , string pathseperator )
2009-04-19 18:07:22 +00:00
{
2013-12-13 09:31:18 +00:00
IDictionary cs ;
2009-04-19 18:07:22 +00:00
// Split the path in an array
string [ ] keys = setting . Split ( pathseperator . ToCharArray ( ) ) ;
// Get the root item
2009-07-28 13:40:30 +00:00
object item = dic ;
2009-04-19 18:07:22 +00:00
// Go for each item
for ( int i = 0 ; i < keys . Length ; i + + )
{
// Check if the current item is of ConfigStruct type
if ( item is IDictionary )
{
// Check if the key is valid
2014-02-18 14:04:14 +00:00
if ( ValidateKey ( keys [ i ] . Trim ( ) , file , line ) )
2009-04-19 18:07:22 +00:00
{
// Cast to ConfigStruct
cs = ( IDictionary ) item ;
// Check if the requested item exists
2013-12-13 09:31:18 +00:00
if ( cs . Contains ( keys [ i ] ) )
2009-04-19 18:07:22 +00:00
{
// Set the item to the next item
item = cs [ keys [ i ] ] ;
}
else
{
// Key not found
// return default setting
return defaultsetting ;
}
}
else
{
// Invalid key in path
// return default setting
return defaultsetting ;
}
}
else
{
// Unable to go any further
// return default setting
return defaultsetting ;
}
}
// Return the item
return item ;
}
// This returns a string added with escape characters
2014-03-05 09:21:28 +00:00
private static string EscapedString ( string str )
2009-04-19 18:07:22 +00:00
{
// Replace the \ with \\ first!
str = str . Replace ( "\\" , "\\\\" ) ;
str = str . Replace ( "\n" , "\\n" ) ;
str = str . Replace ( "\r" , "\\r" ) ;
str = str . Replace ( "\t" , "\\t" ) ;
str = str . Replace ( "\"" , "\\\"" ) ;
// Return result
return str ;
}
// This raises an error
2009-07-28 13:40:30 +00:00
private void RaiseError ( string file , int line , string description )
2009-04-19 18:07:22 +00:00
{
// Raise error
2009-07-28 13:40:30 +00:00
if ( ! cpErrorResult )
{
cpErrorResult = true ;
cpErrorDescription = description ;
cpErrorLine = line ;
cpErrorFile = file ;
}
2009-04-19 18:07:22 +00:00
}
// This validates a given key and sets
// error properties if key is invalid and errorline > -1
2014-02-18 14:04:14 +00:00
private bool ValidateKey ( string key , string file , int errorline )
2009-04-19 18:07:22 +00:00
{
bool validateresult ;
// Check if key is an empty string
if ( key = = "" )
{
// ERROR: Missing key name in statement
2009-07-28 13:40:30 +00:00
if ( errorline > - 1 ) RaiseError ( file , errorline , ERROR_KEYMISSING ) ;
2009-04-19 18:07:22 +00:00
validateresult = false ;
}
else
{
// Check if there are spaces in the key
2013-12-13 09:31:18 +00:00
if ( key . IndexOfAny ( space ) > - 1 )
2009-04-19 18:07:22 +00:00
{
// ERROR: Spaces not allowed in key names
2009-07-28 13:40:30 +00:00
if ( errorline > - 1 ) RaiseError ( file , errorline , ERROR_KEYSPACES ) ;
2009-04-19 18:07:22 +00:00
validateresult = false ;
}
else
{
// Check if we can test existance
2013-12-18 09:11:04 +00:00
//if(container != null)
//{
2009-07-30 05:43:13 +00:00
/ *
2009-04-19 18:07:22 +00:00
// Test if the key exists in this container
if ( container . Contains ( key ) = = true )
{
// ERROR: Key is not unique within struct
2009-07-28 13:40:30 +00:00
if ( errorline > - 1 ) RaiseError ( file , errorline , ERROR_KEYNOTUNQIUE ) ;
2009-04-19 18:07:22 +00:00
validateresult = false ;
}
else
2009-07-30 05:43:13 +00:00
* /
2013-12-18 09:11:04 +00:00
//{
2009-04-19 18:07:22 +00:00
// Key OK
2013-12-18 09:11:04 +00:00
//validateresult = true;
//}
//}
//else
//{
2009-04-19 18:07:22 +00:00
// Key OK
validateresult = true ;
2013-12-18 09:11:04 +00:00
//}
2009-04-19 18:07:22 +00:00
}
}
// Return result
return validateresult ;
}
// This validates a given keyword and sets
// error properties if keyword is invalid and errorline > -1
2009-07-28 13:40:30 +00:00
private bool ValidateKeyword ( string keyword , string file , int errorline )
2009-04-19 18:07:22 +00:00
{
bool validateresult ;
// Check if key is an empty string
if ( keyword = = "" )
{
// ERROR: Missing key name in statement
2009-07-28 13:40:30 +00:00
if ( errorline > - 1 ) RaiseError ( file , errorline , ERROR_ASSIGNINVALID ) ;
2009-04-19 18:07:22 +00:00
validateresult = false ;
}
else
{
// Check if there are spaces in the key
2013-12-13 09:31:18 +00:00
if ( keyword . IndexOfAny ( space ) > - 1 )
2009-04-19 18:07:22 +00:00
{
// ERROR: Spaces not allowed in key names
2009-07-28 13:40:30 +00:00
if ( errorline > - 1 ) RaiseError ( file , errorline , ERROR_ASSIGNINVALID ) ;
2009-04-19 18:07:22 +00:00
validateresult = false ;
}
else
{
// Key OK
validateresult = true ;
}
}
// Return result
return validateresult ;
}
2009-07-28 13:40:30 +00:00
#endregion
2009-04-19 18:07:22 +00:00
2009-07-28 13:40:30 +00:00
#region = = = = = = = = = = = = = = = = = = Parsing
// This parses an assignment
private object ParseAssignment ( ref string file , ref string data , ref int pos , ref int line )
2009-04-19 18:07:22 +00:00
{
2009-07-28 13:40:30 +00:00
object val = null ;
2009-04-19 18:07:22 +00:00
2009-07-28 13:40:30 +00:00
while ( ( pos < data . Length ) & & ! cpErrorResult )
{
// Get current character
char c = data [ pos + + ] ;
// Check for string opening
if ( c = = '\"' )
{
// Now parsing a string
val = ParseString ( ref file , ref data , ref pos , ref line ) ;
if ( cpErrorResult ) return null ;
}
// Check for numeric character
2013-12-18 09:11:04 +00:00
else if ( NUMBERS2 . IndexOf ( c . ToString ( CultureInfo . InvariantCulture ) ) > - 1 )
2009-07-28 13:40:30 +00:00
{
// Go one byte back, because this
// byte is part of the number!
pos - - ;
// Now parsing a number
val = ParseNumber ( ref file , ref data , ref pos , ref line ) ;
if ( cpErrorResult ) return null ;
}
// Check for new line
else if ( c = = '\n' )
{
// Count the new line
line + + ;
}
// Check if assignment ends
else if ( c = = ';' )
{
// End of assignment
return val ;
}
// Otherwise (if not whitespace) it is a keyword
else if ( ( c ! = ' ' ) & & ( c ! = '\t' ) )
{
// Go one byte back, because this
// byte is part of the keyword!
pos - - ;
// Now parsing a keyword
val = ParseKeyword ( ref file , ref data , ref pos , ref line ) ;
if ( cpErrorResult ) return null ;
}
}
2009-04-19 18:07:22 +00:00
2009-07-28 13:40:30 +00:00
RaiseError ( file , line , ERROR_UNEXPECTED_END ) ;
return null ;
}
// This parses a string
private string ParseString ( ref string file , ref string data , ref int pos , ref int line )
{
string val = "" ;
// In escape sequence?
bool escape = false ;
while ( ( pos < data . Length ) & & ! cpErrorResult )
2009-04-19 18:07:22 +00:00
{
// Get current character
2009-07-28 13:40:30 +00:00
char c = data [ pos + + ] ;
2009-04-19 18:07:22 +00:00
2009-07-28 13:40:30 +00:00
// Check if in an escape sequence
if ( escape )
2009-04-19 18:07:22 +00:00
{
2009-07-28 13:40:30 +00:00
// What character?
2009-04-19 18:07:22 +00:00
switch ( c )
{
2009-07-28 13:40:30 +00:00
case '\\' : val + = "\\" ; break ;
case 'n' : val + = "\n" ; break ;
case '\"' : val + = "\"" ; break ;
case 'r' : val + = "\r" ; break ;
case 't' : val + = "\t" ; break ;
default :
// Is it a number?
2013-12-18 09:11:04 +00:00
if ( NUMBERS . IndexOf ( c . ToString ( CultureInfo . InvariantCulture ) ) > - 1 )
2009-04-19 18:07:22 +00:00
{
2014-02-21 14:42:12 +00:00
int vv ;
char vc ;
2009-07-28 13:40:30 +00:00
// Convert the next 3 characters to a number
string v = data . Substring ( pos , 3 ) ;
2014-02-18 14:04:14 +00:00
try { vv = Convert . ToInt32 ( v . Trim ( ) , CultureInfo . InvariantCulture ) ; }
catch ( FormatException )
2009-04-19 18:07:22 +00:00
{
2009-07-28 13:40:30 +00:00
// ERROR: Invalid value in assignment
RaiseError ( file , line , ERROR_VALUEINVALID ) ;
return null ;
2009-04-19 18:07:22 +00:00
}
2009-07-28 13:40:30 +00:00
// Convert the number to a char
2014-02-18 14:04:14 +00:00
try { vc = Convert . ToChar ( vv , CultureInfo . InvariantCulture ) ; }
catch ( FormatException )
2009-04-19 18:07:22 +00:00
{
2009-07-28 13:40:30 +00:00
// ERROR: Invalid value in assignment
RaiseError ( file , line , ERROR_VALUEINVALID ) ;
return null ;
2009-04-19 18:07:22 +00:00
}
2009-07-28 13:40:30 +00:00
// Add the char
val + = vc . ToString ( CultureInfo . InvariantCulture ) ;
2009-04-19 18:07:22 +00:00
}
2009-07-28 13:40:30 +00:00
else
2009-04-19 18:07:22 +00:00
{
2009-07-28 13:40:30 +00:00
// Add the character as it is
val + = c . ToString ( CultureInfo . InvariantCulture ) ;
2009-04-19 18:07:22 +00:00
}
2009-07-28 13:40:30 +00:00
2009-04-19 18:07:22 +00:00
// Leave switch
break ;
}
2009-07-28 13:40:30 +00:00
// End of escape sequence
escape = false ;
2009-04-19 18:07:22 +00:00
}
2009-07-28 13:40:30 +00:00
else
2009-04-19 18:07:22 +00:00
{
2013-12-18 09:11:04 +00:00
switch ( c ) { //mxd
// Check for sequence start
case '\\' :
// Next character is of escape sequence
escape = true ;
break ;
// Check if string ends
case '\"' :
return val ;
// Check for new line
case '\n' :
// Count the new line
line + + ;
break ;
// Everything else is just part of string
default :
// Add to value
val + = c . ToString ( CultureInfo . InvariantCulture ) ;
break ;
2009-04-19 18:07:22 +00:00
}
2009-07-28 13:40:30 +00:00
}
}
RaiseError ( file , line , ERROR_UNEXPECTED_END ) ;
return null ;
}
// Parsing a number
private object ParseNumber ( ref string file , ref string data , ref int pos , ref int line )
{
string val = "" ;
while ( ( pos < data . Length ) & & ! cpErrorResult )
{
// Get current character
char c = data [ pos + + ] ;
// Check if number ends here
if ( ( c = = ';' ) | | ( c = = ',' ) | | ( c = = ')' ) )
{
// One byte back, because this
// is also the end of the assignment
pos - - ;
// Floating point?
if ( val . IndexOf ( "f" ) > - 1 )
2009-04-19 18:07:22 +00:00
{
2014-02-21 14:42:12 +00:00
float fval ;
2009-04-19 18:07:22 +00:00
2009-07-28 13:40:30 +00:00
// Convert to float (remove the f first)
2014-02-18 14:04:14 +00:00
try { fval = Convert . ToSingle ( val . Trim ( ) . Replace ( "f" , "" ) , CultureInfo . InvariantCulture ) ; }
catch ( FormatException )
2009-07-28 13:40:30 +00:00
{
// ERROR: Invalid value in assignment
RaiseError ( file , line , ERROR_VALUEINVALID ) ;
return null ;
}
return fval ;
2009-04-19 18:07:22 +00:00
}
2009-07-28 13:40:30 +00:00
else
2009-04-19 18:07:22 +00:00
{
2009-07-28 13:40:30 +00:00
// Convert to int
try
2009-04-19 18:07:22 +00:00
{
2009-07-28 13:40:30 +00:00
// Convert to value
2014-02-18 14:04:14 +00:00
int ival = Convert . ToInt32 ( val . Trim ( ) , CultureInfo . InvariantCulture ) ;
2009-07-28 13:40:30 +00:00
return ival ;
2009-04-19 18:07:22 +00:00
}
2014-02-18 14:04:14 +00:00
catch ( OverflowException )
2009-04-19 18:07:22 +00:00
{
2009-07-28 13:40:30 +00:00
// Too large for Int32, try Int64
2009-04-19 18:07:22 +00:00
try
{
// Convert to value
2014-02-18 14:04:14 +00:00
long lval = Convert . ToInt64 ( val . Trim ( ) , CultureInfo . InvariantCulture ) ;
2009-07-28 13:40:30 +00:00
return lval ;
2009-04-19 18:07:22 +00:00
}
2014-02-18 14:04:14 +00:00
catch ( OverflowException )
2009-04-19 18:07:22 +00:00
{
2009-07-28 13:40:30 +00:00
// Too large for Int64, return error
RaiseError ( file , line , ERROR_VALUETOOBIG ) ;
return null ;
2009-04-19 18:07:22 +00:00
}
2014-02-18 14:04:14 +00:00
catch ( FormatException )
2009-07-28 13:40:30 +00:00
{
2009-04-19 18:07:22 +00:00
// ERROR: Invalid value in assignment
2009-07-28 13:40:30 +00:00
RaiseError ( file , line , ERROR_VALUEINVALID ) ;
return null ;
2009-04-19 18:07:22 +00:00
}
}
2014-02-18 14:04:14 +00:00
catch ( FormatException )
2009-07-28 13:40:30 +00:00
{
// ERROR: Invalid value in assignment
RaiseError ( file , line , ERROR_VALUEINVALID ) ;
return null ;
}
2009-04-19 18:07:22 +00:00
}
}
2009-07-28 13:40:30 +00:00
// Check for new line
else if ( c = = '\n' )
{
// Count the new line
line + + ;
}
// Everything else is part of the value
else
2009-04-19 18:07:22 +00:00
{
2009-07-28 13:40:30 +00:00
val + = c . ToString ( CultureInfo . InvariantCulture ) ;
}
}
RaiseError ( file , line , ERROR_UNEXPECTED_END ) ;
return null ;
}
// Parsing a keyword
private object ParseKeyword ( ref string file , ref string data , ref int pos , ref int line )
{
string val = "" ;
while ( ( pos < data . Length ) & & ! cpErrorResult )
{
// Get current character
char c = data [ pos + + ] ;
// Check if keyword ends
if ( ( c = = ';' ) | | ( c = = ',' ) | | ( c = = ')' ) )
{
// One byte back, because this
// is also the end of the assignment
pos - - ;
// Validate the keyword
if ( ValidateKeyword ( val . Trim ( ) , file , line ) )
2009-04-19 18:07:22 +00:00
{
2009-07-28 13:40:30 +00:00
// Return result depending on the keyword
switch ( val . Trim ( ) . ToLowerInvariant ( ) )
2009-04-19 18:07:22 +00:00
{
2013-12-13 09:31:18 +00:00
case "true" : return true ;
case "false" : return false ;
2009-07-28 13:40:30 +00:00
case "null" : return null ;
2012-09-26 00:04:17 +00:00
default : RaiseError ( file , line , ERROR_KEYWORDUNKNOWN + "\nUnrecognized token: '" + val . Trim ( ) . ToLowerInvariant ( ) + "'" ) ; return null ;
2009-04-19 18:07:22 +00:00
}
}
2009-07-28 13:40:30 +00:00
}
// Check for new line
else if ( c = = '\n' )
{
// Count the new line
line + + ;
}
// Everything else is just part of keyword
else
{
// Add to value
val + = c . ToString ( CultureInfo . InvariantCulture ) ;
}
}
RaiseError ( file , line , ERROR_UNEXPECTED_END ) ;
return null ;
}
// This includes another file
2015-05-31 21:11:21 +00:00
private void FunctionInclude ( IDictionary cs , List < object > args , ref string file , int line )
2009-07-28 13:40:30 +00:00
{
string data ;
if ( string . IsNullOrEmpty ( file ) ) RaiseError ( file , line , ERROR_INCLUDE_UNSUPPORTED ) ;
if ( args . Count < 1 ) RaiseError ( file , line , ERROR_INVALID_ARGS ) ;
if ( ! ( args [ 0 ] is string ) ) RaiseError ( file , line , ERROR_INVALID_ARGS + " Expected a string for argument 1." ) ;
if ( ( args . Count > 1 ) & & ! ( args [ 1 ] is string ) ) RaiseError ( file , line , ERROR_INVALID_ARGS + " Expected a string for argument 2." ) ;
2014-12-22 21:36:49 +00:00
if ( args [ 0 ] . ToString ( ) . ToUpperInvariant ( ) = = Path . GetFileName ( file ) . ToUpperInvariant ( ) ) RaiseError ( file , line , " A file cannot call include() on itself." ) ; //mxd
2009-07-28 13:40:30 +00:00
if ( cpErrorResult ) return ;
// Determine the full path of the file to include
2014-02-18 14:04:14 +00:00
string includefile = Path . GetDirectoryName ( file ) + Path . DirectorySeparatorChar + args [ 0 ] ;
//mxd. Caching
2014-12-03 23:15:26 +00:00
if ( cfgcache . ContainsKey ( includefile ) )
{
2014-02-18 14:04:14 +00:00
IDictionary cinc = cfgcache [ includefile ] ;
// Check if a path is given
2014-12-03 23:15:26 +00:00
if ( ( args . Count > 1 ) & & ! string . IsNullOrEmpty ( args [ 1 ] . ToString ( ) ) )
{
2014-02-18 14:04:14 +00:00
IDictionary def ;
if ( cs is ListDictionary ) def = new ListDictionary ( ) ; else def = new Hashtable ( ) ;
2014-12-03 23:15:26 +00:00
if ( CheckSetting ( cinc , args [ 1 ] . ToString ( ) , DEFAULT_SEPERATOR ) )
{
2014-02-18 14:04:14 +00:00
cinc = ( IDictionary ) ReadAnySetting ( cinc , file , line , args [ 1 ] . ToString ( ) , def , DEFAULT_SEPERATOR ) ;
2014-12-03 23:15:26 +00:00
}
else
{
2014-02-18 14:04:14 +00:00
RaiseError ( file , line , "Include missing structure '" + args [ 1 ] + "' in file '" + includefile + "'" ) ;
return ;
}
}
// Recursively merge the structures with the current structure
IDictionary newcs = Combine ( cs , cinc , ( cs is ListDictionary ) ) ;
cs . Clear ( ) ;
foreach ( DictionaryEntry de in newcs ) cs . Add ( de . Key , de . Value ) ;
return ;
}
2009-07-28 13:40:30 +00:00
try
{
// Load the file contents
FileStream fstream = File . OpenRead ( includefile ) ;
byte [ ] fbuffer = new byte [ fstream . Length ] ;
fstream . Read ( fbuffer , 0 , fbuffer . Length ) ;
fstream . Close ( ) ;
// Convert byte array to string
data = Encoding . UTF8 . GetString ( fbuffer ) ;
}
catch ( Exception e )
{
RaiseError ( file , line , "Unable to include file '" + includefile + "'. " + e . GetType ( ) . Name + ": " + e . Message ) ;
return ;
}
// Remove returns and tabs because the
// parser only uses newline for new lines.
data = data . Replace ( "\r" , "" ) ;
data = data . Replace ( "\t" , "" ) ;
// Parse the data
IDictionary inc ;
if ( cs is ListDictionary ) inc = new ListDictionary ( ) ; else inc = new Hashtable ( ) ;
int npos = 0 , nline = 1 ;
InputStructure ( inc , ref includefile , ref data , ref npos , ref nline ) ;
if ( ! cpErrorResult )
{
2014-02-18 14:04:14 +00:00
//mxd. Add to cache
cfgcache . Add ( includefile , inc ) ;
2009-07-28 13:40:30 +00:00
// Check if a path is given
if ( ( args . Count > 1 ) & & ! string . IsNullOrEmpty ( args [ 1 ] . ToString ( ) ) )
{
IDictionary def ;
if ( cs is ListDictionary ) def = new ListDictionary ( ) ; else def = new Hashtable ( ) ;
2009-07-30 18:10:57 +00:00
if ( CheckSetting ( inc , args [ 1 ] . ToString ( ) , DEFAULT_SEPERATOR ) )
{
inc = ( IDictionary ) ReadAnySetting ( inc , file , line , args [ 1 ] . ToString ( ) , def , DEFAULT_SEPERATOR ) ;
}
else
{
2013-12-20 09:24:43 +00:00
RaiseError ( file , line , "Include missing structure '" + args [ 1 ] + "' in file '" + includefile + "'" ) ;
2009-07-30 18:10:57 +00:00
return ;
}
2009-07-28 13:40:30 +00:00
}
// Recursively merge the structures with the current structure
IDictionary newcs = Combine ( cs , inc , ( cs is ListDictionary ) ) ;
cs . Clear ( ) ;
foreach ( DictionaryEntry de in newcs ) cs . Add ( de . Key , de . Value ) ;
}
}
// This parses a function
private void ParseFunction ( IDictionary cs , ref string file , ref string data , ref int pos , ref int line , ref string functionname )
{
// We now parse arguments, separated by commas, until we reach the end of the function
2015-05-31 21:11:21 +00:00
List < object > args = new List < object > ( ) ;
2014-02-18 14:04:14 +00:00
object val ;
2009-07-28 13:40:30 +00:00
while ( ( pos < data . Length ) & & ! cpErrorResult )
{
// Get current character
char c = data [ pos + + ] ;
// Check for end of function
if ( c = = ')' )
{
// Check what function to run
switch ( functionname . Trim ( ) . ToLowerInvariant ( ) )
2009-04-19 18:07:22 +00:00
{
2009-07-28 13:40:30 +00:00
// Include another file in here
case "include" :
FunctionInclude ( cs , args , ref file , line ) ;
return ;
2009-04-19 18:07:22 +00:00
2009-07-28 13:40:30 +00:00
default :
RaiseError ( file , line , ERROR_UNKNOWN_FUNCTION ) ;
return ;
}
}
// Check for string opening
else if ( c = = '\"' )
{
// Now parsing a string
val = ParseString ( ref file , ref data , ref pos , ref line ) ;
if ( cpErrorResult ) return ;
args . Add ( val ) ;
}
// Check for numeric character
2013-12-18 09:11:04 +00:00
else if ( NUMBERS2 . IndexOf ( c . ToString ( CultureInfo . InvariantCulture ) ) > - 1 )
2009-07-28 13:40:30 +00:00
{
// Go one byte back, because this
// byte is part of the number!
pos - - ;
// Now parsing a number
val = ParseNumber ( ref file , ref data , ref pos , ref line ) ;
if ( cpErrorResult ) return ;
args . Add ( val ) ;
}
// Check for new line
else if ( c = = '\n' )
{
// Count the new line
line + + ;
}
// Check if argument ends
else if ( c = = ',' )
{
// End of argument
}
// Otherwise (if not whitespace) it is a keyword
else if ( ( c ! = ' ' ) & & ( c ! = '\t' ) )
{
// Go one byte back, because this
// byte is part of the keyword!
pos - - ;
// Now parsing a keyword
val = ParseKeyword ( ref file , ref data , ref pos , ref line ) ;
if ( cpErrorResult ) return ;
args . Add ( val ) ;
}
}
RaiseError ( file , line , ERROR_UNEXPECTED_END ) ;
}
// This parses a structure in the given data starting
// from the given pos and line and updates pos and line.
private void InputStructure ( IDictionary cs , ref string file , ref string data , ref int pos , ref int line )
{
string key = "" ;
// Go through all of the data until
// the end or until the struct closes
// or when an arror occurred
while ( ( pos < data . Length ) & & ! cpErrorResult )
{
// Get current character
char c = data [ pos + + ] ;
// Check what character this is
switch ( c )
{
case '{' : // Begin of new struct
// Validate key
2014-02-18 14:04:14 +00:00
if ( ValidateKey ( key . Trim ( ) , file , line ) )
2009-07-28 13:40:30 +00:00
{
// Parse this struct and add it
IDictionary cs2 ;
if ( cs is ListDictionary ) cs2 = new ListDictionary ( ) ; else cs2 = new Hashtable ( ) ;
InputStructure ( cs2 , ref file , ref data , ref pos , ref line ) ;
2009-07-31 12:54:39 +00:00
if ( cs . Contains ( key . Trim ( ) ) & & ( cs [ key . Trim ( ) ] is IDictionary ) )
cs [ key . Trim ( ) ] = Combine ( ( IDictionary ) cs [ key . Trim ( ) ] , cs2 , ( cs is ListDictionary ) ) ;
2009-07-30 05:43:13 +00:00
else
2009-07-31 12:54:39 +00:00
cs [ key . Trim ( ) ] = cs2 ;
2009-04-19 18:07:22 +00:00
key = "" ;
}
2009-07-28 13:40:30 +00:00
break ;
case '}' : // End of this struct
// Stop parsing in this struct
return ;
case '(' : // Function
ParseFunction ( cs , ref file , ref data , ref pos , ref line , ref key ) ;
key = "" ;
break ;
case '=' : // Assignment
// Validate key
2014-02-18 14:04:14 +00:00
if ( ValidateKey ( key . Trim ( ) , file , line ) )
2009-04-19 18:07:22 +00:00
{
2009-07-28 13:40:30 +00:00
// Now parsing assignment
object val = ParseAssignment ( ref file , ref data , ref pos , ref line ) ;
if ( ! cpErrorResult )
{
2009-07-30 05:43:13 +00:00
cs [ key . Trim ( ) ] = val ;
2009-07-28 13:40:30 +00:00
key = "" ;
}
2009-04-19 18:07:22 +00:00
}
2009-07-28 13:40:30 +00:00
break ;
case ';' : // Terminator
// Validate key
if ( ! string . IsNullOrEmpty ( key ) )
2009-04-19 18:07:22 +00:00
{
2014-02-18 14:04:14 +00:00
if ( ValidateKey ( key . Trim ( ) , file , line ) )
2009-07-28 13:40:30 +00:00
{
// Add the key with null as value
2009-07-30 05:43:13 +00:00
cs [ key . Trim ( ) ] = null ;
2009-07-28 13:40:30 +00:00
key = "" ;
}
2009-04-19 18:07:22 +00:00
}
2009-07-28 13:40:30 +00:00
break ;
case '\n' : // New line
// Count the line
line + + ;
// Add this to the key as a space.
// Spaces are not allowed, but it will be trimmed
// when its the first or last character.
key + = " " ;
break ;
case '\\' : // Possible comment
case '/' :
// Backtrack to use previous character also
pos - - ;
// Check for the line comment //
if ( data . Substring ( pos , 2 ) = = "//" )
2009-04-19 18:07:22 +00:00
{
2009-07-28 13:40:30 +00:00
// Find the next line
int np = data . IndexOf ( "\n" , pos ) ;
// Next line found?
if ( np > - 1 )
2009-04-19 18:07:22 +00:00
{
2009-07-28 13:40:30 +00:00
// Count the line
line + + ;
// Skip everything on this line
pos = np + 1 ;
2009-04-19 18:07:22 +00:00
}
2009-07-28 13:40:30 +00:00
else
{
// No end of line
// Skip everything else
pos = data . Length + 1 ;
}
}
// Check for the block comment /* */
else if ( data . Substring ( pos , 2 ) = = "/*" )
{
// Find the next closing block comment
int np = data . IndexOf ( "*/" , pos ) ;
2009-04-19 18:07:22 +00:00
2009-07-28 13:40:30 +00:00
// Closing block comment found?
if ( np > - 1 )
{
// Count the lines in the block comment
string blockdata = data . Substring ( pos , np - pos + 2 ) ;
2013-12-17 08:19:40 +00:00
line + = ( blockdata . Split ( newline ) . Length - 1 ) ;
2009-07-28 13:40:30 +00:00
// Skip everything in this block
pos = np + 2 ;
}
else
{
// No end of line
// Skip everything else
pos = data . Length + 1 ;
}
2009-04-19 18:07:22 +00:00
}
2009-07-28 13:40:30 +00:00
else
{
// No whitespace
pos + + ;
}
break ;
default : // Everything else
// Add character to key
key + = c . ToString ( CultureInfo . InvariantCulture ) ;
break ;
2009-04-19 18:07:22 +00:00
}
}
}
2009-07-28 13:40:30 +00:00
#endregion
#region = = = = = = = = = = = = = = = = = = Writing
2009-04-19 18:07:22 +00:00
// This will create a data structure from the given object
2014-12-03 23:15:26 +00:00
private static string OutputStructure ( IDictionary cs , int level , string newline , bool whitespace )
2009-04-19 18:07:22 +00:00
{
string leveltabs = "" ;
string spacing = "" ;
StringBuilder db = new StringBuilder ( "" ) ;
// Check if this ConfigStruct is not empty
if ( cs . Count > 0 )
{
// Create whitespace
if ( whitespace )
{
for ( int i = 0 ; i < level ; i + + ) leveltabs + = "\t" ;
spacing = " " ;
}
// Get enumerator
IDictionaryEnumerator de = cs . GetEnumerator ( ) ;
// Go for each item
for ( int i = 0 ; i < cs . Count ; i + + )
{
// Go to next item
de . MoveNext ( ) ;
// Check if the value is null
if ( de . Value = = null )
{
// Output the keyword "null"
//db.Append(leveltabs); db.Append(de.Key.ToString()); db.Append(spacing);
//db.Append("="); db.Append(spacing); db.Append("null;"); db.Append(newline);
// Output key only
2014-02-21 14:42:12 +00:00
db . Append ( leveltabs ) ; db . Append ( de . Key ) ; db . Append ( ";" ) ; db . Append ( newline ) ;
2009-04-19 18:07:22 +00:00
}
// Check if the value if of ConfigStruct type
else if ( de . Value is IDictionary )
{
// Output recursive structure
if ( whitespace ) { db . Append ( leveltabs ) ; db . Append ( newline ) ; }
db . Append ( leveltabs ) ; db . Append ( de . Key ) ; db . Append ( newline ) ;
db . Append ( leveltabs ) ; db . Append ( "{" ) ; db . Append ( newline ) ;
db . Append ( OutputStructure ( ( IDictionary ) de . Value , level + 1 , newline , whitespace ) ) ;
db . Append ( leveltabs ) ; db . Append ( "}" ) ; db . Append ( newline ) ;
if ( whitespace ) { db . Append ( leveltabs ) ; db . Append ( newline ) ; }
}
// Check if the value is of boolean type
else if ( de . Value is bool )
{
// Check value
2013-12-13 09:31:18 +00:00
if ( ( bool ) de . Value )
2009-04-19 18:07:22 +00:00
{
// Output the keyword "true"
2014-02-21 14:42:12 +00:00
db . Append ( leveltabs ) ; db . Append ( de . Key ) ; db . Append ( spacing ) ;
2009-04-19 18:07:22 +00:00
db . Append ( "=" ) ; db . Append ( spacing ) ; db . Append ( "true;" ) ; db . Append ( newline ) ;
}
else
{
// Output the keyword "false"
2014-02-21 14:42:12 +00:00
db . Append ( leveltabs ) ; db . Append ( de . Key ) ; db . Append ( spacing ) ;
2009-04-19 18:07:22 +00:00
db . Append ( "=" ) ; db . Append ( spacing ) ; db . Append ( "false;" ) ; db . Append ( newline ) ;
}
}
// Check if value is of float type
else if ( de . Value is float )
{
// Output the value with a postfixed f
db . Append ( leveltabs ) ; db . Append ( de . Key ) ; db . Append ( spacing ) ; db . Append ( "=" ) ;
db . Append ( spacing ) ; db . Append ( String . Format ( CultureInfo . InvariantCulture , "{0}" , de . Value ) ) ; db . Append ( "f;" ) ; db . Append ( newline ) ;
}
// Check if value is of other numeric type
else if ( de . Value . GetType ( ) . IsPrimitive )
{
// Output the value unquoted
db . Append ( leveltabs ) ; db . Append ( de . Key ) ; db . Append ( spacing ) ; db . Append ( "=" ) ;
db . Append ( spacing ) ; db . Append ( String . Format ( CultureInfo . InvariantCulture , "{0}" , de . Value ) ) ; db . Append ( ";" ) ; db . Append ( newline ) ;
}
else
{
// Output the value with quotes and escape characters
db . Append ( leveltabs ) ; db . Append ( de . Key ) ; db . Append ( spacing ) ; db . Append ( "=" ) ;
db . Append ( spacing ) ; db . Append ( "\"" ) ; db . Append ( EscapedString ( de . Value . ToString ( ) ) ) ; db . Append ( "\";" ) ; db . Append ( newline ) ;
}
}
}
// Return the structure
return db . ToString ( ) ;
}
#endregion
#region = = = = = = = = = = = = = = = = = = Public Methods
// This clears the last error
public void ClearError ( )
{
// Clear error
2009-07-28 13:40:30 +00:00
cpErrorResult = false ;
2009-04-19 18:07:22 +00:00
cpErrorDescription = "" ;
cpErrorLine = 0 ;
2009-07-28 13:40:30 +00:00
cpErrorFile = "" ;
2009-04-19 18:07:22 +00:00
}
// This creates a new configuration
public void NewConfiguration ( ) { NewConfiguration ( false ) ; }
public void NewConfiguration ( bool sorted )
{
// Create new configuration
if ( sorted ) root = new ListDictionary ( ) ; else root = new Hashtable ( ) ;
}
// This checks if a given setting exists (disregards type)
2009-07-30 18:10:57 +00:00
public bool SettingExists ( string setting ) { return CheckSetting ( root , setting , DEFAULT_SEPERATOR ) ; }
public bool SettingExists ( string setting , string pathseperator ) { return CheckSetting ( root , setting , pathseperator ) ; }
2009-04-19 18:07:22 +00:00
// This can give a value of a key specified in a path form
// also, this does not error when the setting does not exist,
// but instead returns the given default value.
public string ReadSetting ( string setting , string defaultsetting ) { object r = ReadAnySetting ( setting , defaultsetting , DEFAULT_SEPERATOR ) ; if ( r ! = null ) return r . ToString ( ) ; else return null ; }
public string ReadSetting ( string setting , string defaultsetting , string pathseperator ) { object r = ReadAnySetting ( setting , defaultsetting , pathseperator ) ; if ( r ! = null ) return r . ToString ( ) ; else return null ; }
public int ReadSetting ( string setting , int defaultsetting ) { return Convert . ToInt32 ( ReadAnySetting ( setting , defaultsetting , DEFAULT_SEPERATOR ) , CultureInfo . InvariantCulture ) ; }
public int ReadSetting ( string setting , int defaultsetting , string pathseperator ) { return Convert . ToInt32 ( ReadAnySetting ( setting , defaultsetting , pathseperator ) , CultureInfo . InvariantCulture ) ; }
public float ReadSetting ( string setting , float defaultsetting ) { return Convert . ToSingle ( ReadAnySetting ( setting , defaultsetting , DEFAULT_SEPERATOR ) , CultureInfo . InvariantCulture ) ; }
public float ReadSetting ( string setting , float defaultsetting , string pathseperator ) { return Convert . ToSingle ( ReadAnySetting ( setting , defaultsetting , pathseperator ) , CultureInfo . InvariantCulture ) ; }
public short ReadSetting ( string setting , short defaultsetting ) { return Convert . ToInt16 ( ReadAnySetting ( setting , defaultsetting , DEFAULT_SEPERATOR ) , CultureInfo . InvariantCulture ) ; }
public short ReadSetting ( string setting , short defaultsetting , string pathseperator ) { return Convert . ToInt16 ( ReadAnySetting ( setting , defaultsetting , pathseperator ) , CultureInfo . InvariantCulture ) ; }
public long ReadSetting ( string setting , long defaultsetting ) { return Convert . ToInt64 ( ReadAnySetting ( setting , defaultsetting , DEFAULT_SEPERATOR ) , CultureInfo . InvariantCulture ) ; }
public long ReadSetting ( string setting , long defaultsetting , string pathseperator ) { return Convert . ToInt64 ( ReadAnySetting ( setting , defaultsetting , pathseperator ) , CultureInfo . InvariantCulture ) ; }
public bool ReadSetting ( string setting , bool defaultsetting ) { return Convert . ToBoolean ( ReadAnySetting ( setting , defaultsetting , DEFAULT_SEPERATOR ) , CultureInfo . InvariantCulture ) ; }
public bool ReadSetting ( string setting , bool defaultsetting , string pathseperator ) { return Convert . ToBoolean ( ReadAnySetting ( setting , defaultsetting , pathseperator ) , CultureInfo . InvariantCulture ) ; }
public byte ReadSetting ( string setting , byte defaultsetting ) { return Convert . ToByte ( ReadAnySetting ( setting , defaultsetting , DEFAULT_SEPERATOR ) , CultureInfo . InvariantCulture ) ; }
public byte ReadSetting ( string setting , byte defaultsetting , string pathseperator ) { return Convert . ToByte ( ReadAnySetting ( setting , defaultsetting , pathseperator ) , CultureInfo . InvariantCulture ) ; }
public IDictionary ReadSetting ( string setting , IDictionary defaultsetting ) { return ( IDictionary ) ReadAnySetting ( setting , defaultsetting , DEFAULT_SEPERATOR ) ; }
public IDictionary ReadSetting ( string setting , IDictionary defaultsetting , string pathseperator ) { return ( IDictionary ) ReadAnySetting ( setting , defaultsetting , pathseperator ) ; }
public object ReadSettingObject ( string setting , object defaultsetting ) { return ReadAnySetting ( setting , defaultsetting , DEFAULT_SEPERATOR ) ; }
// This writes a given setting to the configuration.
// Wont change existing structs, but will add them as needed.
// Returns true when written, false when failed.
public bool WriteSetting ( string setting , object settingvalue ) { return WriteSetting ( setting , settingvalue , DEFAULT_SEPERATOR ) ; }
public bool WriteSetting ( string setting , object settingvalue , string pathseperator )
{
2014-02-18 14:04:14 +00:00
IDictionary cs ;
2009-04-19 18:07:22 +00:00
// Split the path in an array
string [ ] keys = setting . Split ( pathseperator . ToCharArray ( ) ) ;
string finalkey = keys [ keys . Length - 1 ] ;
// Get the root item
object item = root ;
// Go for each path item
for ( int i = 0 ; i < ( keys . Length - 1 ) ; i + + )
{
// Check if the key is valid
2014-02-18 14:04:14 +00:00
if ( ValidateKey ( keys [ i ] . Trim ( ) , "" , - 1 ) )
2009-04-19 18:07:22 +00:00
{
// Cast to ConfigStruct
cs = ( IDictionary ) item ;
// Check if the requested item exists
2013-12-13 09:31:18 +00:00
if ( cs . Contains ( keys [ i ] ) )
2009-04-19 18:07:22 +00:00
{
// Check if the requested item is a ConfigStruct
if ( cs [ keys [ i ] ] is IDictionary )
{
// Set the item to the next item
item = cs [ keys [ i ] ] ;
}
else
{
// Cant proceed with path
return false ;
}
}
else
{
// Key not found
// Create it now
IDictionary ncs ;
if ( root is ListDictionary ) ncs = new ListDictionary ( ) ; else ncs = new Hashtable ( ) ;
cs . Add ( keys [ i ] , ncs ) ;
// Set the item to the next item
item = cs [ keys [ i ] ] ;
}
}
else
{
// Invalid key in path
return false ;
}
}
// Cast to ConfigStruct
cs = ( IDictionary ) item ;
// Check if the key already exists
2013-12-13 09:31:18 +00:00
if ( cs . Contains ( finalkey ) )
2009-04-19 18:07:22 +00:00
{
// Update the value
cs [ finalkey ] = settingvalue ;
}
else
{
// Create the key/value pair
cs . Add ( finalkey , settingvalue ) ;
}
// Return success
return true ;
}
// This removes a given setting from the configuration.
public bool DeleteSetting ( string setting ) { return DeleteSetting ( setting , DEFAULT_SEPERATOR ) ; }
public bool DeleteSetting ( string setting , string pathseperator )
{
2014-02-18 14:04:14 +00:00
IDictionary cs ;
2009-04-19 18:07:22 +00:00
// Split the path in an array
string [ ] keys = setting . Split ( pathseperator . ToCharArray ( ) ) ;
string finalkey = keys [ keys . Length - 1 ] ;
// Get the root item
object item = root ;
// Go for each path item
for ( int i = 0 ; i < ( keys . Length - 1 ) ; i + + )
{
// Check if the key is valid
2014-02-18 14:04:14 +00:00
if ( ValidateKey ( keys [ i ] . Trim ( ) , "" , - 1 ) )
2009-04-19 18:07:22 +00:00
{
// Cast to ConfigStruct
cs = ( IDictionary ) item ;
// Check if the requested item exists
2013-12-13 09:31:18 +00:00
if ( cs . Contains ( keys [ i ] ) )
2009-04-19 18:07:22 +00:00
{
// Check if the requested item is a ConfigStruct
if ( cs [ keys [ i ] ] is IDictionary )
{
// Set the item to the next item
item = cs [ keys [ i ] ] ;
}
else
{
// Cant proceed with path
return false ;
}
}
else
{
// Key not found
// Create it now
IDictionary ncs ;
if ( root is ListDictionary ) ncs = new ListDictionary ( ) ; else ncs = new Hashtable ( ) ;
cs . Add ( keys [ i ] , ncs ) ;
// Set the item to the next item
item = cs [ keys [ i ] ] ;
}
}
else
{
// Invalid key in path
return false ;
}
}
// Cast to ConfigStruct
cs = ( IDictionary ) item ;
// Arrived at our destination
// Delete the key if the key exists
2013-12-13 09:31:18 +00:00
if ( cs . Contains ( finalkey ) )
2009-04-19 18:07:22 +00:00
{
// Key exists, delete it
cs . Remove ( finalkey ) ;
// Return success
return true ;
}
else
{
// Key not found, return fail
return false ;
}
}
// This will save the current configuration to the specified file
public bool SaveConfiguration ( string filename ) { return SaveConfiguration ( filename , "\r\n" , true ) ; }
public bool SaveConfiguration ( string filename , string newline ) { return SaveConfiguration ( filename , newline , true ) ; }
public bool SaveConfiguration ( string filename , string newline , bool whitespace )
{
// Kill the file if it exists
2013-12-13 09:31:18 +00:00
if ( File . Exists ( filename ) ) File . Delete ( filename ) ;
2009-04-19 18:07:22 +00:00
// Open file stream for writing
FileStream fstream = File . OpenWrite ( filename ) ;
// Create output structure and write to file
string data = OutputConfiguration ( newline , whitespace ) ;
byte [ ] baData = Encoding . UTF8 . GetBytes ( data ) ;
fstream . Write ( baData , 0 , baData . Length ) ;
fstream . Flush ( ) ;
fstream . Close ( ) ;
// Return true when done, false when errors occurred
2009-07-28 13:40:30 +00:00
return ! cpErrorResult ;
2009-04-19 18:07:22 +00:00
}
// This will output the current configuration as a string
public string OutputConfiguration ( ) { return OutputConfiguration ( "\r\n" , true ) ; }
public string OutputConfiguration ( string newline ) { return OutputConfiguration ( newline , true ) ; }
public string OutputConfiguration ( string newline , bool whitespace )
{
// Simply return the configuration structure as string
return OutputStructure ( root , 0 , newline , whitespace ) ;
}
// This will load a configuration from file
public bool LoadConfiguration ( string filename ) { return LoadConfiguration ( filename , false ) ; }
public bool LoadConfiguration ( string filename , bool sorted )
{
// Check if the file is missing
2013-12-13 09:31:18 +00:00
if ( ! File . Exists ( filename ) )
2009-04-19 18:07:22 +00:00
{
throw ( new FileNotFoundException ( "File not found \"" + filename + "\"" , filename ) ) ;
}
else
{
// Load the file contents
FileStream fstream = File . OpenRead ( filename ) ;
byte [ ] fbuffer = new byte [ fstream . Length ] ;
fstream . Read ( fbuffer , 0 , fbuffer . Length ) ;
fstream . Close ( ) ;
// Convert byte array to string
string data = Encoding . UTF8 . GetString ( fbuffer ) ;
// Load the configuration from this data
2009-07-28 13:40:30 +00:00
return InputConfiguration ( filename , data , sorted ) ;
2009-04-19 18:07:22 +00:00
}
}
// This will load a configuration from string
public bool InputConfiguration ( string data ) { return InputConfiguration ( data , false ) ; }
2009-07-28 13:40:30 +00:00
public bool InputConfiguration ( string data , bool sorted ) { return InputConfiguration ( "" , data , sorted ) ; }
private bool InputConfiguration ( string file , string data , bool sorted )
2009-04-19 18:07:22 +00:00
{
// Remove returns and tabs because the
// parser only uses newline for new lines.
data = data . Replace ( "\r" , "" ) ;
data = data . Replace ( "\t" , "" ) ;
// Clear errors
ClearError ( ) ;
// Parse the data to the root structure
2009-07-28 13:40:30 +00:00
if ( sorted ) root = new ListDictionary ( ) ; else root = new Hashtable ( ) ;
int pos = 0 , line = 1 ;
InputStructure ( root , ref file , ref data , ref pos , ref line ) ;
2009-04-19 18:07:22 +00:00
// Return true when done, false when errors occurred
2009-07-28 13:40:30 +00:00
return ! cpErrorResult ;
2009-04-19 18:07:22 +00:00
}
#endregion
}
}