Merge pull request #1949 from lewurm/fixtype
[mono.git] / mcs / class / Microsoft.Build.Engine / Microsoft.Build.BuildEngine / ExpressionCollection.cs
index d10bc895520599b1b638602a18b9db27c8e7df1e..ca0034a10923b5ab756aa8c38ba1c96c82115856 100644 (file)
@@ -1,10 +1,12 @@
 //
-// ExpressionCollections.cs
+// ExpressionCollection.cs
 //
 // Author:
 //   Marek Sieradzki (marek.sieradzki@gmail.com)
+//   Ankit Jain (jankit@novell.com)
 // 
 // (C) 2006 Marek Sieradzki
+// Copyright 2009 Novell, Inc (http://www.novell.com)
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -25,8 +27,6 @@
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-#if NET_2_0
-
 using System;
 using System.Collections;
 using System.Collections.Generic;
@@ -39,25 +39,32 @@ namespace Microsoft.Build.BuildEngine {
        internal class ExpressionCollection {
        
                IList objects;
+               static Dictionary<string, bool> boolValues;
+
+               static ExpressionCollection ()
+               {
+                       string[] trueValuesArray = new string[] {"true", "on", "yes"};
+                       string[] falseValuesArray = new string[] {"false", "off", "no"};
+
+                       boolValues = new Dictionary<string, bool> (StringComparer.OrdinalIgnoreCase);
+                       foreach (string s in trueValuesArray)
+                               boolValues.Add (s, true);
+                       foreach (string s in falseValuesArray)
+                               boolValues.Add (s, false);
+               }
        
                public ExpressionCollection ()
                {
                        objects = new ArrayList ();
                }
-               
-               public void Add (ItemReference itemReference)
-               {
-                       objects.Add (itemReference);
-               }
-               
-               public void Add (MetadataReference metadataReference)
-               {
-                       objects.Add (metadataReference);
+
+               public int Count {
+                       get { return objects.Count; }
                }
                
-               public void Add (PropertyReference propertyReference)
+               public void Add (IReference reference)
                {
-                       objects.Add (propertyReference);
+                       objects.Add (reference);
                }
                
                public void Add (string s)
@@ -65,18 +72,18 @@ namespace Microsoft.Build.BuildEngine {
                        objects.Add (s);
                }
                
-               public object ConvertTo (Project project, Type type)
+               public object ConvertTo (Project project, Type type, ExpressionOptions options)
                {
                        if (type.IsArray) {
                                if (type == typeof (ITaskItem[]))
-                                       return ConvertToITaskItemArray (project);
+                                       return ConvertToITaskItemArray (project, options);
                                else
-                                       return ConvertToArray (project, type);
+                                       return ConvertToArray (project, type, options);
                        } else {
                                if (type == typeof (ITaskItem))
-                                       return ConvertToITaskItem (project);
+                                       return ConvertToITaskItem (project, options);
                                else
-                                       return ConvertToNonArray (project, type);
+                                       return ConvertToNonArray (project, type, options);
                        }
                }
                
@@ -86,159 +93,199 @@ namespace Microsoft.Build.BuildEngine {
                                yield return o;
                }
                
-               object ConvertToNonArray (Project project, Type type)
+               object ConvertToNonArray (Project project, Type type, ExpressionOptions options)
                {
-                       return ConvertToObject (ConvertToString (project), type);
+                       return ConvertToObject (ConvertToString (project, options), type, options);
                }
 
-               object ConvertToArray (Project project, Type type)
+               object ConvertToArray (Project project, Type type, ExpressionOptions options)
                {
-                       string[] rawTable = ConvertToString (project).Split (';');
-                       int i = 0;
-                       
-                       if (type == typeof (bool[])) {
-                               bool[] array = new bool [rawTable.Length];
-                               foreach (string raw in rawTable)
-                                       array [i++] = (bool) ConvertToObject (raw, type.GetElementType ());
-                               return array;
-                       } else if (type == typeof (string[])) {
-                               string[] array = new string [rawTable.Length];
-                               foreach (string raw in rawTable)
-                                       array [i++] = (string) ConvertToObject (raw, type.GetElementType ());
-                               return array;
-                       } else if (type == typeof (int[])) {
-                               int[] array = new int [rawTable.Length];
-                               foreach (string raw in rawTable)
-                                       array [i++] = (int) ConvertToObject (raw, type.GetElementType ());
-                               return array;
-                       } else if (type == typeof (uint[])) {
-                               uint[] array = new uint [rawTable.Length];
-                               foreach (string raw in rawTable)
-                                       array [i++] = (uint) ConvertToObject (raw, type.GetElementType ());
-                               return array;
-                       } else if (type == typeof (DateTime[])) {
-                               DateTime[] array = new DateTime [rawTable.Length];
-                               foreach (string raw in rawTable)
-                                       array [i++] = (DateTime) ConvertToObject (raw, type.GetElementType ());
-                               return array;
-                       } else
-                               throw new Exception ("Invalid type");
+                       ITaskItem[] items = ConvertToITaskItemArray (project, options);
+
+                       Type element_type = type.GetElementType ();
+                       Array arr = Array.CreateInstance (element_type, items.Length);
+                       for (int i = 0; i < arr.Length; i ++)
+                               arr.SetValue (ConvertToObject (items [i].ItemSpec, element_type, options), i);
+                       return arr;
                }
-               
-               object ConvertToObject (string raw, Type type)
+
+               object ConvertToObject (string raw, Type type, ExpressionOptions options)
                {
                        if (type == typeof (bool)) {
-                               return Boolean.Parse (raw);
-                       } else if (type == typeof (string)) {
+                               bool value;
+                               if (boolValues.TryGetValue (raw, out value))
+                                       return value;
+                               else
+                                       return false;
+                       }
+
+                       if (type == typeof (string))
                                return raw;
-                       } else if (type == typeof (int)) {
-                               return Int32.Parse (raw);
-                       } else if (type == typeof (uint)) {
-                               return UInt32.Parse (raw);
-                       } else if (type == typeof (DateTime)) {
+
+                       if (type.IsPrimitive)
+                               return Convert.ChangeType (raw, type);
+
+                       if (type == typeof (DateTime))
                                return DateTime.Parse (raw);
-                       } else {
-                               throw new Exception (String.Format ("Unknown type: {0}", type.ToString ()));
-                       }
+
+                       throw new Exception (String.Format ("Unsupported type: {0}", type));
                }
-               string ConvertToString (Project project)
+
+               string ConvertToString (Project project, ExpressionOptions options)
                {
                        StringBuilder sb = new StringBuilder ();
                        
                        foreach (object o in objects) {
-                               if (o is string) {
-                                       sb.Append ((string) o);
-                               } else if (o is ItemReference) {
-                                       ItemReference ir = (ItemReference) o;
-                                       sb.Append (ir.ConvertToString (project));
-                               } else if (o is PropertyReference) {
-                                       PropertyReference pr = (PropertyReference) o;
-                                       sb.Append (pr.ConvertToString (project));
-                               } else if (o is MetadataReference) {
-                                       // FIXME: we don't handle them yet
-                               } else {
-                                       throw new Exception ("Invalid type in objects collection.");
+                               string s = o as string;
+                               if (s != null) {
+                                       sb.Append (s);
+                                       continue;
                                }
+
+                               IReference br = o as IReference;
+                               if (br != null)
+                                       sb.Append (br.ConvertToString (project, options));
+                               else
+                                       throw new Exception ("BUG: Invalid type in objects collection.");
                        }
                        return sb.ToString ();
                }
 
-               ITaskItem ConvertToITaskItem (Project project)
+               ITaskItem ConvertToITaskItem (Project project, ExpressionOptions options)
                {
-                       ITaskItem item;
-                       
                        if (objects == null)
                                throw new Exception ("Cannot cast empty expression to ITaskItem.");
-                       
-                       if (objects [0] is ItemReference) {
-                               ItemReference ir = (ItemReference) objects [0];
-                               ITaskItem[] array = ir.ConvertToITaskItemArray (project);
-                               if (array.Length == 1) {
-                                       return array [0];
-                               } else {
-                                       throw new Exception ("TaskItem array too long");
-                               }
-                       } else {
-                               item = new TaskItem (ConvertToString (project));
-                               return item;
-                       }
+
+                       ITaskItem[] items = ConvertToITaskItemArray (project, options);
+                       if (items.Length > 1)
+                               //FIXME: msbuild gives better errors
+                               throw new Exception (String.Format ("Exactly one item required, but got: {0}", items.Length));
+
+                       if (items.Length == 0) return null;
+                       return items [0];
                }
                
-               ITaskItem[] ConvertToITaskItemArray (Project project)
+               // Concat rules (deduced)
+               // - ItemRef can concat only with a string ';' or PropertyRef ending in ';'
+               // - MetadataRef can concat with anything other than ItemRef
+               // - PropertyRef cannot be right after a ItemRef
+               //   PropertyRef concats if it doesn't end in ';'
+               // - string cannot concat with ItemRef unless it is ';'.
+               //   string concats if it ends in ';'
+               ITaskItem[] ConvertToITaskItemArray (Project project, ExpressionOptions options)
                {
                        List <ITaskItem> finalItems = new List <ITaskItem> ();
-                       ArrayList tempItems = new ArrayList ();
-                       ITaskItem[] array;
                        
+                       object prev = null;
+                       bool prev_can_concat = false;
+
                        foreach (object o in objects) {
+                               bool can_concat = prev_can_concat;
+
+                               string str = o as string;
+                               if (str != null) {
+                                       string trimmed_str = str.Trim ();
+                                       if (!IsSemicolon (str) && trimmed_str.Length > 0 && prev != null && prev is ItemReference)
+                                               // non-empty, non-semicolon string after item ref
+                                               ThrowCantConcatError (prev, str);
+
+                                       if (trimmed_str.Length == 0 && prev is string && IsSemicolon ((string) prev)) {
+                                               // empty string after a ';', ignore it
+                                               continue;
+                                       }
+
+                                       // empty string _after_ a itemref, not an error
+                                       prev_can_concat = !(str.Length > 0 && str [str.Length - 1] == ';') && trimmed_str.Length > 0;
+                                       AddItemsToArray (finalItems,
+                                                       ConvertToITaskItemArrayFromString (str),
+                                                       can_concat);
+                                       prev = o;
+                                       continue;
+                               }
+
+                               IReference br = o as IReference;
+                               if (br == null)
+                                       throw new Exception ("BUG: Invalid type in objects collection.");
+
                                if (o is ItemReference) {
-                                       tempItems.Add (o);
-                               } else if (o is PropertyReference) {
-                                       PropertyReference pr = (PropertyReference) o;
-                                       tempItems.Add (pr.ConvertToString (project));
+                                       if (prev != null && !(prev is string && (string)prev == ";"))
+                                               ThrowCantConcatError (prev, br);
+
+                                       prev_can_concat = true;
                                } else if (o is MetadataReference) {
-                                       // FIXME: not handled yet
-                               } else if (o is string) {
-                                       tempItems.Add (o);
-                               } else {
-                                       throw new Exception ("Invalid type in objects collection.");
+                                       if (prev != null && prev is ItemReference)
+                                               ThrowCantConcatError (prev, br);
+
+                                       prev_can_concat = true;
+                               } else if (o is PropertyReference) {
+                                       if (prev != null && prev is ItemReference)
+                                               ThrowCantConcatError (prev, br);
+
+                                       string value = ((PropertyReference) o).GetValue (project);
+                                       prev_can_concat = !(value.Length > 0 && value [value.Length - 1] == ';');
                                }
+
+                               AddItemsToArray (finalItems, br.ConvertToITaskItemArray (project, options), can_concat);
+
+                               prev = o;
                        }
-                       foreach (object o in tempItems) {
-                               if (o is ItemReference) {
-                                       ItemReference ir = (ItemReference) o;
-                                       array = ir.ConvertToITaskItemArray (project);
-                                       if (array != null)
-                                               finalItems.AddRange (array);
-                               } else if (o is string) {
-                                       string s = (string) o;
-                                       array = ConvertToITaskItemArrayFromString (project, s);
-                                       finalItems.AddRange (array);
-                               } else {
-                                       throw new Exception ("Invalid type in tempItems collection.");
-                               }
+
+                       // Trim and Remove empty items
+                       List<ITaskItem> toRemove = new List<ITaskItem> ();
+                       for (int i = 0; i < finalItems.Count; i ++) {
+                               string s = finalItems [i].ItemSpec.Trim ();
+                               if (s.Length == 0)
+                                       toRemove.Add (finalItems [i]);
+                               else
+                                       finalItems [i].ItemSpec = s;
                        }
+                       foreach (ITaskItem ti in toRemove)
+                               finalItems.Remove (ti);
                        
                        return finalItems.ToArray ();
                }
+
+               // concat's first item in @items to last item in @list if @concat is true
+               // else just adds all @items to @list
+               void AddItemsToArray (List<ITaskItem> list, ITaskItem[] items, bool concat)
+               {
+                       if (items == null || items.Length == 0)
+                               return;
+
+                       int start_index = 1;
+                       if (concat && list.Count > 0)
+                               list [list.Count - 1].ItemSpec += items [0].ItemSpec;
+                       else
+                               start_index = 0;
+
+                       for (int i = start_index; i < items.Length; i ++)
+                               list.Add (items [i]);
+               }
                
-               ITaskItem[] ConvertToITaskItemArrayFromString (Project project, string source)
-               {
-                       ArrayList tempItems = new ArrayList ();
-                       ITaskItem[] finalArray;
-                       string[] splittedSource = source.Split (';');
-                       foreach (string s in splittedSource) {
-                               if (s != String.Empty) {
-                                       tempItems.Add (new TaskItem (s));
-                               }
-                       }
-                       finalArray = new ITaskItem [tempItems.Count];
-                       int i = 0;
-                       foreach (ITaskItem item in tempItems)
-                               finalArray [i++] = item;
-                       return finalArray;
+               ITaskItem [] ConvertToITaskItemArrayFromString (string source)
+               {
+                       List <ITaskItem> items = new List <ITaskItem> ();
+                       string [] splitSource = source.Split (new char [] {';'},
+                                       StringSplitOptions.RemoveEmptyEntries);
+
+                       foreach (string s in splitSource)
+                               items.Add (new TaskItem (s));
+
+                       return items.ToArray ();
                }
+
+               bool IsSemicolon (string str)
+               {
+                       return str != null && str.Length == 1 && str [0] == ';';
+               }
+
+               void ThrowCantConcatError (object first, object second)
+               {
+                       throw new Exception (String.Format (
+                                       "Can't concatenate Item list with other strings where an item list is " +
+                                       "expected ('{0}', '{1}'). Use semi colon to separate items.",
+                                       first.ToString (), second.ToString ()));
+               }
+
        }
 }
-
-#endif