Example #1
0
        /// <summary>
        /// Get the value of a variable or model.
        /// </summary>
        /// <param name="namePath">The name of the object to return</param>
        /// <param name="relativeTo">The model calling this method</param>
        /// <param name="ignoreCase">If true, ignore case when searching for the object or property</param>
        /// <returns>The found object or null if not found</returns>
        public IVariable GetInternal(string namePath, Model relativeTo, bool ignoreCase = false)
        {
            Model            relativeToModel = relativeTo;
            string           cacheKey        = namePath;
            StringComparison compareType     = ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;

            // Look in cache first.
            object value = GetFromCache(cacheKey, relativeToModel);

            if (value != null)
            {
                return(value as IVariable);
            }

            IVariable returnVariable = null;

            if (namePath == null || namePath.Length == 0)
            {
                return(null);
            }
            else if (namePath[0] != '.' &&
                     namePath.Replace("()", "").IndexOfAny("(+*/".ToCharArray()) != -1)
            {
                // expression - need a better way of detecting an expression
                returnVariable = new VariableExpression(namePath, relativeToModel);
            }
            else
            {
                // Remove a square bracketed model name and change our relativeTo model to
                // the referenced model.
                if (namePath.StartsWith("["))
                {
                    int posCloseBracket = namePath.IndexOf(']');
                    if (posCloseBracket == -1)
                    {
                        return(null);
                    }
                    string modelName = namePath.Substring(1, posCloseBracket - 1);
                    namePath = namePath.Remove(0, posCloseBracket + 1);
                    Model foundModel = Apsim.Find(relativeToModel, modelName) as Model;
                    if (foundModel == null)
                    {
                        // Didn't find a model with a name matching the square bracketed string so
                        // now try and look for a model with a type matching the square bracketed string.
                        Type[] modelTypes = GetTypeWithoutNameSpace(modelName);
                        if (modelTypes.Length == 1)
                        {
                            foundModel = Apsim.Find(relativeToModel, modelTypes[0]) as Model;
                        }
                    }
                    if (foundModel == null)
                    {
                        return(null);
                    }
                    else
                    {
                        relativeToModel = foundModel;
                    }
                }
                else if (namePath.StartsWith("."))
                {
                    // Absolute path
                    Model root = relativeToModel;
                    while (root.Parent != null)
                    {
                        root = root.Parent as Model;
                    }
                    relativeToModel = root;

                    int posPeriod = namePath.IndexOf('.', 1);
                    if (posPeriod == -1)
                    {
                        // Path starts with a . and only contains a single period.
                        // If name matches then no problem. Otherwise we need to return null.
                        posPeriod = namePath.IndexOf('.');
                        if (namePath.Remove(0, posPeriod) == relativeToModel.Name)
                        {
                            posPeriod = namePath.Length;
                        }
                    }

                    namePath = namePath.Remove(0, posPeriod);
                    if (namePath.StartsWith("."))
                    {
                        namePath = namePath.Remove(0, 1);
                    }
                }

                // Now walk the series of '.' separated path bits, assuming the path bits
                // are child models. Stop when we can't find the child model.
                string[] namePathBits = namePath.Split(".".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                int      i;
                for (i = 0; i < namePathBits.Length; i++)
                {
                    IModel localModel = relativeToModel.Children.FirstOrDefault(m => m.Name.Equals(namePathBits[i], compareType));
                    if (localModel == null)
                    {
                        // Allow for the possibility that the first path element may point to
                        // the starting parent model, rather than to a child within that model
                        if ((i == 0) && relativeToModel.Name.Equals(namePathBits[i], compareType))
                        {
                            continue;
                        }
                        else
                        {
                            break;
                        }
                    }
                    else
                    {
                        relativeToModel = localModel as Model;
                    }
                }

                // At this point there are only 2 possibilities. We have encountered a
                // PropertyInfo or the path is invalid.
                // We now need to loop through the remaining path bits and keep track of each
                // section of the path as each section will have to be evaulated everytime a
                // a get is done for this path.
                // The variable 'i' will point to the name path that cannot be found as a model.
                object           relativeToObject = relativeToModel;
                List <IVariable> properties       = new List <IVariable>();
                properties.Add(new VariableObject(relativeToModel));
                for (int j = i; j < namePathBits.Length; j++)
                {
                    // look for an array specifier e.g. sw[2]
                    string arraySpecifier = null;
                    if (namePathBits[j].Contains("["))
                    {
                        arraySpecifier = StringUtilities.SplitOffBracketedValue(ref namePathBits[j], '[', ']');
                    }

                    // Look for either a property or a child model.
                    IModel localModel = null;
                    if (relativeToObject == null)
                    {
                        return(null);
                    }
                    PropertyInfo propertyInfo = relativeToObject.GetType().GetProperty(namePathBits[j]);
                    if (propertyInfo == null && ignoreCase) // If not found, try using a case-insensitive search
                    {
                        propertyInfo = relativeToObject.GetType().GetProperty(namePathBits[j], BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.IgnoreCase);
                    }
                    if (relativeToObject is IFunction && namePathBits[j] == "Value()")
                    {
                        MethodInfo method = relativeToModel.GetType().GetMethod("Value");
                        properties.Add(new VariableMethod(relativeToModel, method));
                    }
                    else if (propertyInfo == null && relativeToObject is Model)
                    {
                        // Not a property, may be a child model.
                        localModel = (relativeToObject as IModel).Children.FirstOrDefault(m => m.Name.Equals(namePathBits[i], compareType));
                        if (localModel == null)
                        {
                            return(null);
                        }

                        properties.Add(new VariableObject(localModel));
                        relativeToObject = localModel;
                    }
                    else if (propertyInfo != null)
                    {
                        VariableProperty property = new VariableProperty(relativeToObject, propertyInfo, arraySpecifier);
                        properties.Add(property);
                        relativeToObject = property.Value;
                    }
                    else if (relativeToObject is IList)
                    {
                        // Special case: we are trying to get a property of an array(IList). In this case
                        // we want to return the property value for all items in the array.
                        VariableProperty property = new VariableProperty(relativeToObject, namePathBits[j], arraySpecifier);
                        properties.Add(property);
                        relativeToObject = property.Value;
                    }
                    else
                    {
                        return(null);
                    }
                }

                // We now have a list of IVariable instances that can be evaluated to
                // produce a return value for the given path. Wrap the list into an IVariable.
                returnVariable = new VariableComposite(namePath, properties);
            }

            // Add variable to cache.
            AddToCache(cacheKey, relativeTo, returnVariable);

            return(returnVariable);
        }
Example #2
0
        /// <summary>
        /// Get the value of a variable or model.
        /// </summary>
        /// <param name="namePath">The name of the object to return</param>
        /// <param name="ignoreCase">If true, ignore case when searching for the object or property</param>
        /// <returns>The found object or null if not found</returns>
        private IVariable GetInternal(string namePath, bool ignoreCase = true)
        {
            IModel           relativeTo  = relativeToModel;
            string           cacheKey    = namePath;
            StringComparison compareType = ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;

            // Look in cache first.
            object value = null;

            if (cache.ContainsKey(cacheKey))
            {
                value = cache[cacheKey];
            }
            if (value != null)
            {
                return(value as IVariable);
            }

            IVariable returnVariable = null;

            if (namePath == null || namePath.Length == 0)
            {
                return(null);
            }
            else if (namePath[0] != '.' &&
                     (namePath.Replace("()", "").IndexOfAny("+*/".ToCharArray()) != -1
                      | (namePath.IndexOfAny("(".ToCharArray()) >= 0 && namePath.Substring(0, (namePath.IndexOf('(') >= 0? namePath.IndexOf('(') : 0)).IndexOfAny("[.".ToCharArray()) == -1)))
            {
                // expression - need a better way of detecting an expression
                returnVariable = new VariableExpression(namePath, relativeTo as Model);
            }
            else
            {
                namePath = namePath.Replace("Value()", "Value().");
                // Remove a square bracketed model name and change our relativeTo model to
                // the referenced model.
                if (namePath.StartsWith("["))
                {
                    int posCloseBracket = namePath.IndexOf(']');
                    if (posCloseBracket == -1)
                    {
                        return(null);
                    }
                    string modelName = namePath.Substring(1, posCloseBracket - 1);
                    namePath = namePath.Remove(0, posCloseBracket + 1);
                    Model foundModel = Apsim.Find(relativeTo, modelName) as Model;
                    if (foundModel == null)
                    {
                        // Didn't find a model with a name matching the square bracketed string so
                        // now try and look for a model with a type matching the square bracketed string.
                        Type[] modelTypes = GetTypeWithoutNameSpace(modelName);
                        if (modelTypes.Length == 1)
                        {
                            foundModel = Apsim.Find(relativeTo, modelTypes[0]) as Model;
                        }
                    }
                    if (foundModel == null)
                    {
                        return(null);
                    }
                    else
                    {
                        relativeTo = foundModel;
                    }
                }
                else if (namePath.StartsWith("."))
                {
                    // Absolute path
                    IModel root = relativeTo;
                    while (root.Parent != null)
                    {
                        root = root.Parent as Model;
                    }
                    relativeTo = root;

                    int posPeriod = namePath.IndexOf('.', 1);
                    if (posPeriod == -1)
                    {
                        posPeriod = namePath.Length;
                    }

                    namePath = namePath.Remove(0, posPeriod);
                    if (namePath.StartsWith("."))
                    {
                        namePath.Remove(1);
                    }
                }

                // Now walk the series of '.' separated path bits, assuming the path bits
                // are child models. Stop when we can't find the child model.
                string[] namePathBits = namePath.Split(".".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                int      i;
                for (i = 0; i < namePathBits.Length; i++)
                {
                    IModel localModel = relativeTo.Children.FirstOrDefault(m => m.Name.Equals(namePathBits[i], compareType) && m.Enabled);
                    if (localModel == null)
                    {
                        break;
                    }
                    else
                    {
                        relativeTo = localModel as Model;
                    }
                }

                // At this point there are only 2 possibilities. We have encountered a
                // PropertyInfo/MethodInfo or the path is invalid.
                // We now need to loop through the remaining path bits and keep track of each
                // section of the path as each section will have to be evaulated everytime a
                // a get is done for this path.
                // The variable 'i' will point to the name path that cannot be found as a model.
                object           relativeToObject = relativeTo;
                List <IVariable> properties       = new List <IVariable>();
                properties.Add(new VariableObject(relativeTo));
                for (int j = i; j < namePathBits.Length; j++)
                {
                    // look for an array specifier e.g. sw[2]
                    string arraySpecifier = null;
                    if (namePathBits[j].Contains("["))
                    {
                        arraySpecifier = StringUtilities.SplitOffBracketedValue(ref namePathBits[j], '[', ']');
                    }

                    // Look for either a property or a child model.
                    IModel localModel = null;
                    if (relativeToObject == null)
                    {
                        return(null);
                    }

                    // Check property info
                    PropertyInfo propertyInfo = relativeToObject.GetType().GetProperty(namePathBits[j]);
                    if (propertyInfo == null && ignoreCase) // If not found, try using a case-insensitive search
                    {
                        propertyInfo = relativeToObject.GetType().GetProperty(namePathBits[j], BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.IgnoreCase);
                    }

                    // If not property info
                    // Check method info
                    MethodInfo    methodInfo    = null;
                    List <object> argumentsList = null;
                    if (propertyInfo == null)
                    {
                        if (namePathBits[j].IndexOf('(') > 0)
                        {
                            relativeToObject.GetType().GetMethod(namePathBits[j].Substring(0, namePathBits[j].IndexOf('(')));
                            if (methodInfo == null && ignoreCase) // If not found, try using a case-insensitive search
                            {
                                methodInfo = relativeToObject.GetType().GetMethod(namePathBits[j].Substring(0, namePathBits[j].IndexOf('(')), BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.IgnoreCase);
                            }
                        }
                        if (methodInfo != null)
                        {
                            // get arguments and store in VariableMethod
                            string args = namePathBits[j].Substring(namePathBits[j].IndexOf('('));
                            args = args.Substring(0, args.IndexOf(')'));
                            args = args.Replace("(", "").Replace(")", "");
                            if (args.Length > 0)
                            {
                                argumentsList = new List <object>();
                                args          = args.Trim('(').Trim(')');
                                var argList = args.Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                                // get arguments
                                ParameterInfo[] pars = methodInfo.GetParameters();

                                for (int argid = 0; argid < argList.Length; argid++)
                                {
                                    var cleanArg = argList[argid].Trim(' ').Trim(new char[] { '(', ')' }).Trim(' ').Trim('"');
                                    switch (Type.GetTypeCode(pars[argid].ParameterType))
                                    {
                                    case TypeCode.Double:
                                        argumentsList.Add(Convert.ToDouble(cleanArg, CultureInfo.InvariantCulture));
                                        break;

                                    case TypeCode.Int32:
                                        argumentsList.Add(Convert.ToInt32(cleanArg, CultureInfo.InvariantCulture));
                                        break;

                                    case TypeCode.String:
                                        argumentsList.Add(cleanArg);
                                        break;

                                    default:
                                        break;
                                    }
                                }
                            }
                        }
                    }

                    //if (relativeToObject is IFunction && namePathBits[j] == "Value()")
                    //{
                    //    MethodInfo method = relativeTo.GetType().GetMethod("Value");
                    //    properties.Add(new VariableMethod(relativeTo, method));
                    //}
                    //                    else if (propertyInfo == null && methodInfo == null && relativeToObject is Model)

                    if (propertyInfo == null && methodInfo == null && relativeToObject is Model)
                    {
                        // Not a property, may be an unchecked method or a child model.
                        localModel = (relativeToObject as IModel).Children.FirstOrDefault(m => m.Name.Equals(namePathBits[j], compareType));
                        if (localModel == null)
                        {
                            // Not a model
                            return(null);
                        }
                        else
                        {
                            properties.Add(new VariableObject(localModel));
                            relativeToObject = localModel;
                        }
                    }
                    else if (propertyInfo != null)
                    {
                        VariableProperty property = new VariableProperty(relativeToObject, propertyInfo, arraySpecifier);
                        properties.Add(property);
                        relativeToObject = property.Value;
                    }
                    else if (methodInfo != null)
                    {
                        VariableMethod method = null;
                        if (argumentsList != null)
                        {
                            method = new VariableMethod(relativeToObject, methodInfo, argumentsList.ToArray <object>());
                        }
                        else
                        {
                            method = new VariableMethod(relativeToObject, methodInfo, null);
                        }
                        //                        VariableProperty property = new VariableProperty(relativeToObject, propertyInfo, arraySpecifier);
                        properties.Add(method);
                        relativeToObject = method.Value;
                    }
                    else if (relativeToObject is IList)
                    {
                        // Special case: we are trying to get a property of an array(IList). In this case
                        // we want to return the property value for all items in the array.
                        VariableProperty property = new VariableProperty(relativeToObject, namePathBits[j]);
                        properties.Add(property);
                        relativeToObject = property.Value;
                    }
                    else
                    {
                        return(null);
                    }
                }

                // We now have a list of IVariable instances that can be evaluated to
                // produce a return value for the given path. Wrap the list into an IVariable.
                returnVariable = new VariableComposite(namePath, properties);
            }

            // Add variable to cache.
            cache.Add(cacheKey, returnVariable);

            return(returnVariable);
        }
Example #3
0
        /// <summary>
        /// Get the value of a variable or model.
        /// </summary>
        /// <param name="namePath">The name of the object to return</param>
        /// <param name="relativeTo">The model calling this method</param>
        /// <param name="ignoreCase">If true, ignore case when searching for the object or property</param>
        /// <returns>The found object or null if not found</returns>
        public IVariable GetInternal(string namePath, Model relativeTo, bool ignoreCase = false)
        {
            Model relativeToModel = relativeTo;
            string cacheKey = namePath;
            StringComparison compareType = ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;

            // Look in cache first.
            object value = GetFromCache(cacheKey, relativeToModel);
            if (value != null)
            {
                return value as IVariable;
            }

            IVariable returnVariable = null;
            if (namePath == null || namePath.Length == 0)
            {
                return null;
            }
            else if (namePath[0] != '.' && namePath[0] != '.' &&
                     namePath.IndexOfAny("(+*/".ToCharArray()) != -1)
            {
                // expression - need a better way of detecting an expression
                returnVariable = new VariableExpression(namePath, relativeToModel);
            }
            else
            {
                // Remove a square bracketed model name and change our relativeTo model to
                // the referenced model.
                if (namePath.StartsWith("["))
                {
                    int posCloseBracket = namePath.IndexOf(']');
                    if (posCloseBracket == -1)
                    {
                        return null;
                    }
                    string modelName = namePath.Substring(1, posCloseBracket - 1);
                    namePath = namePath.Remove(0, posCloseBracket + 1);
                    Model foundModel = this.Find(modelName, relativeToModel);
                    if (foundModel == null)
                    {
                        // Didn't find a model with a name matching the square bracketed string so
                        // now try and look for a model with a type matching the square bracketed string.
                        Type[] modelTypes = GetTypeWithoutNameSpace(modelName);
                        if (modelTypes.Length == 1)
                            foundModel = this.Find(modelTypes[0], relativeToModel);
                    }
                    if (foundModel == null)
                        return null;
                    else
                        relativeToModel = foundModel;
                }
                else if (namePath.StartsWith("."))
                {
                    // Absolute path
                    Model root = relativeToModel;
                    while (root.Parent != null)
                    {
                        root = root.Parent as Model;
                    }
                    relativeToModel = root;

                    int posPeriod = namePath.IndexOf('.', 1);
                    if (posPeriod == -1)
                    {
                        posPeriod = namePath.Length;
                    }

                    namePath = namePath.Remove(0, posPeriod);
                    if (namePath.StartsWith("."))
                    {
                        namePath.Remove(1);
                    }
                }

                // Now walk the series of '.' separated path bits, assuming the path bits
                // are child models. Stop when we can't find the child model.
                string[] namePathBits = namePath.Split(".".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                int i;
                for (i = 0; i < namePathBits.Length; i++)
                {
                    IModel localModel = relativeToModel.Children.FirstOrDefault(m => m.Name.Equals(namePathBits[i], compareType));
                    if (localModel == null)
                    {
                        break;
                    }
                    else
                    {
                        relativeToModel = localModel as Model;
                    }
                }

                // At this point there are only 2 possibilities. We have encountered a
                // PropertyInfo or the path is invalid.
                // We now need to loop through the remaining path bits and keep track of each
                // section of the path as each section will have to be evaulated everytime a
                // a get is done for this path.
                // The variable 'i' will point to the name path that cannot be found as a model.
                object relativeToObject = relativeToModel;
                List<IVariable> properties = new List<IVariable>();
                properties.Add(new VariableObject(relativeToModel));
                for (int j = i; j < namePathBits.Length; j++)
                {
                    // look for an array specifier e.g. sw[2]
                    string arraySpecifier = null;
                    if (namePathBits[j].Contains("["))
                    {
                        arraySpecifier = StringUtilities.SplitOffBracketedValue(ref namePathBits[j], '[', ']');
                    }

                    // Look for either a property or a child model.
                    IModel localModel = null;
                    if (relativeToObject == null)
                        return null;
                    PropertyInfo propertyInfo = relativeToObject.GetType().GetProperty(namePathBits[j]);
                    if (propertyInfo == null && ignoreCase) // If not found, try using a case-insensitive search
                        propertyInfo = relativeToObject.GetType().GetProperty(namePathBits[j], BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.IgnoreCase);
                    if (propertyInfo == null && relativeToObject is Model)
                    {
                        // Not a property, may be a child model.
                        localModel = (relativeToObject as IModel).Children.FirstOrDefault(m => m.Name.Equals(namePathBits[i], compareType));
                        if (localModel == null)
                        {
                            return null;
                        }

                        properties.Add(new VariableObject(localModel));
                        relativeToObject = localModel;
                    }
                    else if (propertyInfo != null)
                    {
                        VariableProperty property = new VariableProperty(relativeToObject, propertyInfo, arraySpecifier);
                        properties.Add(property);
                        relativeToObject = property.Value;
                    }
                    else if (relativeToObject is IList)
                    {
                        // Special case: we are trying to get a property of an array(IList). In this case
                        // we want to return the property value for all items in the array.
                        VariableProperty property = new VariableProperty(relativeToObject, namePathBits[j]);
                        properties.Add(property);
                        relativeToObject = property.Value;
                    }
                    else
                    {
                        return null;
                    }
                }

                // We now have a list of IVariable instances that can be evaluated to
                // produce a return value for the given path. Wrap the list into an IVariable.
                returnVariable = new VariableComposite(namePath, properties);
            }

            // Add variable to cache.
            AddToCache(cacheKey, relativeTo, returnVariable);

            return returnVariable;
        }
Example #4
0
        /// <summary>
        /// Get the value of a variable or model.
        /// </summary>
        /// <param name="namePath">The name of the object to return</param>
        /// <param name="relativeTo">The model calling this method</param>
        /// <returns>The found object or null if not found</returns>
        public IVariable GetInternal(string namePath, Model relativeTo)
        {
            Model  relativeToModel = relativeTo;
            string cacheKey        = namePath;

            // Look in cache first.
            object value = GetFromCache(cacheKey, relativeToModel);

            if (value != null)
            {
                return(value as IVariable);
            }

            IVariable returnVariable = null;

            if (namePath == null || namePath.Length == 0)
            {
                return(null);
            }
            else if (namePath[0] != '.' && namePath[0] != '.' &&
                     namePath.IndexOfAny("(+*/".ToCharArray()) != -1)
            {
                // expression - need a better way of detecting an expression
                returnVariable = new VariableExpression(namePath, relativeToModel);
            }
            else
            {
                // Remove a square bracketed model name and change our relativeTo model to
                // the referenced model.
                if (namePath.StartsWith("["))
                {
                    int posCloseBracket = namePath.IndexOf(']');
                    if (posCloseBracket == -1)
                    {
                        return(null);
                    }
                    string modelName = namePath.Substring(1, posCloseBracket - 1);
                    namePath = namePath.Remove(0, posCloseBracket + 1);
                    Model foundModel = this.Find(modelName, relativeToModel);
                    if (foundModel == null)
                    {
                        // Didn't find a model with a name matching the square bracketed string so
                        // now try and look for a model with a type matching the square bracketed string.
                        Type[] modelTypes = GetTypeWithoutNameSpace(modelName);
                        if (modelTypes.Length == 1)
                        {
                            foundModel = this.Find(modelTypes[0], relativeToModel);
                        }
                    }
                    if (foundModel == null)
                    {
                        return(null);
                    }
                    else
                    {
                        relativeToModel = foundModel;
                    }
                }
                else if (namePath.StartsWith("."))
                {
                    // Absolute path
                    Model root = relativeToModel;
                    while (root.Parent != null)
                    {
                        root = root.Parent as Model;
                    }
                    relativeToModel = root;

                    int posPeriod = namePath.IndexOf('.', 1);
                    if (posPeriod == -1)
                    {
                        posPeriod = namePath.Length;
                    }

                    namePath = namePath.Remove(0, posPeriod);
                    if (namePath.StartsWith("."))
                    {
                        namePath.Remove(1);
                    }
                }

                // Now walk the series of '.' separated path bits, assuming the path bits
                // are child models. Stop when we can't find the child model.
                string[] namePathBits = namePath.Split(".".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                int      i;
                for (i = 0; i < namePathBits.Length; i++)
                {
                    IModel localModel = relativeToModel.Children.FirstOrDefault(m => m.Name == namePathBits[i]);
                    if (localModel == null)
                    {
                        break;
                    }
                    else
                    {
                        relativeToModel = localModel as Model;
                    }
                }

                // At this point there are only 2 possibilities. We have encountered a
                // PropertyInfo or the path is invalid.
                // We now need to loop through the remaining path bits and keep track of each
                // section of the path as each section will have to be evaulated everytime a
                // a get is done for this path.
                // The variable 'i' will point to the name path that cannot be found as a model.
                object           relativeToObject = relativeToModel;
                List <IVariable> properties       = new List <IVariable>();
                properties.Add(new VariableObject(relativeToModel));
                for (int j = i; j < namePathBits.Length; j++)
                {
                    // look for an array specifier e.g. sw[2]
                    string arraySpecifier = null;
                    if (namePathBits[j].Contains("["))
                    {
                        arraySpecifier = StringUtilities.SplitOffBracketedValue(ref namePathBits[j], '[', ']');
                    }

                    // Look for either a property or a child model.
                    IModel       localModel   = null;
                    PropertyInfo propertyInfo = relativeToObject.GetType().GetProperty(namePathBits[j]);
                    if (propertyInfo == null && relativeToObject is Model)
                    {
                        // Not a property, may be a child model.
                        localModel = (relativeToObject as IModel).Children.FirstOrDefault(m => m.Name == namePathBits[i]);
                        if (localModel == null)
                        {
                            return(null);
                        }

                        properties.Add(new VariableObject(localModel));
                        relativeToObject = localModel;
                    }
                    else if (propertyInfo != null)
                    {
                        VariableProperty property = new VariableProperty(relativeToObject, propertyInfo, arraySpecifier);
                        properties.Add(property);
                        relativeToObject = property.Value;
                    }
                    else
                    {
                        return(null);
                    }
                }

                // We now have a list of IVariable instances that can be evaluated to
                // produce a return value for the given path. Wrap the list into an IVariable.
                returnVariable = new VariableComposite(namePath, properties);
            }

            // Add variable to cache.
            AddToCache(cacheKey, relativeTo, returnVariable);

            return(returnVariable);
        }