PBXParser.cs 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. using UnityEngine;
  2. using System;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using System.Text;
  6. using System.Text.RegularExpressions;
  7. namespace cn.sharesdk.unity3d.sdkporter
  8. {
  9. public class PBXParser
  10. {
  11. public const string PBX_HEADER_TOKEN = "// !$*UTF8*$!\n";
  12. public const char WHITESPACE_SPACE = ' ';
  13. public const char WHITESPACE_TAB = '\t';
  14. public const char WHITESPACE_NEWLINE = '\n';
  15. public const char WHITESPACE_CARRIAGE_RETURN = '\r';
  16. public const char ARRAY_BEGIN_TOKEN = '(';
  17. public const char ARRAY_END_TOKEN = ')';
  18. public const char ARRAY_ITEM_DELIMITER_TOKEN = ',';
  19. public const char DICTIONARY_BEGIN_TOKEN = '{';
  20. public const char DICTIONARY_END_TOKEN = '}';
  21. public const char DICTIONARY_ASSIGN_TOKEN = '=';
  22. public const char DICTIONARY_ITEM_DELIMITER_TOKEN = ';';
  23. public const char QUOTEDSTRING_BEGIN_TOKEN = '"';
  24. public const char QUOTEDSTRING_END_TOKEN = '"';
  25. public const char QUOTEDSTRING_ESCAPE_TOKEN = '\\';
  26. public const char END_OF_FILE = (char)0x1A;
  27. public const string COMMENT_BEGIN_TOKEN = "/*";
  28. public const string COMMENT_END_TOKEN = "*/";
  29. public const string COMMENT_LINE_TOKEN = "//";
  30. private const int BUILDER_CAPACITY = 20000;
  31. //
  32. private char[] data;
  33. private int index;
  34. // public bool success;
  35. // private int indent;
  36. public PBXDictionary Decode( string data )
  37. {
  38. // success = true;
  39. if( !data.StartsWith( PBX_HEADER_TOKEN ) ) {
  40. Debug.Log( "Wrong file format." );
  41. return null;
  42. }
  43. data = data.Substring( 13 );
  44. this.data = data.ToCharArray();
  45. index = 0;
  46. return (PBXDictionary)ParseValue();
  47. }
  48. public string Encode( PBXDictionary pbxData, bool readable = false )
  49. {
  50. // indent = 0;
  51. StringBuilder builder = new StringBuilder( PBX_HEADER_TOKEN, BUILDER_CAPACITY );
  52. bool success = SerializeValue( pbxData, builder, readable );
  53. return ( success ? builder.ToString() : null );
  54. }
  55. #region Move
  56. private char NextToken()
  57. {
  58. SkipWhitespaces();
  59. return StepForeward();
  60. }
  61. private string Peek( int step = 1 )
  62. {
  63. string sneak = string.Empty;
  64. for( int i = 1; i <= step; i++ ) {
  65. if( data.Length - 1 < index + i ) {
  66. break;
  67. }
  68. sneak += data[ index + i ];
  69. }
  70. return sneak;
  71. }
  72. private bool SkipWhitespaces()
  73. {
  74. bool whitespace = false;
  75. while( Regex.IsMatch( StepForeward().ToString(), @"\s" ) )
  76. whitespace = true;
  77. StepBackward();
  78. if( SkipComments() ) {
  79. whitespace = true;
  80. SkipWhitespaces();
  81. }
  82. return whitespace;
  83. }
  84. private bool SkipComments()
  85. {
  86. string s = string.Empty;
  87. string tag = Peek( 2 );
  88. switch( tag ) {
  89. case COMMENT_BEGIN_TOKEN: {
  90. while( Peek( 2 ).CompareTo( COMMENT_END_TOKEN ) != 0 ) {
  91. s += StepForeward();
  92. }
  93. s += StepForeward( 2 );
  94. break;
  95. }
  96. case COMMENT_LINE_TOKEN: {
  97. while( !Regex.IsMatch( StepForeward().ToString(), @"\n" ) )
  98. continue;
  99. break;
  100. }
  101. default:
  102. return false;
  103. }
  104. return true;
  105. }
  106. private char StepForeward( int step = 1 )
  107. {
  108. index = Math.Min( data.Length, index + step );
  109. return data[ index ];
  110. }
  111. private char StepBackward( int step = 1 )
  112. {
  113. index = Math.Max( 0, index - step );
  114. return data[ index ];
  115. }
  116. #endregion
  117. #region Parse
  118. private object ParseValue()
  119. {
  120. switch( NextToken() ) {
  121. case END_OF_FILE:
  122. Debug.Log( "End of file" );
  123. return null;
  124. case DICTIONARY_BEGIN_TOKEN:
  125. return ParseDictionary();
  126. case ARRAY_BEGIN_TOKEN:
  127. return ParseArray();
  128. case QUOTEDSTRING_BEGIN_TOKEN:
  129. return ParseString();
  130. default:
  131. StepBackward();
  132. return ParseEntity();
  133. }
  134. }
  135. // private T Convert<T>( PBXDictionary dictionary )
  136. // {
  137. // if( dictionary.ContainsKey( "isa" ) ){
  138. //// ((string)dictionary["isa"]).CompareTo(
  139. // Type targetType = Type.GetType( (string)dictionary["isa"] );
  140. // if( targetType.IsSubclassOf( typeof(PBXObject) ) ) {
  141. // Debug.Log( "ok" );
  142. // T converted = (T)Activator.CreateInstance( targetType );
  143. // return converted;
  144. // }
  145. // else {
  146. // Debug.Log( "Warning: unknown PBX type: " + targetType.Name );
  147. // return default(T);
  148. // }
  149. //
  150. // }
  151. // return default(T);
  152. //
  153. // }
  154. private PBXDictionary ParseDictionary()
  155. {
  156. SkipWhitespaces();
  157. PBXDictionary dictionary = new PBXDictionary();
  158. string keyString = string.Empty;
  159. object valueObject = null;
  160. bool complete = false;
  161. while( !complete ) {
  162. switch( NextToken() ) {
  163. case END_OF_FILE:
  164. Debug.Log( "Error: reached end of file inside a dictionary: " + index );
  165. complete = true;
  166. break;
  167. case DICTIONARY_ITEM_DELIMITER_TOKEN:
  168. keyString = string.Empty;
  169. valueObject = null;
  170. break;
  171. case DICTIONARY_END_TOKEN:
  172. keyString = string.Empty;
  173. valueObject = null;
  174. complete = true;
  175. break;
  176. case DICTIONARY_ASSIGN_TOKEN:
  177. valueObject = ParseValue();
  178. dictionary.Add( keyString, valueObject );
  179. break;
  180. default:
  181. StepBackward();
  182. keyString = ParseValue() as string;
  183. break;
  184. }
  185. }
  186. return dictionary;
  187. }
  188. private PBXList ParseArray()
  189. {
  190. PBXList list = new PBXList();
  191. bool complete = false;
  192. while( !complete ) {
  193. switch( NextToken() ) {
  194. case END_OF_FILE:
  195. Debug.Log( "Error: Reached end of file inside a list: " + list );
  196. complete = true;
  197. break;
  198. case ARRAY_END_TOKEN:
  199. complete = true;
  200. break;
  201. case ARRAY_ITEM_DELIMITER_TOKEN:
  202. break;
  203. default:
  204. StepBackward();
  205. list.Add( ParseValue() );
  206. break;
  207. }
  208. }
  209. return list;
  210. }
  211. private object ParseString()
  212. {
  213. string s = string.Empty;
  214. char c = StepForeward();
  215. while( c != QUOTEDSTRING_END_TOKEN ) {
  216. s += c;
  217. if( c == QUOTEDSTRING_ESCAPE_TOKEN )
  218. s += StepForeward();
  219. c = StepForeward();
  220. }
  221. return s;
  222. }
  223. private object ParseEntity()
  224. {
  225. string word = string.Empty;
  226. while( !Regex.IsMatch( Peek(), @"[;,\s=]" ) ) {
  227. word += StepForeward();
  228. }
  229. if( word.Length != 24 && Regex.IsMatch( word, @"^\d+$" ) ) {
  230. return Int32.Parse( word );
  231. }
  232. return word;
  233. }
  234. #endregion
  235. #region Serialize
  236. private bool SerializeValue( object value, StringBuilder builder, bool readable = false )
  237. {
  238. if( value == null ) {
  239. builder.Append( "null" );
  240. }
  241. else if( value is PBXObject ) {
  242. SerializeDictionary( ((PBXObject)value).data, builder, readable );
  243. }
  244. else if( value is Dictionary<string, object> ) {
  245. SerializeDictionary( (Dictionary<string, object>)value, builder, readable );
  246. }
  247. else if( value.GetType().IsArray ) {
  248. SerializeArray( new ArrayList( (ICollection)value ), builder, readable );
  249. }
  250. else if( value is ArrayList ) {
  251. SerializeArray( (ArrayList)value, builder, readable );
  252. }
  253. else if( value is string ) {
  254. SerializeString( (string)value, builder, readable );
  255. }
  256. else if( value is Char ) {
  257. SerializeString( Convert.ToString( (char)value ), builder, readable );
  258. }
  259. else if( value is bool ) {
  260. builder.Append( Convert.ToInt32( value ).ToString() );
  261. }
  262. else if( value.GetType().IsPrimitive ) {
  263. builder.Append( Convert.ToString( value ) );
  264. }
  265. // else if( value is Hashtable )
  266. // {
  267. // serializeObject( (Hashtable)value, builder );
  268. // }
  269. // else if( ( value is Boolean ) && ( (Boolean)value == true ) )
  270. // {
  271. // builder.Append( "NO" );
  272. // }
  273. // else if( ( value is Boolean ) && ( (Boolean)value == false ) )
  274. // {
  275. // builder.Append( "YES" );
  276. // }
  277. else {
  278. Debug.LogWarning( "Error: unknown object of type " + value.GetType().Name );
  279. return false;
  280. }
  281. return true;
  282. }
  283. private bool SerializeDictionary( Dictionary<string, object> dictionary, StringBuilder builder, bool readable = false )
  284. {
  285. builder.Append( DICTIONARY_BEGIN_TOKEN );
  286. foreach( KeyValuePair<string, object> pair in dictionary ) {
  287. SerializeString( pair.Key, builder );
  288. builder.Append( DICTIONARY_ASSIGN_TOKEN );
  289. SerializeValue( pair.Value, builder );
  290. builder.Append( DICTIONARY_ITEM_DELIMITER_TOKEN );
  291. }
  292. builder.Append( DICTIONARY_END_TOKEN );
  293. return true;
  294. }
  295. private bool SerializeArray( ArrayList anArray, StringBuilder builder, bool readable = false )
  296. {
  297. builder.Append( ARRAY_BEGIN_TOKEN );
  298. for( int i = 0; i < anArray.Count; i++ )
  299. {
  300. object value = anArray[i];
  301. if( !SerializeValue( value, builder ) )
  302. {
  303. return false;
  304. }
  305. builder.Append( ARRAY_ITEM_DELIMITER_TOKEN );
  306. }
  307. builder.Append( ARRAY_END_TOKEN );
  308. return true;
  309. }
  310. private bool SerializeString( string aString, StringBuilder builder, bool useQuotes = false, bool readable = false )
  311. {
  312. // Is a GUID?
  313. if( Regex.IsMatch( aString, @"^[A-F0-9]{24}$" ) ) {
  314. builder.Append( aString );
  315. return true;
  316. }
  317. // Is an empty string?
  318. if( string.IsNullOrEmpty( aString ) ) {
  319. builder.Append( QUOTEDSTRING_BEGIN_TOKEN );
  320. builder.Append( QUOTEDSTRING_END_TOKEN );
  321. return true;
  322. }
  323. if( !Regex.IsMatch( aString, @"^[A-Za-z0-9_.]+$" ) ) {
  324. useQuotes = true;
  325. }
  326. if( useQuotes )
  327. builder.Append( QUOTEDSTRING_BEGIN_TOKEN );
  328. builder.Append( aString );
  329. if( useQuotes )
  330. builder.Append( QUOTEDSTRING_END_TOKEN );
  331. return true;
  332. }
  333. #endregion
  334. }
  335. }