X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2FMicrosoft.Build.Engine%2FMicrosoft.Build.BuildEngine%2FExpression.cs;h=0d7ad2fa5984896fe57d3b622bc8b6ceefddab82;hb=619dc08671b52a4173f0c621e9beae5fbabddb39;hp=998b53f6840f0c6cd8aff2f76f645582bd80b817;hpb=7e18ed47c9606f3981e7b18cbc238d6781843153;p=mono.git diff --git a/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/Expression.cs b/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/Expression.cs index 998b53f6840..0d7ad2fa598 100644 --- a/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/Expression.cs +++ b/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/Expression.cs @@ -28,350 +28,342 @@ #if NET_2_0 using System; +using System.IO; using System.Collections; +using System.Collections.Generic; using System.Text; -using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; +using System.Text.RegularExpressions; +using Mono.XBuild.Utilities; namespace Microsoft.Build.BuildEngine { + + // Properties and items are processed in two ways + // 1. Evaluate, Project calls evaluate on all the item and property groups. + // At this time, the items are fully expanded, all item and property + // references are expanded to get the item's value. + // Properties on the other hand, expand property refs, but _not_ + // item references. + // + // 2. After the 'evaluation' phase, this could be when executing a target/task, + // - Items : no expansion required, as they are already at final value + // - Properties: Item references get expanded now, in the context of the + // batching + // + // The enum ExpressionOptions is for specifying this expansion of item references. + // + // GroupingCollection.Evaluate, evaluates all properties and then items + internal class Expression { - IList objects; - Project project; - ItemReference parentItemReference; + ExpressionCollection expressionCollection; + + static Regex item_regex; + static Regex property_regex; + static Regex metadata_regex; - public Expression (Project project) + public Expression () { - this.objects = new ArrayList (); - this.project = project; + this.expressionCollection = new ExpressionCollection (); } - - public Expression (Project project, string source) - : this (project) + + public static T ParseAs (string expression, ParseOptions options, Project project) { - ParseSource (source); + Expression expr = new Expression (); + expr.Parse (expression, options); + return (T)expr.ConvertTo (project, typeof (T)); } - - public void ParseSource (string source) + + public static T ParseAs (string expression, ParseOptions options, Project project, ExpressionOptions exprOptions) { - // FIXME: change StringBuilder to substrings - if (source == null) - throw new ArgumentNullException ("source"); - - StringBuilder temp = new StringBuilder (); - CharEnumerator it = source.GetEnumerator (); - EvaluationState eState = EvaluationState.Out; - ParenState pState = ParenState.Out; - ApostropheState aState = ApostropheState.Out; - int start = 0; - int current = -1; - - while (it.MoveNext ()) { - current++; - switch (eState) { - case EvaluationState.Out: - switch (it.Current) { - case '@': - if (temp.Length > 0) { - objects.Add (temp.ToString ()); - temp = new StringBuilder (); - } - eState = EvaluationState.InItem; - start = current; - break; - case '$': - if (temp.Length > 0) { - objects.Add (temp.ToString ()); - temp = new StringBuilder (); - } - eState = EvaluationState.InProperty; - start = current; - break; - case '%': - if (temp.Length > 0) { - objects.Add (temp.ToString ()); - temp = new StringBuilder (); - } - eState = EvaluationState.InMetadata; - start = current; - break; - default: - temp.Append (it.Current); - if (current == source.Length - 1) - objects.Add (temp.ToString ()); - break; - } - break; - case EvaluationState.InItem: - switch (it.Current) { - case '(': - if (pState == ParenState.Out && aState == ApostropheState.Out) - pState = ParenState.Left; - else if (aState == ApostropheState.Out) - throw new Exception ("'(' not expected."); - break; - case ')': - if (pState == ParenState.Left && aState == ApostropheState.Out) { - objects.Add (new ItemReference (this, source.Substring (start, current - start + 1))); - eState = EvaluationState.Out; - pState = ParenState.Out; - } - break; - case '\'': - if (aState == ApostropheState.In) - aState = ApostropheState.Out; - else - aState = ApostropheState.In; - break; - default: - break; - } - break; - case EvaluationState.InProperty: - switch (it.Current) { - case '(': - if (pState == ParenState.Out) - pState = ParenState.Left; - else - throw new Exception ("'(' expected."); - break; - case ')': - if (pState == ParenState.Left) { - objects.Add (new PropertyReference (this, source.Substring (start, current - start + 1))); - eState = EvaluationState.Out; - pState = ParenState.Out; - } - break; - default: - break; - } - break; - case EvaluationState.InMetadata: - switch (it.Current) { - case '(': - if (pState == ParenState.Out) - pState = ParenState.Left; - break; - case ')': - if (pState == ParenState.Left) { - objects.Add (new MetadataReference (this, source.Substring (start, current - start + 1))); - eState = EvaluationState.Out; - pState = ParenState.Out; - } - break; - default: - break; - } - break; - default: - throw new Exception ("Invalid evaluation state."); - } - } + Expression expr = new Expression (); + expr.Parse (expression, options); + return (T)expr.ConvertTo (project, typeof (T), exprOptions); } - public IEnumerator GetEnumerator () + // Split: Split on ';' + // Eg. Property values don't need to be split + // + // AllowItems: if false, item refs should not be treated as item refs! + // it converts them to strings in the final expressionCollection + // + // AllowMetadata: same as AllowItems, for metadata + public void Parse (string expression, ParseOptions options) { - foreach (object o in objects) - yield return o; - } + bool split = (options & ParseOptions.Split) == ParseOptions.Split; + bool allowItems = (options & ParseOptions.AllowItems) == ParseOptions.AllowItems; + bool allowMd = (options & ParseOptions.AllowMetadata) == ParseOptions.AllowMetadata; + + expression = expression.Replace ('\\', Path.DirectorySeparatorChar); - public object ToNonArray (Type type) - { - if (type.IsArray == true) - throw new ArgumentException ("Type specified can not be array type."); - - return ToObject (ToString (), type); + string [] parts; + if (split) + parts = expression.Split (new char [] {';'}, StringSplitOptions.RemoveEmptyEntries); + else + parts = new string [] { expression }; + + List p1 = new List (parts.Length); + List p2 = new List (parts.Length); + List p3 = new List (parts.Length); + + Prepare (p1, parts.Length); + Prepare (p2, parts.Length); + Prepare (p3, parts.Length); + + for (int i = 0; i < parts.Length; i++) + p1 [i] = SplitItems (parts [i], allowItems); + + for (int i = 0; i < parts.Length; i++) { + p2 [i] = new ArrayList (); + foreach (object o in p1 [i]) { + if (o is string) + p2 [i].AddRange (SplitProperties ((string) o)); + else + p2 [i].Add (o); + } + } + + for (int i = 0; i < parts.Length; i++) { + p3 [i] = new ArrayList (); + foreach (object o in p2 [i]) { + if (o is string) + p3 [i].AddRange (SplitMetadata ((string) o)); + else + p3 [i].Add (o); + } + } + + CopyToExpressionCollection (p3, allowItems, allowMd); } - - public object ToArray (Type type) + + void Prepare (List l, int length) { - if (type.IsArray == false) - throw new ArgumentException ("Type specified can not be element type."); - - string[] rawTable = ToString ().Split (';'); - int i = 0; - - if (type == typeof (bool[])) { - bool[] array = new bool [rawTable.Length]; - foreach (string raw in rawTable) - array [i++] = (bool) ToObject (raw, typeof (bool)); - return array; - } else if (type == typeof (string[])) { - string[] array = new string [rawTable.Length]; - foreach (string raw in rawTable) - array [i++] = (string) ToObject (raw, typeof (string)); - return array; - } else if (type == typeof (int[])) { - int[] array = new int [rawTable.Length]; - foreach (string raw in rawTable) - array [i++] = (int) ToObject (raw, typeof (int)); - return array; - } else if (type == typeof (uint[])) { - uint[] array = new uint [rawTable.Length]; - foreach (string raw in rawTable) - array [i++] = (uint) ToObject (raw, typeof (uint)); - return array; - } else if (type == typeof (DateTime[])) { - DateTime[] array = new DateTime [rawTable.Length]; - foreach (string raw in rawTable) - array [i++] = (DateTime) ToObject (raw, typeof (DateTime)); - return array; - } else throw new Exception ("Invalid type."); + for (int i = 0; i < length; i++) + l.Add (null); } - private object ToObject (string raw, Type type) + void CopyToExpressionCollection (List lists, bool allowItems, bool allowMd) { - if (type == typeof (bool)) { - return Boolean.Parse (raw); - } else 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)) { - return DateTime.Parse (raw); - } else { - throw new Exception (String.Format ("Unknown type: {0}", type.ToString ())); + for (int i = 0; i < lists.Count; i++) { + foreach (object o in lists [i]) { + if (o is string) + expressionCollection.Add (MSBuildUtils.Unescape ((string) o)); + else if (!allowItems && o is ItemReference) + expressionCollection.Add (((ItemReference) o).OriginalString); + else if (!allowMd && o is MetadataReference) { + expressionCollection.Add (((MetadataReference) o).OriginalString); + } + else if (o is IReference) + expressionCollection.Add ((IReference) o); + } + if (i < lists.Count - 1) + expressionCollection.Add (";"); } } - - private new string ToString () + + ArrayList SplitItems (string text, bool allowItems) { - StringBuilder sb = new StringBuilder (); - - foreach (object o in this) { - if (o is string) { - sb.Append ((string) o); - } else if (o is ItemReference) { - sb.Append (((ItemReference)o).ToString ()); - } else if (o is PropertyReference) { - sb.Append (((PropertyReference)o).ToString ()); - } else if (o is MetadataReference) { - // FIXME: we don't handle them yet - } else { - throw new Exception ("Invalid type in objects collection."); + ArrayList phase1 = new ArrayList (); + Match m; + m = ItemRegex.Match (text); + + while (m.Success) { + string name = null, transform = null, separator = null; + ItemReference ir; + + name = m.Groups [ItemRegex.GroupNumberFromName ("itemname")].Value; + + if (m.Groups [ItemRegex.GroupNumberFromName ("has_transform")].Success) + transform = m.Groups [ItemRegex.GroupNumberFromName ("transform")].Value; + + if (m.Groups [ItemRegex.GroupNumberFromName ("has_separator")].Success) + separator = m.Groups [ItemRegex.GroupNumberFromName ("separator")].Value; + + ir = new ItemReference (text.Substring (m.Groups [0].Index, m.Groups [0].Length), + name, transform, separator, m.Groups [0].Index, m.Groups [0].Length); + phase1.Add (ir); + m = m.NextMatch (); + } + + ArrayList phase2 = new ArrayList (); + int last_end = -1; + int end = text.Length - 1; + + foreach (ItemReference ir in phase1) { + int a,b; + + a = last_end; + b = ir.Start; + + if (b - a - 1 > 0) { + phase2.Add (text.Substring (a + 1, b - a - 1)); } + + last_end = ir.End; + phase2.Add (ir); } - return sb.ToString (); + + if (last_end < end) + phase2.Add (text.Substring (last_end + 1, end - last_end)); + + return phase2; } - public ITaskItem ToITaskItem () + ArrayList SplitProperties (string text) { - 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.ToITaskItemArray (); - if (array.Length == 1) { - return array [0]; - } else { - throw new Exception ("TaskItem array too long"); + ArrayList phase1 = new ArrayList (); + Match m; + m = PropertyRegex.Match (text); + + while (m.Success) { + string name = null; + PropertyReference pr; + + name = m.Groups [PropertyRegex.GroupNumberFromName ("name")].Value; + + pr = new PropertyReference (name, m.Groups [0].Index, m.Groups [0].Length); + phase1.Add (pr); + m = m.NextMatch (); + } + + ArrayList phase2 = new ArrayList (); + int last_end = -1; + int end = text.Length - 1; + + foreach (PropertyReference pr in phase1) { + int a,b; + + a = last_end; + b = pr.Start; + + if (b - a - 1 > 0) { + phase2.Add (text.Substring (a + 1, b - a - 1)); } - } else { - item = new TaskItem (ToString ()); - return item; + + last_end = pr.End; + phase2.Add (pr); } + + if (last_end < end) + phase2.Add (text.Substring (last_end + 1, end - last_end)); + + return phase2; } - - public ITaskItem[] ToITaskItemArray () + + ArrayList SplitMetadata (string text) { - ArrayList finalItems = new ArrayList (); - ArrayList tempItems = new ArrayList (); - ITaskItem[] array; - ITaskItem[] finalArray; - - foreach (object o in objects) { - if (o is ItemReference) { - tempItems.Add (o); - } else if (o is PropertyReference) { - PropertyReference pr = (PropertyReference) o; - tempItems.Add (pr.ToString ()); - } 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."); - } + ArrayList phase1 = new ArrayList (); + Match m; + m = MetadataRegex.Match (text); + + while (m.Success) { + string name = null, meta = null; + MetadataReference mr; + + if (m.Groups [MetadataRegex.GroupNumberFromName ("name")].Success) + name = m.Groups [MetadataRegex.GroupNumberFromName ("name")].Value; + + meta = m.Groups [MetadataRegex.GroupNumberFromName ("meta")].Value; + + mr = new MetadataReference (text.Substring (m.Groups [0].Index, m.Groups [0].Length), + name, meta, m.Groups [0].Index, m.Groups [0].Length); + phase1.Add (mr); + m = m.NextMatch (); } - foreach (object o in tempItems) { - if (o is ItemReference) { - ItemReference ir = (ItemReference) o; - array = ir.ToITaskItemArray (); - if (array != null) - foreach (ITaskItem item in array) - finalItems.Add (item); - } else if (o is string) { - string s = (string) o; - array = ITaskItemArrayFromString (s); - foreach (ITaskItem item in array) - finalItems.Add (item); - } else { - throw new Exception ("Invalid type in tempItems collection."); + + ArrayList phase2 = new ArrayList (); + int last_end = -1; + int end = text.Length - 1; + + foreach (MetadataReference mr in phase1) { + int a,b; + + a = last_end; + b = mr.Start; + + if (b - a - 1> 0) { + phase2.Add (text.Substring (a + 1, b - a - 1)); } + + last_end = mr.End; + phase2.Add (mr); } - - finalArray = new ITaskItem [finalItems.Count]; - int i = 0; - foreach (ITaskItem item in finalItems) - finalArray [i++] = item; - return finalArray; + + if (last_end < end) + phase2.Add (text.Substring (last_end + 1, end - last_end)); + + return phase2; } - - // FIXME: quite stupid name - private ITaskItem[] ITaskItemArrayFromString (string source) + + public object ConvertTo (Project project, Type type) { - ArrayList tempItems = new ArrayList (); - ITaskItem[] finalArray; - string[] splittedSource = source.Split (';'); - foreach (string s in splittedSource) { - if (s != String.Empty) { - tempItems.Add (new TaskItem (s)); - } + return ConvertTo (project, type, ExpressionOptions.ExpandItemRefs); + } + + public object ConvertTo (Project project, Type type, ExpressionOptions options) + { + return expressionCollection.ConvertTo (project, type, options); + } + + public ExpressionCollection Collection { + get { return expressionCollection; } + } + + static Regex ItemRegex { + get { + if (item_regex == null) + item_regex = new Regex ( + @"@\(\s*" + + @"(?[_A-Za-z][_\-0-9a-zA-Z]*)" + + @"(?\s*->\s*'(?[^']*)')?" + + @"(?\s*,\s*'(?[^']*)')?" + + @"\s*\)"); + return item_regex; } - finalArray = new ITaskItem [tempItems.Count]; - int i = 0; - foreach (ITaskItem item in tempItems) - finalArray [i++] = item; - return finalArray; } - - public Project Project { - get { return project; } + + static Regex PropertyRegex { + get { + if (property_regex == null) + property_regex = new Regex ( + @"\$\(\s*" + + @"(?[_a-zA-Z][_\-0-9a-zA-Z]*)" + + @"\s*\)"); + return property_regex; + } } - - public ItemReference ParentItemReference { - get { return parentItemReference; } + + static Regex MetadataRegex { + get { + if (metadata_regex == null) + metadata_regex = new Regex ( + @"%\(\s*" + + @"((?[_a-zA-Z][_\-0-9a-zA-Z]*)\.)?" + + @"(?[_a-zA-Z][_\-0-9a-zA-Z]*)" + + @"\s*\)"); + return metadata_regex; + } } } - internal enum EvaluationState { - Out, - InItem, - InMetadata, - InProperty - } - - internal enum ParenState { - Out, - Left - } - - internal enum ApostropheState { - In, - Out + [Flags] + enum ParseOptions { + // absence of one of these flags, means + // false for that option + AllowItems = 0x1, + Split = 0x2, + AllowMetadata = 0x4, + + None = 0x8, // == no items, no metadata, and no split + + // commonly used options + AllowItemsMetadataAndSplit = AllowItems | Split | AllowMetadata, + AllowItemsNoMetadataAndSplit = AllowItems | Split } - - internal enum ItemParsingState { - Name, - Transform1, - Transform2, - Separator + + enum ExpressionOptions { + ExpandItemRefs, + DoNotExpandItemRefs } } -#endif \ No newline at end of file +#endif