Move from xbuild into mcs
[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 using System;
29 using System.Collections;
30 using System.Text;
31 using Microsoft.Build.Framework;
32 using Microsoft.Build.Utilities;
33
34 namespace Microsoft.Build.BuildEngine {
35         internal class Expression {
36         
37                 IList   objects;
38                 Project project;
39                 ItemReference parentItemReference;
40         
41                 public Expression (Project project)
42                 {
43                         this.objects = new ArrayList ();
44                         this.project = project;
45                 }
46                 
47                 public Expression (Project project, string source)
48                         : this (project)
49                 {
50                         ParseSource (source);
51                 }
52                 
53                 public void ParseSource (string source)
54                 {
55                         // FIXME: change StringBuilder to substrings 
56                         if (source == null)
57                                 throw new ArgumentNullException ("source");                             
58                         
59                         StringBuilder temp = new StringBuilder ();
60                         CharEnumerator it = source.GetEnumerator ();
61                         EvaluationState eState = EvaluationState.Out;
62                         ParenState pState = ParenState.Out;
63                         ApostropheState aState = ApostropheState.Out;
64                         int start = 0;
65                         int current = -1;
66                         
67                         while (it.MoveNext ()) {
68                                 current++;
69                                 switch (eState) {
70                                 case EvaluationState.Out:
71                                         switch (it.Current) {
72                                         case '@':
73                                                 if (temp.Length > 0) {
74                                                         objects.Add (temp.ToString ());
75                                                         temp = new StringBuilder ();
76                                                 }
77                                                 eState = EvaluationState.InItem;
78                                                 start = current;
79                                                 break;
80                                         case '$':
81                                                 if (temp.Length > 0) {
82                                                         objects.Add (temp.ToString ());
83                                                         temp = new StringBuilder ();
84                                                 }
85                                                 eState = EvaluationState.InProperty;
86                                                 start = current;
87                                                 break;
88                                         case '%':
89                                                 if (temp.Length > 0) {
90                                                         objects.Add (temp.ToString ());
91                                                         temp = new StringBuilder ();
92                                                 }
93                                                 eState = EvaluationState.InMetadata;
94                                                 start = current;
95                                                 break;
96                                         default:
97                                                 temp.Append (it.Current);
98                                                 if (current == source.Length - 1)
99                                                         objects.Add (temp.ToString ());
100                                                 break;
101                                         }
102                                         break;
103                                 case EvaluationState.InItem:
104                                         switch (it.Current) {
105                                         case '(':
106                                                 if (pState == ParenState.Out && aState == ApostropheState.Out)
107                                                         pState = ParenState.Left;
108                                                 else if (aState == ApostropheState.Out)
109                                                         throw new Exception ("'(' not expected.");
110                                                 break;
111                                         case ')':
112                                                 if (pState == ParenState.Left && aState == ApostropheState.Out) {
113                                                         objects.Add (new ItemReference (this, source.Substring (start, current - start + 1)));
114                                                         eState = EvaluationState.Out;
115                                                         pState = ParenState.Out;
116                                                 }
117                                                 break;
118                                         case '\'':
119                                                 if (aState == ApostropheState.In)
120                                                         aState = ApostropheState.Out;
121                                                 else
122                                                         aState = ApostropheState.In;
123                                                 break;
124                                         default:
125                                                 break;
126                                         }
127                                         break;
128                                 case EvaluationState.InProperty:
129                                         switch (it.Current) {
130                                         case '(':
131                                                 if (pState == ParenState.Out)
132                                                         pState = ParenState.Left;
133                                                 else
134                                                         throw new Exception ("'(' expected.");
135                                                 break;
136                                         case ')':
137                                                 if (pState == ParenState.Left) {
138                                                         objects.Add (new PropertyReference (this, source.Substring (start, current - start + 1)));
139                                                         eState = EvaluationState.Out;
140                                                         pState = ParenState.Out;
141                                                 }
142                                                 break;
143                                         default:
144                                                 break;
145                                         }
146                                         break;
147                                 case EvaluationState.InMetadata:
148                                         switch (it.Current) {
149                                         case '(':
150                                                 if (pState == ParenState.Out)
151                                                         pState = ParenState.Left;
152                                                 break;
153                                         case ')':
154                                                 if (pState == ParenState.Left) {
155                                                         objects.Add (new MetadataReference (this, source.Substring (start, current - start + 1)));
156                                                         eState = EvaluationState.Out;
157                                                         pState = ParenState.Out;
158                                                 }
159                                                 break;
160                                         default:
161                                                 break;
162                                         }
163                                         break;
164                                 default:
165                                         throw new Exception ("Invalid evaluation state.");
166                                 }
167                         }
168                 }
169
170                 public IEnumerator GetEnumerator ()
171                 {
172                         foreach (object o in objects)
173                                 yield return o;
174                 }
175                 
176                 public object ToNonArray (Type type)
177                 {
178                         if (type.IsArray == true)
179                                 throw new ArgumentException ("Type specified can not be array type.");
180                         
181                         return ToObject (ToString (), type);
182                 }
183                 
184                 public object ToArray (Type type)
185                 {
186                         if (type.IsArray == false)
187                                 throw new ArgumentException ("Type specified can not be element type.");
188                         
189                         string[] rawTable = ToString ().Split (';');
190                         int i = 0;
191                         
192                         if (type == typeof (bool[])) {
193                                 bool[] array = new bool [rawTable.Length];
194                                 foreach (string raw in rawTable)
195                                         array [i++] = (bool) ToObject (raw, typeof (bool));
196                                 return array;
197                         } else if (type == typeof (string[])) {
198                                 string[] array = new string [rawTable.Length];
199                                 foreach (string raw in rawTable)
200                                         array [i++] = (string) ToObject (raw, typeof (string));
201                                 return array;
202                         } else if (type == typeof (int[])) {
203                                 int[] array = new int [rawTable.Length];
204                                 foreach (string raw in rawTable)
205                                         array [i++] = (int) ToObject (raw, typeof (int));
206                                 return array;
207                         } else if (type == typeof (uint[])) {
208                                 uint[] array = new uint [rawTable.Length];
209                                 foreach (string raw in rawTable)
210                                         array [i++] = (uint) ToObject (raw, typeof (uint));
211                                 return array;
212                         } else if (type == typeof (DateTime[])) {
213                                 DateTime[] array = new DateTime [rawTable.Length];
214                                 foreach (string raw in rawTable)
215                                         array [i++] = (DateTime) ToObject (raw, typeof (DateTime));
216                                 return array;
217                         } else throw new Exception ("Invalid type.");
218                 }
219                 
220                 private object ToObject (string raw, Type type)
221                 {
222                         if (type == typeof (bool)) {
223                                 return Boolean.Parse (raw);
224                         } else if (type == typeof (string)) {
225                                 return raw;
226                         } else if (type == typeof (int)) {
227                                 return Int32.Parse (raw);
228                         } else if (type == typeof (uint)) {
229                                 return UInt32.Parse (raw);
230                         } else if (type == typeof (DateTime)) {
231                                 return DateTime.Parse (raw);
232                         } else {
233                                 throw new Exception (String.Format ("Unknown type: {0}", type.ToString ()));
234                         }
235                 }
236                 
237                 private new string ToString ()
238                 {
239                         StringBuilder sb = new StringBuilder ();
240                         
241                         foreach (object o in this) {
242                                 if (o is string) {
243                                         sb.Append ((string) o);
244                                 } else if (o is ItemReference) {
245                                         sb.Append (((ItemReference)o).ToString ());
246                                 } else if (o is PropertyReference) {
247                                         sb.Append (((PropertyReference)o).ToString ());
248                                 } else if (o is MetadataReference) {
249                                         // FIXME: we don't handle them yet
250                                 } else {
251                                         throw new Exception ("Invalid type in objects collection.");
252                                 }
253                         }
254                         return sb.ToString ();
255                 }
256
257                 public ITaskItem ToITaskItem ()
258                 {
259                         ITaskItem item;
260                         
261                         if (objects == null)
262                                 throw new Exception ("Cannot cast empty expression to ITaskItem.");
263                         
264                         if (objects [0] is ItemReference) {
265                                 ItemReference ir = (ItemReference) objects [0];
266                                 ITaskItem[] array = ir.ToITaskItemArray ();
267                                 if (array.Length == 1) {
268                                         return array [0];
269                                 } else {
270                                         throw new Exception ("TaskItem array too long");
271                                 }
272                         } else {
273                                 item = new TaskItem (ToString ());
274                                 return item;
275                         }
276                 }
277                 
278                 public ITaskItem[] ToITaskItemArray ()
279                 {
280                         ArrayList finalItems = new ArrayList ();
281                         ArrayList tempItems = new ArrayList ();
282                         ITaskItem[] array;
283                         ITaskItem[] finalArray;
284                         
285                         foreach (object o in objects) {
286                                 if (o is ItemReference) {
287                                         tempItems.Add (o);
288                                 } else if (o is PropertyReference) {
289                                         PropertyReference pr = (PropertyReference) o;
290                                         tempItems.Add (pr.ToString ());
291                                 } else if (o is MetadataReference) {
292                                         // FIXME: not handled yet
293                                 } else if (o is string) {
294                                         tempItems.Add (o);
295                                 } else {
296                                         throw new Exception ("Invalid type in objects collection.");
297                                 }
298                         }
299                         foreach (object o in tempItems) {
300                                 if (o is ItemReference) {
301                                         ItemReference ir = (ItemReference) o;
302                                         array = ir.ToITaskItemArray ();
303                                         if (array != null)
304                                                 foreach (ITaskItem item in array)
305                                                         finalItems.Add (item);
306                                 } else if (o is string) {
307                                         string s = (string) o;
308                                         array = ITaskItemArrayFromString (s);
309                                         foreach (ITaskItem item in array)
310                                                 finalItems.Add (item);
311                                 } else {
312                                         throw new Exception ("Invalid type in tempItems collection.");
313                                 }
314                         }
315                         
316                         finalArray = new ITaskItem [finalItems.Count];
317                         int i = 0;
318                         foreach (ITaskItem item in finalItems)
319                                 finalArray [i++] = item;
320                         return finalArray;
321                 }
322                 
323                 // FIXME: quite stupid name
324                 private ITaskItem[] ITaskItemArrayFromString (string source)
325                 {
326                         ArrayList tempItems = new ArrayList ();
327                         ITaskItem[] finalArray;
328                         string[] splittedSource = source.Split (';');
329                         foreach (string s in splittedSource) {
330                                 if (s != String.Empty) {
331                                         tempItems.Add (new TaskItem (s));
332                                 }
333                         }
334                         finalArray = new ITaskItem [tempItems.Count];
335                         int i = 0;
336                         foreach (ITaskItem item in tempItems)
337                                 finalArray [i++] = item;
338                         return finalArray;
339                 }
340                 
341                 public Project Project {
342                         get { return project; }
343                 }
344                 
345                 public ItemReference ParentItemReference {
346                         get { return parentItemReference; }
347                 }
348         }
349
350         internal enum EvaluationState {
351                 Out,
352                 InItem,
353                 InMetadata,
354                 InProperty
355         }
356         
357         internal enum ParenState {
358                 Out,
359                 Left
360         }
361         
362         internal enum ApostropheState {
363                 In,
364                 Out
365         }
366         
367         internal enum ItemParsingState {
368                 Name,
369                 Transform1,
370                 Transform2,
371                 Separator
372         }
373 }