/// <summary>
        /// Builds code statements that will build <paramref name="part"/> using the
        /// <see cref="OpenXmlPart.FeedData(Stream)"/> method.
        /// </summary>
        /// <param name="part">
        /// The <see cref="OpenXmlPart"/> object to build the source code for.
        /// </param>
        /// <param name="namespaces">
        /// <see cref="ISet{T}">Set</see> of <see cref="String"/> values used to keep
        /// track of all openxml namespaces used during the process.
        /// </param>
        /// <returns>
        /// A <see cref="CodeStatementCollection">collection of code statements</see>
        /// that would regenerate <paramref name="part"/> using the
        /// <see cref="OpenXmlPart.FeedData(Stream)"/> method.
        /// </returns>
        public static CodeStatementCollection BuildPartFeedData(this OpenXmlPart part, ISet <string> namespaces)
        {
            // Make sure no null values were passed.
            if (part == null)
            {
                throw new ArgumentNullException(nameof(part));
            }
            if (namespaces == null)
            {
                throw new ArgumentNullException(nameof(namespaces));
            }

            // If the root element is not present (aka: null) then perform a simple feed
            // dump of the part in the current method
            const string memName = "mem";
            const string b64Name = "base64";

            var result = new CodeStatementCollection();

            // Add the necessary namespaces by hand to the namespace set
            namespaces.Add("System");
            namespaces.Add("System.IO");

            using (var partStream = part.GetStream(FileMode.Open, FileAccess.Read))
            {
                using (var mem = new MemoryStream())
                {
                    partStream.CopyTo(mem);
                    result.Add(new CodeVariableDeclarationStatement(typeof(string), b64Name,
                                                                    new CodePrimitiveExpression(Convert.ToBase64String(mem.ToArray()))));
                }
            }
            result.AddBlankLine();

            var fromBase64 = new CodeMethodReferenceExpression(new CodeVariableReferenceExpression("Convert"),
                                                               "FromBase64String");
            var invokeFromBase64 = new CodeMethodInvokeExpression(fromBase64, new CodeVariableReferenceExpression("base64"));
            var createStream     = new CodeObjectCreateExpression(new CodeTypeReference("MemoryStream"),
                                                                  invokeFromBase64, new CodePrimitiveExpression(false));
            var feedData         = new CodeMethodReferenceExpression(new CodeArgumentReferenceExpression(methodParamName), "FeedData");
            var invokeFeedData   = new CodeMethodInvokeExpression(feedData, new CodeVariableReferenceExpression(memName));
            var disposeMem       = new CodeMethodReferenceExpression(new CodeVariableReferenceExpression(memName), "Dispose");
            var invokeDisposeMem = new CodeMethodInvokeExpression(disposeMem);

            // Setup the try statement
            var tryAndCatch = new CodeTryCatchFinallyStatement();

            tryAndCatch.TryStatements.Add(invokeFeedData);
            tryAndCatch.FinallyStatements.Add(invokeDisposeMem);

            // Put all of the pieces together
            result.Add(new CodeVariableDeclarationStatement("Stream", memName, createStream));
            result.Add(tryAndCatch);

            return(result);
        }
Example #2
0
        /// <summary>
        /// Builds the appropriate code objects that would build the contents of
        /// <paramref name="e"/>.
        /// </summary>
        /// <param name="e">
        /// The <see cref="OpenXmlElement"/> object to codify.
        /// </param>
        /// <param name="settings">
        /// The <see cref="ISerializeSettings"/> to use during the code generation
        /// process.
        /// </param>
        /// <param name="typeCounts">
        /// A lookup <see cref="IDictionary{TKey, TValue}"/> object containing the
        /// number of times a given type was referenced.  This is used for variable naming
        /// purposes.
        /// </param>
        /// <param name="namespaces">
        /// Collection <see cref="ISet{T}"/> used to keep track of all openxml namespaces
        /// used during the process.
        /// </param>
        /// <param name="elementName">
        /// The variable name of the root <see cref="OpenXmlElement"/> object that was built
        /// from the <paramref name="e"/>.
        /// </param>
        /// <returns>
        /// A collection of code statements and expressions that could be used to generate
        /// a new <paramref name="e"/> object from code.
        /// </returns>
        public static CodeStatementCollection BuildCodeStatements(
            this OpenXmlElement e,
            ISerializeSettings settings,
            IDictionary <Type, int> typeCounts,
            ISet <string> namespaces,
            out string elementName)
        {
            // argument validation
            if (e is null)
            {
                throw new ArgumentNullException(nameof(e));
            }
            if (settings is null)
            {
                throw new ArgumentNullException(nameof(settings));
            }
            if (typeCounts is null)
            {
                throw new ArgumentNullException(nameof(typeCounts));
            }
            if (namespaces is null)
            {
                throw new ArgumentNullException(nameof(namespaces));
            }

            // method vars
            var result      = new CodeStatementCollection();
            var elementType = e.GetType();

            // If current element is OpenXmlUnknownElement and IgnoreUnknownElements
            // setting is enabled, return an empty CodeStatementCollection and
            // proceed no further.
            if (settings.IgnoreUnknownElements && e is OpenXmlUnknownElement)
            {
                elementName = String.Empty;
                return(result);
            }

            // If current element is OpenXmlMiscNode and its XmlNodeType is found in
            // the IgnoreMiscNoteTypes setting, return an empty CodeStatementCollection
            // and proceed no futher.
            if (e is OpenXmlMiscNode eMisc)
            {
                if (settings.IgnoreMiscNodeTypes != null && settings.IgnoreMiscNodeTypes.Contains(eMisc.XmlNodeType))
                {
                    elementName = String.Empty;
                    return(result);
                }
            }

            // If there is any custom code available for the current element, use the
            // custom code instead
            if (settings?.Handlers != null && settings.Handlers.TryGetValue(elementType, out IOpenXmlHandler customHandler))
            {
                // Make sure that the current handler implements IOpenXmlElementHandler.
                // If so, return the custom code statement collection.
                if (customHandler is IOpenXmlElementHandler cHandler)
                {
                    // Only return the custom code statements if the hanlder
                    // implementation doesn't return null
                    var customCodeStatements = cHandler.BuildCodeStatements(
                        e, settings, typeCounts, namespaces, out elementName);

                    if (customCodeStatements != null)
                    {
                        return(customCodeStatements);
                    }
                }
            }

            // Build the initializer for the current element
            elementName = elementType.GenerateVariableName(typeCounts);

            CodeStatement statement;
            CodeObjectCreateExpression    createExpression;
            CodeMethodReferenceExpression methodReferenceExpression;
            CodeMethodInvokeExpression    invokeExpression;
            CodeTypeReferenceCollection   typeReferenceCollection;
            CodeTypeReference             typeReference;

            // Used for dealing with thrown FormatExceptions
            Func <string, CodeStatement> handleFmtException;

            // Dictionary used to map complex objects to element properties
            var simpleTypePropReferences = new Dictionary <string, string>();

            Type              tmpType    = null;
            string            simpleName = null;
            string            junk       = null;
            object            val        = null;
            object            propVal    = null;
            PropertyInfo      pi         = null;
            OpenXmlSimpleType tmpSimpleType;

            // Start pulling out the properties of the current element.
            var sProperties = elementType.GetOpenXmlSimpleValuesProperties();
            var cProps      = elementType.GetOpenXmlSimpleTypeProperties(false);
            IReadOnlyList <PropertyInfo> cProperties = cProps
                                                       .Where(m => !m.PropertyType.IsEnumValueType())
                                                       .ToList();
            IReadOnlyList <PropertyInfo> enumProperties = cProps
                                                          .Where(m => m.PropertyType.IsEnumValueType())
                                                          .ToList();

            // Create a variable reference statement
            CodeAssignStatement primitivePropertyAssignment(
                string objName,
                string varName,
                string rVal,
                bool varIsRef = false)
            {
                var rValExp = (varIsRef
                    ? new CodeVariableReferenceExpression(rVal)
                    : new CodePrimitiveExpression(rVal) as CodeExpression);

                return(new CodeAssignStatement(
                           new CodePropertyReferenceExpression(
                               new CodeVariableReferenceExpression(objName), varName),
                           rValExp));
            }

            // Add the current element type namespace to the set object
            namespaces.Add(elementType.Namespace);

            // Need to build the non enumvalue complex objects first before assigning
            // them as properties of the current element
            foreach (var complex in cProperties)
            {
                // Get the value of the current property
                val = complex.GetValue(e);

                // Skip properties that are null
                if (val is null)
                {
                    continue;
                }

                // Add the complex property namespace to the set
                namespaces.Add(complex.PropertyType.Namespace);

                // Use the junk var to store the property type name
                junk = complex.PropertyType.Name;

                // Build the variable name
                simpleName = complex.PropertyType.GenerateVariableName(typeCounts);

                // Need to handle the generic properties special when trying
                // to build a variable name.
                if (complex.PropertyType.IsGenericType)
                {
                    // Setup necessary CodeDom objects
                    typeReferenceCollection = new CodeTypeReferenceCollection();

                    foreach (var gen in complex.PropertyType.GenericTypeArguments)
                    {
                        typeReferenceCollection.Add(gen.Name);
                    }

                    typeReference = new CodeTypeReference(junk);
                    typeReference.TypeArguments.AddRange(typeReferenceCollection);
                    createExpression = new CodeObjectCreateExpression(typeReference);
                    statement        = new CodeVariableDeclarationStatement(typeReference, simpleName, createExpression);
                }
                else
                {
                    createExpression = new CodeObjectCreateExpression(junk);
                    statement        = new CodeVariableDeclarationStatement(junk, simpleName, createExpression);
                }
                result.Add(statement);

                // Finish the variable assignment statement
                result.Add(primitivePropertyAssignment(simpleName, "InnerText", val.ToString()));
                result.AddBlankLine();

                // Keep track of the objects to assign to the current element
                // complex properties
                simpleTypePropReferences.Add(complex.Name, simpleName);
            }

            // Initialize the mc attribute information, if available
            if (e.MCAttributes != null)
            {
                tmpType = e.MCAttributes.GetType();

                simpleName = tmpType.GenerateVariableName(typeCounts);

                createExpression = new CodeObjectCreateExpression(tmpType.Name);
                statement        = new CodeVariableDeclarationStatement(tmpType.Name, simpleName, createExpression);
                result.Add(statement);

                foreach (var m in tmpType.GetStringValueProperties())
                {
                    val = m.GetValue(e.MCAttributes);
                    if (val != null)
                    {
                        statement = new CodeAssignStatement(
                            new CodePropertyReferenceExpression(
                                new CodeVariableReferenceExpression(simpleName), m.Name),
                            new CodePrimitiveExpression(val.ToString()));
                        result.Add(statement);
                    }
                }
                result.AddBlankLine();
                simpleTypePropReferences.Add("MCAttributes", simpleName);
            }

            // Include the alias prefix if the current element belongs to a class
            // within the namespaces identified to needing an alias
            junk             = elementType.GetObjectTypeName(settings.NamespaceAliasOptions.Order);
            createExpression = new CodeObjectCreateExpression(junk);

            // OpenXmlUknownElement objects require the calling of custom constructors
            if (e is OpenXmlUnknownElement)
            {
                createExpression.Parameters.AddRange(new CodeExpression[]
                {
                    new CodePrimitiveExpression(e.Prefix),
                    new CodePrimitiveExpression(e.LocalName),
                    new CodePrimitiveExpression(e.NamespaceUri)
                });
            }
            // OpenXmlLeafTextElement classes have constructors that take in
            // one StringValue object as a parameter to populate the new
            // object's Text property.  This takes advantange of that knowledge.
            else if (elementType.IsSubclassOf(typeof(OpenXmlLeafTextElement)))
            {
                var leafText = elementType.GetProperty("Text").GetValue(e);
                var param    = new CodePrimitiveExpression(leafText);
                createExpression.Parameters.Add(param);
            }
            statement = new CodeVariableDeclarationStatement(junk, elementName, createExpression);
            result.Add(statement);

            // Don't forget to add any additional namespaces to the element
            if (e.NamespaceDeclarations != null && e.NamespaceDeclarations.Count() > 0)
            {
                result.AddBlankLine();
                foreach (var ns in e.NamespaceDeclarations)
                {
                    methodReferenceExpression = new CodeMethodReferenceExpression(
                        new CodeVariableReferenceExpression(elementName),
                        "AddNamespaceDeclaration");
                    invokeExpression = new CodeMethodInvokeExpression(methodReferenceExpression,
                                                                      new CodePrimitiveExpression(ns.Key),
                                                                      new CodePrimitiveExpression(ns.Value));
                    result.Add(invokeExpression);
                }

                // Add a line break if namespace declarations were present and if the current
                // element has additional properties that need to be filled out.
                if ((cProperties.Count > 0 || simpleTypePropReferences.Count > 0) ||
                    (sProperties.Count > 0 && sProperties.Count(sp => sp.GetValue(e) != null) > 0))
                {
                    result.AddBlankLine();
                }
            }

            // Now set the properties of the current variable
            foreach (var p in sProperties)
            {
                val = p.GetValue(e);
                if (val == null)
                {
                    continue;
                }

                // Add the simple property type namespace to the set
                namespaces.Add(p.PropertyType.Namespace);

                tmpSimpleType = val as OpenXmlSimpleType;

                if (!tmpSimpleType.HasValue)
                {
                    statement = new CodeCommentStatement(
                        $"'{val}' is not a valid value for the {p.Name} property");
                }
                else
                {
                    propVal = val.GetType().GetProperty("Value").GetValue(val);

                    statement = new CodeAssignStatement(
                        new CodePropertyReferenceExpression(
                            new CodeVariableReferenceExpression(elementName), p.Name),
                        new CodePrimitiveExpression(propVal));
                }
                result.Add(statement);
            }

            if (simpleTypePropReferences.Count > 0)
            {
                foreach (var sProp in simpleTypePropReferences)
                {
                    statement = new CodeAssignStatement(
                        new CodePropertyReferenceExpression(
                            new CodeVariableReferenceExpression(elementName), sProp.Key),
                        new CodeVariableReferenceExpression(sProp.Value));
                    result.Add(statement);
                }
            }

            // Go through the list of complex properties again but include
            // EnumValue`1 type properties in the search.
            foreach (var cp in enumProperties)
            {
                val        = cp.GetValue(e);
                simpleName = null;

                if (val is null)
                {
                    continue;
                }

                pi         = cp.PropertyType.GetProperty("Value");
                simpleName = pi.PropertyType.GetObjectTypeName(settings.NamespaceAliasOptions.Order);

                // Add the simple property type namespace to the set
                namespaces.Add(pi.PropertyType.Namespace);

                handleFmtException = (eName) =>
                                     new CodeCommentStatement(
                    $"Could not parse value of '{cp.Name}' property for variable " +
                    $"`{eName}` - {simpleName} enum does not contain '{val}' field");

                // This code may run into issues if, for some unfortunate reason, the xml schema used to help
                // create the current OpenXml SDK library is not set correctly.  If that happens, the
                // catch statements will print out the current line of code as a comment as a stop gap until
                // the issue is reported and fix.
                try
                {
                    statement = new CodeAssignStatement(
                        new CodePropertyReferenceExpression(
                            new CodeVariableReferenceExpression(elementName), cp.Name),
                        new CodeFieldReferenceExpression(
                            new CodeVariableReferenceExpression(simpleName),
                            pi.GetValue(val).ToString()));
                }
                catch (TargetInvocationException tie)
                    when(tie.InnerException != null && tie.InnerException is FormatException)
                    {
                        // This is used if the value retrieved from an element property
                        // doesn't match any of the enum values of the expected property
                        // type
                        statement = handleFmtException(elementName);
                    }
                catch (FormatException)
                {
                    statement = handleFmtException(elementName);
                }
                catch
                {
                    throw;
                }
                result.Add(statement);
            }
            // Insert an empty line
            result.AddBlankLine();

            // See if the current element has children and retrieve that information
            if (e.HasChildren)
            {
                foreach (var child in e)
                {
                    // Ignore OpenXmlUnknownElement objects if specified
                    if (settings.IgnoreUnknownElements && child is OpenXmlUnknownElement)
                    {
                        continue;
                    }

                    // use recursion to generate source code for the child elements
                    result.AddRange(
                        child.BuildCodeStatements(settings, typeCounts, namespaces, out string appendName));

                    methodReferenceExpression = new CodeMethodReferenceExpression(
                        new CodeVariableReferenceExpression(elementName),
                        "Append");
                    invokeExpression = new CodeMethodInvokeExpression(methodReferenceExpression,
                                                                      new CodeVariableReferenceExpression(appendName));
                    result.Add(invokeExpression);
                    result.AddBlankLine();
                }
            }

            // Return all of the collected expressions and statements
            return(result);
        }
        /// <summary>
        /// Creates the appropriate code objects needed to create the entry method for the
        /// current request.
        /// </summary>
        /// <param name="part">
        /// The <see cref="OpenXmlPart"/> object and relationship id to build code for.
        /// </param>
        /// <param name="settings">
        /// The <see cref="ISerializeSettings"/> to use during the code generation
        /// process.
        /// </param>
        /// /// <param name="typeCounts">
        /// A lookup <see cref="IDictionary{TKey, TValue}"/> object containing the
        /// number of times a given type was referenced.  This is used for variable naming
        /// purposes.
        /// </param>
        /// <param name="namespaces">
        /// Collection <see cref="ISet{T}"/> used to keep track of all openxml namespaces
        /// used during the process.
        /// </param>
        /// <param name="blueprints">
        /// The collection of <see cref="OpenXmlPartBluePrint"/> objects that have already been
        /// visited.
        /// </param>
        /// <param name="rootVar">
        /// The root variable name and <see cref="Type"/> to use when building code
        /// statements to create new <see cref="OpenXmlPart"/> objects.
        /// </param>
        /// <returns>
        /// A collection of code statements and expressions that could be used to generate
        /// a new <paramref name="part"/> object from code.
        /// </returns>
        public static CodeStatementCollection BuildEntryMethodCodeStatements(
            IdPartPair part,
            ISerializeSettings settings,
            IDictionary <string, int> typeCounts,
            ISet <string> namespaces,
            OpenXmlPartBluePrintCollection blueprints,
            KeyValuePair <string, Type> rootVar)
        {
            // Argument validation
            if (part is null)
            {
                throw new ArgumentNullException(nameof(part));
            }
            if (settings is null)
            {
                throw new ArgumentNullException(nameof(settings));
            }
            if (blueprints is null)
            {
                throw new ArgumentNullException(nameof(blueprints));
            }
            if (String.IsNullOrWhiteSpace(rootVar.Key))
            {
                throw new ArgumentNullException(nameof(rootVar.Key));
            }
            bool hasHandlers = settings?.Handlers != null;

            // Use the custom handler methods if present and provide actual code
            if (hasHandlers && settings.Handlers.TryGetValue(part.OpenXmlPart.GetType(), out IOpenXmlHandler h))
            {
                if (h is IOpenXmlPartHandler partHandler)
                {
                    var customStatements = partHandler.BuildEntryMethodCodeStatements(
                        part, settings, typeCounts, namespaces, blueprints, rootVar);

                    if (customStatements != null)
                    {
                        return(customStatements);
                    }
                }
            }

            var    result                   = new CodeStatementCollection();
            var    partType                 = part.OpenXmlPart.GetType();
            var    partTypeName             = partType.Name;
            var    partTypeFullName         = partType.FullName;
            string varName                  = partType.Name.ToCamelCase();
            bool   customAddNewPartRequired = CheckForCustomAddNewPartMethod(partType, rootVar.Value, out string addNewPartName);

#pragma warning disable IDE0018 // Inline variable declaration
            OpenXmlPartBluePrint bpTemp;
#pragma warning restore IDE0018 // Inline variable declaration

            CodeMethodReferenceExpression referenceExpression;
            CodeMethodInvokeExpression    invokeExpression;
            CodeMethodReferenceExpression methodReference;

            // Make sure that the namespace for the current part is captured
            namespaces.Add(partType.Namespace);

            // If the URI of the current part has already been included into
            // the blue prints collection, build the AddPart invocation
            // code statement and exit current method iteration.
            if (blueprints.TryGetValue(part.OpenXmlPart.Uri, out bpTemp))
            {
                // Surround this snippet with blank lines to make it
                // stand out in the current section of code.
                result.AddBlankLine();
                referenceExpression = new CodeMethodReferenceExpression(
                    new CodeVariableReferenceExpression(rootVar.Key), "AddPart",
                    new CodeTypeReference(part.OpenXmlPart.GetType().Name));
                invokeExpression = new CodeMethodInvokeExpression(referenceExpression,
                                                                  new CodeVariableReferenceExpression(bpTemp.VariableName),
                                                                  new CodePrimitiveExpression(part.RelationshipId));
                result.Add(invokeExpression);
                result.AddBlankLine();
                return(result);
            }

            // Assign the appropriate variable name
            if (typeCounts.ContainsKey(partTypeFullName))
            {
                varName = String.Concat(varName, typeCounts[partTypeFullName]++);
            }
            else
            {
                typeCounts.Add(partTypeFullName, 1);
            }

            // Setup the blueprint
            bpTemp = new OpenXmlPartBluePrint(part.OpenXmlPart, varName);

            // Need to evaluate the current OpenXmlPart type first to make sure the
            // correct "Add" statement is used as not all Parts can be initialized
            // using the "AddNewPart" method

            // Check for image part methods
            if (customAddNewPartRequired)
            {
                referenceExpression = new CodeMethodReferenceExpression(
                    new CodeVariableReferenceExpression(rootVar.Key), addNewPartName);
            }
            else
            {
                // Setup the add new part statement for the current OpenXmlPart object
                referenceExpression = new CodeMethodReferenceExpression(
                    new CodeVariableReferenceExpression(rootVar.Key), "AddNewPart",
                    new CodeTypeReference(partTypeName));
            }

            // Create the invoke expression
            invokeExpression = new CodeMethodInvokeExpression(referenceExpression);

            // Add content type to invoke method for embeddedpackage and image parts.
            if (part.OpenXmlPart is EmbeddedPackagePart || customAddNewPartRequired)
            {
                invokeExpression.Parameters.Add(
                    new CodePrimitiveExpression(part.OpenXmlPart.ContentType));
            }
            else if (!customAddNewPartRequired)
            {
                invokeExpression.Parameters.Add(
                    new CodePrimitiveExpression(part.RelationshipId));
            }

            result.Add(new CodeVariableDeclarationStatement(partTypeName, varName, invokeExpression));

            // Because the custom AddNewPart methods don't consistently take in a string relId
            // as a parameter, the id needs to be assigned after it is created.
            if (customAddNewPartRequired)
            {
                methodReference = new CodeMethodReferenceExpression(
                    new CodeVariableReferenceExpression(rootVar.Key), "ChangeIdOfPart");
                result.Add(new CodeMethodInvokeExpression(methodReference,
                                                          new CodeVariableReferenceExpression(varName),
                                                          new CodePrimitiveExpression(part.RelationshipId)));
            }

            // Add the call to the method to populate the current OpenXmlPart object
            methodReference = new CodeMethodReferenceExpression(new CodeThisReferenceExpression(), bpTemp.MethodName);
            result.Add(new CodeMethodInvokeExpression(methodReference,
                                                      new CodeDirectionExpression(FieldDirection.Ref,
                                                                                  new CodeVariableReferenceExpression(varName))));

            // Add the appropriate code statements if the current part
            // contains any hyperlink relationships
            if (part.OpenXmlPart.HyperlinkRelationships.Count() > 0)
            {
                // Add a line break first for easier reading
                result.AddBlankLine();
                result.AddRange(
                    part.OpenXmlPart.HyperlinkRelationships.BuildHyperlinkRelationshipStatements(varName));
            }

            // Add the appropriate code statements if the current part
            // contains any non-hyperlink external relationships
            if (part.OpenXmlPart.ExternalRelationships.Count() > 0)
            {
                // Add a line break first for easier reading
                result.AddBlankLine();
                result.AddRange(
                    part.OpenXmlPart.ExternalRelationships.BuildExternalRelationshipStatements(varName));
            }

            // put a line break before going through the child parts
            result.AddBlankLine();

            // Add the current blueprint to the collection
            blueprints.Add(bpTemp);

            // Now check to see if there are any child parts for the current OpenXmlPart object.
            if (bpTemp.Part.Parts != null)
            {
                OpenXmlPartBluePrint childBluePrint;

                foreach (var p in bpTemp.Part.Parts)
                {
                    // If the current child object has already been created, simply add a reference to
                    // said object using the AddPart method.
                    if (blueprints.Contains(p.OpenXmlPart.Uri))
                    {
                        childBluePrint = blueprints[p.OpenXmlPart.Uri];

                        referenceExpression = new CodeMethodReferenceExpression(
                            new CodeVariableReferenceExpression(varName), "AddPart",
                            new CodeTypeReference(p.OpenXmlPart.GetType().Name));

                        invokeExpression = new CodeMethodInvokeExpression(referenceExpression,
                                                                          new CodeVariableReferenceExpression(childBluePrint.VariableName),
                                                                          new CodePrimitiveExpression(p.RelationshipId));

                        result.Add(invokeExpression);
                        continue;
                    }

                    // If this is a new part, call this method with the current part's details
                    result.AddRange(BuildEntryMethodCodeStatements(p, settings, typeCounts, namespaces, blueprints,
                                                                   new KeyValuePair <string, Type>(varName, partType)));
                }
            }

            return(result);
        }