2006-03-19 Marek Sieradzki <marek.sieradzki@gmail.com>
[mono.git] / mcs / class / Microsoft.Build.Engine / Microsoft.Build.BuildEngine / Expression.cs
1 //
2 // Expression.cs: Stores references to items or properties.
3 //
4 // Author:
5 //   Marek Sieradzki (marek.sieradzki@gmail.com)
6 // 
7 // (C) 2005 Marek Sieradzki
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
28 #if NET_2_0
29
30 using System;
31 using System.IO;
32 using System.Collections;
33 using System.Text;
34 using Microsoft.Build.Framework;
35 using Microsoft.Build.Utilities;
36
37 namespace Microsoft.Build.BuildEngine {
38         internal class Expression {
39         
40                 IList   objects;
41                 Project project;
42                 ItemReference parentItemReference;
43         
44                 public Expression (Project project)
45                 {
46                         this.objects = new ArrayList ();
47                         this.project = project;
48                 }
49                 
50                 public Expression (Project project, string source)
51                         : this (project)
52                 {
53                         ParseSource (source);
54                 }
55                 
56                 public void ParseSource (string source)
57                 {
58                         // FIXME: change StringBuilder to substrings 
59                         if (source == null)
60                                 throw new ArgumentNullException ("source");                             
61
62                         // FIXME: hack
63                         source = source.Replace ('/', Path.DirectorySeparatorChar);
64                         source = source.Replace ('\\', Path.DirectorySeparatorChar);
65                         StringBuilder temp = new StringBuilder ();
66                         CharEnumerator it = source.GetEnumerator ();
67                         EvaluationState eState = EvaluationState.Out;
68                         ParenState pState = ParenState.Out;
69                         ApostropheState aState = ApostropheState.Out;
70                         int start = 0;
71                         int current = -1;
72                         
73                         while (it.MoveNext ()) {
74                                 current++;
75                                 switch (eState) {
76                                 case EvaluationState.Out:
77                                         switch (it.Current) {
78                                         case '@':
79                                                 if (temp.Length > 0) {
80                                                         objects.Add (temp.ToString ());
81                                                         temp = new StringBuilder ();
82                                                 }
83                                                 eState = EvaluationState.InItem;
84                                                 start = current;
85                                                 break;
86                                         case '$':
87                                                 if (temp.Length > 0) {
88                                                         objects.Add (temp.ToString ());
89                                                         temp = new StringBuilder ();
90                                                 }
91                                                 eState = EvaluationState.InProperty;
92                                                 start = current;
93                                                 break;
94                                         case '%':
95                                                 if (temp.Length > 0) {
96                                                         objects.Add (temp.ToString ());
97                                                         temp = new StringBuilder ();
98                                                 }
99                                                 eState = EvaluationState.InMetadata;
100                                                 start = current;
101                                                 break;
102                                         default:
103                                                 temp.Append (it.Current);
104                                                 if (current == source.Length - 1)
105                                                         objects.Add (temp.ToString ());
106                                                 break;
107                                         }
108                                         break;
109                                 case EvaluationState.InItem:
110                                         switch (it.Current) {
111                                         case '(':
112                                                 if (pState == ParenState.Out && aState == ApostropheState.Out)
113                                                         pState = ParenState.Left;
114                                                 else if (aState == ApostropheState.Out)
115                                                         throw new Exception ("'(' not expected.");
116                                                 break;
117                                         case ')':
118                                                 if (pState == ParenState.Left && aState == ApostropheState.Out) {
119                                                         objects.Add (new ItemReference (this, source.Substring (start, current - start + 1)));
120                                                         eState = EvaluationState.Out;
121                                                         pState = ParenState.Out;
122                                                 }
123                                                 break;
124                                         case '\'':
125                                                 if (aState == ApostropheState.In)
126                                                         aState = ApostropheState.Out;
127                                                 else
128                                                         aState = ApostropheState.In;
129                                                 break;
130                                         default:
131                                                 break;
132                                         }
133                                         break;
134                                 case EvaluationState.InProperty:
135                                         switch (it.Current) {
136                                         case '(':
137                                                 if (pState == ParenState.Out)
138                                                         pState = ParenState.Left;
139                                                 else
140                                                         throw new Exception ("'(' expected.");
141                                                 break;
142                                         case ')':
143                                                 if (pState == ParenState.Left) {
144                                                         objects.Add (new PropertyReference (this, source.Substring (start, current - start + 1)));
145                                                         eState = EvaluationState.Out;
146                                                         pState = ParenState.Out;
147                                                 }
148                                                 break;
149                                         default:
150                                                 break;
151                                         }
152                                         break;
153                                 case EvaluationState.InMetadata:
154                                         switch (it.Current) {
155                                         case '(':
156                                                 if (pState == ParenState.Out)
157                                                         pState = ParenState.Left;
158                                                 break;
159                                         case ')':
160                                                 if (pState == ParenState.Left) {
161                                                         objects.Add (new MetadataReference (this, source.Substring (start, current - start + 1)));
162                                                         eState = EvaluationState.Out;
163                                                         pState = ParenState.Out;
164                                                 }
165                                                 break;
166                                         default:
167                                                 break;
168                                         }
169                                         break;
170                                 default:
171                                         throw new Exception ("Invalid evaluation state.");
172                                 }
173                         }
174                 }
175
176                 public IEnumerator GetEnumerator ()
177                 {
178                         foreach (object o in objects)
179                                 yield return o;
180                 }
181                 
182                 public object ToNonArray (Type type)
183                 {
184                         if (type.IsArray == true)
185                                 throw new ArgumentException ("Type specified can not be array type.");
186                         
187                         return ToObject (ToString (), type);
188                 }
189                 
190                 public object ToArray (Type type)
191                 {
192                         if (type.IsArray == false)
193                                 throw new ArgumentException ("Type specified can not be element type.");
194                         
195                         string[] rawTable = ToString ().Split (';');
196                         int i = 0;
197                         
198                         if (type == typeof (bool[])) {
199                                 bool[] array = new bool [rawTable.Length];
200                                 foreach (string raw in rawTable)
201                                         array [i++] = (bool) ToObject (raw, typeof (bool));
202                                 return array;
203                         } else if (type == typeof (string[])) {
204                                 string[] array = new string [rawTable.Length];
205                                 foreach (string raw in rawTable)
206                                         array [i++] = (string) ToObject (raw, typeof (string));
207                                 return array;
208                         } else if (type == typeof (int[])) {
209                                 int[] array = new int [rawTable.Length];
210                                 foreach (string raw in rawTable)
211                                         array [i++] = (int) ToObject (raw, typeof (int));
212                                 return array;
213                         } else if (type == typeof (uint[])) {
214                                 uint[] array = new uint [rawTable.Length];
215                                 foreach (string raw in rawTable)
216                                         array [i++] = (uint) ToObject (raw, typeof (uint));
217                                 return array;
218                         } else if (type == typeof (DateTime[])) {
219                                 DateTime[] array = new DateTime [rawTable.Length];
220                                 foreach (string raw in rawTable)
221                                         array [i++] = (DateTime) ToObject (raw, typeof (DateTime));
222                                 return array;
223                         } else throw new Exception ("Invalid type.");
224                 }
225                 
226                 private object ToObject (string raw, Type type)
227                 {
228                         if (type == typeof (bool)) {
229                                 return Boolean.Parse (raw);
230                         } else if (type == typeof (string)) {
231                                 return raw;
232                         } else if (type == typeof (int)) {
233                                 return Int32.Parse (raw);
234                         } else if (type == typeof (uint)) {
235                                 return UInt32.Parse (raw);
236                         } else if (type == typeof (DateTime)) {
237                                 return DateTime.Parse (raw);
238                         } else {
239                                 throw new Exception (String.Format ("Unknown type: {0}", type.ToString ()));
240                         }
241                 }
242                 
243                 private new string ToString ()
244                 {
245                         StringBuilder sb = new StringBuilder ();
246                         
247                         foreach (object o in this) {
248                                 if (o is string) {
249                                         sb.Append ((string) o);
250                                 } else if (o is ItemReference) {
251                                         sb.Append (((ItemReference)o).ToString ());
252                                 } else if (o is PropertyReference) {
253                                         sb.Append (((PropertyReference)o).ToString ());
254                                 } else if (o is MetadataReference) {
255                                         // FIXME: we don't handle them yet
256                                 } else {
257                                         throw new Exception ("Invalid type in objects collection.");
258                                 }
259                         }
260                         return sb.ToString ();
261                 }
262
263                 public ITaskItem ToITaskItem ()
264                 {
265                         ITaskItem item;
266                         
267                         if (objects == null)
268                                 throw new Exception ("Cannot cast empty expression to ITaskItem.");
269                         
270                         if (objects [0] is ItemReference) {
271                                 ItemReference ir = (ItemReference) objects [0];
272                                 ITaskItem[] array = ir.ToITaskItemArray ();
273                                 if (array.Length == 1) {
274                                         return array [0];
275                                 } else {
276                                         throw new Exception ("TaskItem array too long");
277                                 }
278                         } else {
279                                 item = new TaskItem (ToString ());
280                                 return item;
281                         }
282                 }
283                 
284                 public ITaskItem[] ToITaskItemArray ()
285                 {
286                         ArrayList finalItems = new ArrayList ();
287                         ArrayList tempItems = new ArrayList ();
288                         ITaskItem[] array;
289                         ITaskItem[] finalArray;
290                         
291                         foreach (object o in objects) {
292                                 if (o is ItemReference) {
293                                         tempItems.Add (o);
294                                 } else if (o is PropertyReference) {
295                                         PropertyReference pr = (PropertyReference) o;
296                                         tempItems.Add (pr.ToString ());
297                                 } else if (o is MetadataReference) {
298                                         // FIXME: not handled yet
299                                 } else if (o is string) {
300                                         tempItems.Add (o);
301                                 } else {
302                                         throw new Exception ("Invalid type in objects collection.");
303                                 }
304                         }
305                         foreach (object o in tempItems) {
306                                 if (o is ItemReference) {
307                                         ItemReference ir = (ItemReference) o;
308                                         array = ir.ToITaskItemArray ();
309                                         if (array != null)
310                                                 foreach (ITaskItem item in array)
311                                                         finalItems.Add (item);
312                                 } else if (o is string) {
313                                         string s = (string) o;
314                                         array = ITaskItemArrayFromString (s);
315                                         foreach (ITaskItem item in array)
316                                                 finalItems.Add (item);
317                                 } else {
318                                         throw new Exception ("Invalid type in tempItems collection.");
319                                 }
320                         }
321                         
322                         finalArray = new ITaskItem [finalItems.Count];
323                         int i = 0;
324                         foreach (ITaskItem item in finalItems)
325                                 finalArray [i++] = item;
326                         return finalArray;
327                 }
328                 
329                 // FIXME: quite stupid name
330                 private ITaskItem[] ITaskItemArrayFromString (string source)
331                 {
332                         ArrayList tempItems = new ArrayList ();
333                         ITaskItem[] finalArray;
334                         string[] splittedSource = source.Split (';');
335                         foreach (string s in splittedSource) {
336                                 if (s != String.Empty) {
337                                         tempItems.Add (new TaskItem (s));
338                                 }
339                         }
340                         finalArray = new ITaskItem [tempItems.Count];
341                         int i = 0;
342                         foreach (ITaskItem item in tempItems)
343                                 finalArray [i++] = item;
344                         return finalArray;
345                 }
346                 
347                 public Project Project {
348                         get { return project; }
349                 }
350                 
351                 public ItemReference ParentItemReference {
352                         get { return parentItemReference; }
353                 }
354         }
355
356         internal enum EvaluationState {
357                 Out,
358                 InItem,
359                 InMetadata,
360                 InProperty
361         }
362         
363         internal enum ParenState {
364                 Out,
365                 Left
366         }
367         
368         internal enum ApostropheState {
369                 In,
370                 Out
371         }
372         
373         internal enum ItemParsingState {
374                 Name,
375                 Transform1,
376                 Transform2,
377                 Separator
378         }
379 }
380
381 #endif