//
-// 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
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)
objects.Add (s);
}
- public object ConvertTo (Type type)
+ public object ConvertTo (Project project, Type type, ExpressionOptions options)
{
if (type.IsArray) {
if (type == typeof (ITaskItem[]))
- return ConvertToITaskItemArray ();
+ return ConvertToITaskItemArray (project, options);
else
- return ConvertToArray (type);
+ return ConvertToArray (project, type, options);
} else {
if (type == typeof (ITaskItem))
- return ConvertToITaskItem ();
+ return ConvertToITaskItem (project, options);
else
- return ConvertToNonArray (type);
+ return ConvertToNonArray (project, type, options);
}
}
yield return o;
}
- object ConvertToNonArray (Type type)
+ object ConvertToNonArray (Project project, Type type, ExpressionOptions options)
{
- return ConvertToObject (ConvertToString (), type);
+ return ConvertToObject (ConvertToString (project, options), type, options);
}
- object ConvertToArray (Type type)
+ object ConvertToArray (Project project, Type type, ExpressionOptions options)
{
- string[] rawTable = ConvertToString ().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 ()
+
+ 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) {
- sb.Append (((ItemReference)o).ConvertToString ());
- } else if (o is PropertyReference) {
- sb.Append (((PropertyReference)o).ConvertToString ());
- } 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 ()
+ 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 ();
- if (array.Length == 1) {
- return array [0];
- } else {
- throw new Exception ("TaskItem array too long");
- }
- } else {
- item = new TaskItem (ConvertToString ());
- 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 ()
+ // 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 ());
+ 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 ();
- if (array != null)
- foreach (ITaskItem item in array)
- finalItems.Add (item);
- } else if (o is string) {
- string s = (string) o;
- array = ConvertToITaskItemArrayFromString (s);
- foreach (ITaskItem item in array)
- finalItems.Add (item);
- } 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 (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 ()));
+ }
+
}
}