PageRenderTime 105ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/Assets/Facebook/Editor/iOS/third_party/XCodeEditor-for-Unity/PBXParser.cs

https://gitlab.com/mutatis/Krazy
C# | 470 lines | 394 code | 73 blank | 3 comment | 62 complexity | 09536cac680431336f8929fd0d84499b MD5 | raw file
  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 UnityEditor.XCodeEditor
  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. private char[] data;
  32. private int index;
  33. private int indent;
  34. public PBXDictionary Decode( string data )
  35. {
  36. if( !data.StartsWith( PBX_HEADER_TOKEN ) ) {
  37. Debug.Log( "Wrong file format." );
  38. return null;
  39. }
  40. data = data.Substring( 13 );
  41. this.data = data.ToCharArray();
  42. index = 0;
  43. return (PBXDictionary)ParseValue();
  44. }
  45. public string Encode( PBXDictionary pbxData)
  46. {
  47. indent = 0;
  48. StringBuilder builder = new StringBuilder( PBX_HEADER_TOKEN, BUILDER_CAPACITY );
  49. bool success = SerializeValue( pbxData, builder);
  50. return ( success ? builder.ToString() : null );
  51. }
  52. #region Move
  53. private char NextToken()
  54. {
  55. SkipWhitespaces();
  56. return StepForeward();
  57. }
  58. private string Peek( int step = 1 )
  59. {
  60. string sneak = string.Empty;
  61. for( int i = 1; i <= step; i++ ) {
  62. if( data.Length - 1 < index + i ) {
  63. break;
  64. }
  65. sneak += data[ index + i ];
  66. }
  67. return sneak;
  68. }
  69. private bool SkipWhitespaces()
  70. {
  71. bool whitespace = false;
  72. while( Regex.IsMatch( StepForeward().ToString(), @"\s" ) )
  73. whitespace = true;
  74. StepBackward();
  75. if( SkipComments() ) {
  76. whitespace = true;
  77. SkipWhitespaces();
  78. }
  79. return whitespace;
  80. }
  81. private bool SkipComments()
  82. {
  83. string s = string.Empty;
  84. string tag = Peek( 2 );
  85. switch( tag ) {
  86. case COMMENT_BEGIN_TOKEN: {
  87. while( Peek( 2 ).CompareTo( COMMENT_END_TOKEN ) != 0 ) {
  88. s += StepForeward();
  89. }
  90. s += StepForeward( 2 );
  91. break;
  92. }
  93. case COMMENT_LINE_TOKEN: {
  94. while( !Regex.IsMatch( StepForeward().ToString(), @"\n" ) )
  95. continue;
  96. break;
  97. }
  98. default:
  99. return false;
  100. }
  101. return true;
  102. }
  103. private char StepForeward( int step = 1 )
  104. {
  105. index = Math.Min( data.Length, index + step );
  106. return data[ index ];
  107. }
  108. private char StepBackward( int step = 1 )
  109. {
  110. index = Math.Max( 0, index - step );
  111. return data[ index ];
  112. }
  113. #endregion
  114. #region Parse
  115. private object ParseValue()
  116. {
  117. switch( NextToken() ) {
  118. case END_OF_FILE:
  119. Debug.Log( "End of file" );
  120. return null;
  121. case DICTIONARY_BEGIN_TOKEN:
  122. return ParseDictionary();
  123. case ARRAY_BEGIN_TOKEN:
  124. return ParseArray();
  125. case QUOTEDSTRING_BEGIN_TOKEN:
  126. return ParseString();
  127. default:
  128. StepBackward();
  129. return ParseEntity();
  130. }
  131. }
  132. private PBXDictionary ParseDictionary()
  133. {
  134. SkipWhitespaces();
  135. PBXDictionary dictionary = new PBXDictionary();
  136. string keyString = string.Empty;
  137. object valueObject = null;
  138. bool complete = false;
  139. while( !complete ) {
  140. switch( NextToken() ) {
  141. case END_OF_FILE:
  142. Debug.Log( "Error: reached end of file inside a dictionary: " + index );
  143. complete = true;
  144. break;
  145. case DICTIONARY_ITEM_DELIMITER_TOKEN:
  146. keyString = string.Empty;
  147. valueObject = null;
  148. break;
  149. case DICTIONARY_END_TOKEN:
  150. keyString = string.Empty;
  151. valueObject = null;
  152. complete = true;
  153. break;
  154. case DICTIONARY_ASSIGN_TOKEN:
  155. valueObject = ParseValue();
  156. dictionary.Add( keyString, valueObject );
  157. break;
  158. default:
  159. StepBackward();
  160. keyString = ParseValue() as string;
  161. break;
  162. }
  163. }
  164. return dictionary;
  165. }
  166. private PBXList ParseArray()
  167. {
  168. PBXList list = new PBXList();
  169. bool complete = false;
  170. while( !complete ) {
  171. switch( NextToken() ) {
  172. case END_OF_FILE:
  173. Debug.Log( "Error: Reached end of file inside a list: " + list );
  174. complete = true;
  175. break;
  176. case ARRAY_END_TOKEN:
  177. complete = true;
  178. break;
  179. case ARRAY_ITEM_DELIMITER_TOKEN:
  180. break;
  181. default:
  182. StepBackward();
  183. list.Add( ParseValue() );
  184. break;
  185. }
  186. }
  187. return list;
  188. }
  189. private object ParseString()
  190. {
  191. string s = string.Empty;
  192. s += "\"";
  193. char c = StepForeward();
  194. while( c != QUOTEDSTRING_END_TOKEN ) {
  195. s += c;
  196. if( c == QUOTEDSTRING_ESCAPE_TOKEN )
  197. s += StepForeward();
  198. c = StepForeward();
  199. }
  200. s += "\"";
  201. return s;
  202. }
  203. //there has got to be a better way to do this
  204. private string GetDataSubstring(int begin, int length)
  205. {
  206. string res = string.Empty;
  207. for(int i=begin; i<begin+length && i<data.Length; i++)
  208. {
  209. res += data[i];
  210. }
  211. return res;
  212. }
  213. private int CountWhitespace(int pos)
  214. {
  215. int i=0;
  216. for(int currPos=pos; currPos<data.Length && Regex.IsMatch( GetDataSubstring(currPos, 1), @"[;,\s=]" ); i++, currPos++) {}
  217. return i;
  218. }
  219. private string ParseCommentFollowingWhitespace()
  220. {
  221. int currIdx = index+1;
  222. int whitespaceLength = CountWhitespace(currIdx);
  223. currIdx += whitespaceLength;
  224. if(currIdx + 1 >= data.Length)
  225. return "";
  226. if(data[currIdx] == '/' && data[currIdx+1] == '*')
  227. {
  228. while(!GetDataSubstring(currIdx, 2).Equals(COMMENT_END_TOKEN))
  229. {
  230. if(currIdx >= data.Length)
  231. {
  232. Debug.LogError("Unterminated comment found in .pbxproj file. Bad things are probably going to start happening");
  233. return "";
  234. }
  235. currIdx++;
  236. }
  237. return GetDataSubstring (index+1, (currIdx-index+1));
  238. }
  239. else
  240. {
  241. return "";
  242. }
  243. }
  244. private object ParseEntity()
  245. {
  246. string word = string.Empty;
  247. while(!Regex.IsMatch( Peek(), @"[;,\s=]" ))
  248. {
  249. word += StepForeward();
  250. }
  251. string comment = ParseCommentFollowingWhitespace();
  252. if(comment.Length > 0)
  253. {
  254. word += comment;
  255. index += comment.Length;
  256. }
  257. if( word.Length != 24 && Regex.IsMatch( word, @"^\d+$" ) ) {
  258. return Int32.Parse( word );
  259. }
  260. return word;
  261. }
  262. #endregion
  263. #region Serialize
  264. private void AppendNewline(StringBuilder builder)
  265. {
  266. builder.Append(WHITESPACE_NEWLINE);
  267. for(int i=0; i<indent; i++)
  268. {
  269. builder.Append (WHITESPACE_TAB);
  270. }
  271. }
  272. private void AppendLineDelim(StringBuilder builder, bool newline)
  273. {
  274. if(newline)
  275. {
  276. AppendNewline(builder);
  277. }
  278. else
  279. {
  280. builder.Append(WHITESPACE_SPACE);
  281. }
  282. }
  283. private bool SerializeValue( object value, StringBuilder builder)
  284. {
  285. bool internalNewlines = false;
  286. if(value is PBXObject)
  287. {
  288. internalNewlines = ((PBXObject)value).internalNewlines;
  289. }
  290. else if(value is PBXDictionary)
  291. {
  292. internalNewlines = ((PBXDictionary)value).internalNewlines;
  293. }
  294. else if(value is PBXList)
  295. {
  296. internalNewlines = ((PBXList)value).internalNewlines;
  297. }
  298. if( value == null ) {
  299. builder.Append( "null" );
  300. }
  301. else if( value is PBXObject ) {
  302. SerializeDictionary( ((PBXObject)value).data, builder, internalNewlines);
  303. }
  304. else if( value is PBXDictionary ) {
  305. SerializeDictionary( (Dictionary<string, object>)value, builder, internalNewlines);
  306. }
  307. else if( value is Dictionary<string, object> ) {
  308. SerializeDictionary( (Dictionary<string, object>)value, builder, internalNewlines);
  309. }
  310. else if( value.GetType().IsArray ) {
  311. SerializeArray( new ArrayList( (ICollection)value ), builder, internalNewlines);
  312. }
  313. else if( value is ArrayList ) {
  314. SerializeArray( (ArrayList)value, builder, internalNewlines);
  315. }
  316. else if( value is string ) {
  317. SerializeString( (string)value, builder);
  318. }
  319. else if( value is Char ) {
  320. SerializeString( Convert.ToString( (char)value ), builder);
  321. }
  322. else if( value is bool ) {
  323. builder.Append( Convert.ToInt32( value ).ToString() );
  324. }
  325. else if( value.GetType().IsPrimitive ) {
  326. builder.Append( Convert.ToString( value ) );
  327. }
  328. else {
  329. Debug.LogWarning( "Error: unknown object of type " + value.GetType().Name );
  330. return false;
  331. }
  332. return true;
  333. }
  334. private bool SerializeDictionary( Dictionary<string, object> dictionary, StringBuilder builder, bool internalNewlines)
  335. {
  336. builder.Append( DICTIONARY_BEGIN_TOKEN );
  337. if(dictionary.Count > 0)
  338. indent++;
  339. if(internalNewlines)
  340. AppendNewline(builder);
  341. int i=0;
  342. foreach( KeyValuePair<string, object> pair in dictionary ) {
  343. SerializeString( pair.Key, builder );
  344. builder.Append( WHITESPACE_SPACE );
  345. builder.Append( DICTIONARY_ASSIGN_TOKEN );
  346. builder.Append( WHITESPACE_SPACE );
  347. SerializeValue( pair.Value, builder );
  348. builder.Append( DICTIONARY_ITEM_DELIMITER_TOKEN );
  349. if(i == dictionary.Count-1)
  350. indent--;
  351. AppendLineDelim(builder, internalNewlines);
  352. i++;
  353. }
  354. builder.Append( DICTIONARY_END_TOKEN );
  355. return true;
  356. }
  357. private bool SerializeArray( ArrayList anArray, StringBuilder builder, bool internalNewlines)
  358. {
  359. builder.Append( ARRAY_BEGIN_TOKEN );
  360. if(anArray.Count > 0)
  361. indent++;
  362. if(internalNewlines)
  363. AppendNewline(builder);
  364. for( int i = 0; i < anArray.Count; i++ )
  365. {
  366. object value = anArray[i];
  367. if( !SerializeValue( value, builder ) )
  368. {
  369. return false;
  370. }
  371. builder.Append( ARRAY_ITEM_DELIMITER_TOKEN );
  372. if(i == anArray.Count-1)
  373. indent--;
  374. AppendLineDelim(builder, internalNewlines);
  375. }
  376. builder.Append( ARRAY_END_TOKEN );
  377. return true;
  378. }
  379. private bool SerializeString( string aString, StringBuilder builder)
  380. {
  381. // Is a GUID?
  382. if(PBXObject.IsGuid(aString)) {
  383. builder.Append( aString );
  384. return true;
  385. }
  386. // Is an empty string?
  387. if( string.IsNullOrEmpty( aString ) ) {
  388. builder.Append( QUOTEDSTRING_BEGIN_TOKEN );
  389. builder.Append( QUOTEDSTRING_END_TOKEN );
  390. return true;
  391. }
  392. builder.Append( aString );
  393. return true;
  394. }
  395. #endregion
  396. }
  397. }