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