[xbuild] Expression.ParseAs<T> - new method
[mono.git] / mcs / class / Microsoft.Build.Engine / Microsoft.Build.BuildEngine / Expression.cs
index 460b3bd57196b4303a563c07b5e342e2ca0387e1..0d7ad2fa5984896fe57d3b622bc8b6ceefddab82 100644 (file)
@@ -33,8 +33,26 @@ using System.Collections;
 using System.Collections.Generic;
 using System.Text;
 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 {
        
                ExpressionCollection expressionCollection;
@@ -48,12 +66,40 @@ namespace Microsoft.Build.BuildEngine {
                        this.expressionCollection = new ExpressionCollection ();
                }
 
-               public void Parse (string expression, bool allowItems)
+               public static T ParseAs<T> (string expression, ParseOptions options, Project project)
+               {
+                       Expression expr = new Expression ();
+                       expr.Parse (expression, options);
+                       return (T)expr.ConvertTo (project, typeof (T));
+               }
+
+               public static T ParseAs<T> (string expression, ParseOptions options, Project project, ExpressionOptions exprOptions)
                {
-                       expression = expression.Replace ('/', Path.DirectorySeparatorChar);
+                       Expression expr = new Expression ();
+                       expr.Parse (expression, options);
+                       return (T)expr.ConvertTo (project, typeof (T), exprOptions);
+               }
+
+               // 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)
+               {
+                       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);
                
-                       string [] parts = expression.Split (';');
+                       string [] parts;
+                       if (split)
+                               parts = expression.Split (new char [] {';'}, StringSplitOptions.RemoveEmptyEntries);
+                       else
+                               parts = new string [] { expression };
 
                        List <ArrayList> p1 = new List <ArrayList> (parts.Length);
                        List <ArrayList> p2 = new List <ArrayList> (parts.Length);
@@ -86,7 +132,7 @@ namespace Microsoft.Build.BuildEngine {
                                }
                        }
 
-                       CopyToExpressionCollection (p3);
+                       CopyToExpressionCollection (p3, allowItems, allowMd);
                }
 
                void Prepare (List <ArrayList> l, int length)
@@ -95,18 +141,19 @@ namespace Microsoft.Build.BuildEngine {
                                l.Add (null);
                }
                
-               void CopyToExpressionCollection (List <ArrayList> lists)
+               void CopyToExpressionCollection (List <ArrayList> lists, bool allowItems, bool allowMd)
                {
                        for (int i = 0; i < lists.Count; i++) {
                                foreach (object o in lists [i]) {
                                        if (o is string)
-                                               expressionCollection.Add (Utilities.Unescape ((string) o));
-                                       else if (o is ItemReference)
-                                               expressionCollection.Add ((ItemReference) o);
-                                       else if (o is PropertyReference)
-                                               expressionCollection.Add ((PropertyReference) o);
-                                       else if (o is MetadataReference)
-                                               expressionCollection.Add ((MetadataReference) o);
+                                               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 (";");
@@ -115,13 +162,6 @@ namespace Microsoft.Build.BuildEngine {
 
                ArrayList SplitItems (string text, bool allowItems)
                {
-                       if (!allowItems) {
-                               // FIXME: it's probably larger than 1
-                               ArrayList l = new ArrayList ();
-                               l.Add (text);
-                               return l;
-                       }
-
                        ArrayList phase1 = new ArrayList ();
                        Match m;
                        m = ItemRegex.Match (text);
@@ -138,7 +178,8 @@ namespace Microsoft.Build.BuildEngine {
                                if (m.Groups [ItemRegex.GroupNumberFromName ("has_separator")].Success)
                                        separator = m.Groups [ItemRegex.GroupNumberFromName ("separator")].Value;
 
-                               ir = new ItemReference (name, transform, separator, m.Groups [0].Index, m.Groups [0].Length);
+                               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 ();
                        }
@@ -223,7 +264,8 @@ namespace Microsoft.Build.BuildEngine {
                                
                                meta = m.Groups [MetadataRegex.GroupNumberFromName ("meta")].Value;
                                
-                               mr = new MetadataReference (name, meta, m.Groups [0].Index, m.Groups [0].Length);
+                               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 ();
                        }
@@ -251,10 +293,15 @@ namespace Microsoft.Build.BuildEngine {
 
                        return phase2;
                }
-               
+
                public object ConvertTo (Project project, Type type)
                {
-                       return expressionCollection.ConvertTo (project, type);
+                       return ConvertTo (project, type, ExpressionOptions.ExpandItemRefs);
+               }
+
+               public object ConvertTo (Project project, Type type, ExpressionOptions options)
+               {
+                       return expressionCollection.ConvertTo (project, type, options);
                }
 
                public ExpressionCollection Collection {
@@ -266,7 +313,7 @@ namespace Microsoft.Build.BuildEngine {
                                if (item_regex == null)
                                        item_regex = new Regex (
                                                @"@\(\s*"
-                                               + @"(?<itemname>[_A-Za-z][_0-9a-zA-Z]*)"
+                                               + @"(?<itemname>[_A-Za-z][_\-0-9a-zA-Z]*)"
                                                + @"(?<has_transform>\s*->\s*'(?<transform>[^']*)')?"
                                                + @"(?<has_separator>\s*,\s*'(?<separator>[^']*)')?"
                                                + @"\s*\)");
@@ -279,7 +326,7 @@ namespace Microsoft.Build.BuildEngine {
                                if (property_regex == null)
                                        property_regex = new Regex (
                                                @"\$\(\s*"
-                                               + @"(?<name>[_a-zA-Z][_0-9a-zA-Z]*)"
+                                               + @"(?<name>[_a-zA-Z][_\-0-9a-zA-Z]*)"
                                                + @"\s*\)");
                                return property_regex;
                        }
@@ -290,13 +337,33 @@ namespace Microsoft.Build.BuildEngine {
                                if (metadata_regex == null)
                                        metadata_regex = new Regex (
                                                @"%\(\s*"
-                                               + @"((?<name>[_a-zA-Z][_0-9a-zA-Z]*)\.)?"
-                                               + @"(?<meta>[_a-zA-Z][_0-9a-zA-Z]*)"
+                                               + @"((?<name>[_a-zA-Z][_\-0-9a-zA-Z]*)\.)?"
+                                               + @"(?<meta>[_a-zA-Z][_\-0-9a-zA-Z]*)"
                                                + @"\s*\)");
                                return metadata_regex;
                        }
                }
        }
+
+       [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
+       }
+
+       enum ExpressionOptions {
+               ExpandItemRefs,
+               DoNotExpandItemRefs
+       }
 }
 
 #endif