Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

Resolving Symbolic References in a CodeDOM (Part 7)

, 2 Dec 2012
Resolving symbolic references in a CodeDOM.
Nova.0.6.exe.zip
Nova.0.6.zip
Nova.CLI
Properties
Nova.CodeDOM
CodeDOM
Annotations
Base
Comments
Base
DocComments
CodeRef
Base
List
Name
Base
Other
Simple
CompilerDirectives
Base
Conditionals
Base
Messages
Base
Pragmas
Base
Symbols
Base
Base
Interfaces
Expressions
AnonymousMethods
Base
Operators
Base
Binary
Arithmetic
Base
Assignment
Base
Bitwise
Base
Conditional
Relational
Base
Shift
Base
Other
Base
Unary
Base
Other
References
Base
GotoTargets
Base
Methods
Namespaces
Other
Properties
Types
Base
Variables
Base
Projects
Assemblies
Namespaces
References
Base
Statements
Base
Conditionals
Base
Exceptions
Generics
Constraints
Base
Iterators
Base
Jumps
Loops
Methods
OperatorDecls
Miscellaneous
Namespaces
Properties
Base
Events
Types
Base
Variables
Base
Parsing
Base
Properties
Rendering
Resolving
Utilities
Mono.Cecil
Reflection
Nova.Examples
Properties
Nova.Studio
Images
About.png
Configuration.png
EditCopy.png
EditCut.png
EditDelete.png
EditPaste.png
EditRedo.png
EditUndo.png
Error.png
Exit.png
FileNew.png
FileOpen.png
FileSave.png
FileSaveAll.png
FileSaveAs.png
Find.png
Help.png
Info.png
Logo.png
Options.png
Print.png
PrintPreview.png
Properties.png
Todo.png
Warning.png
Objects.ico
Properties
Settings.settings
Nova.Test
Properties
Nova.UI
CodeDOM
Annotations
Base
Comments
Base
DocComments
CodeRef
Base
List
Name
Base
Other
Simple
CompilerDirectives
Base
Conditionals
Base
Messages
Base
Pragmas
Base
Symbols
Base
Base
Expressions
AnonymousMethods
Base
Operators
Base
Binary
Arithmetic
Base
Assignment
Base
Bitwise
Base
Conditional
Relational
Base
Shift
Base
Other
Base
Unary
Base
Other
References
Base
GotoTargets
Base
Methods
Namespaces
Other
Properties
Types
Base
Variables
Base
Projects
Namespaces
References
Base
Statements
Base
Conditionals
Base
Exceptions
Generics
Constraints
Base
Iterators
Base
Jumps
Loops
Methods
OperatorDecls
Miscellaneous
Namespaces
Properties
Base
Events
Types
Base
Variables
Base
Properties
Resolving
Utilties
// The Nova Project by Ken Beckett.
// Copyright (C) 2007-2012 Inevitable Software, all rights reserved.
// Released under the Common Development and Distribution License, CDDL-1.0: http://opensource.org/licenses/cddl1.php

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

using Nova.Parsing;
using Nova.Rendering;
using Nova.Resolving;
using Nova.Utilities;

namespace Nova.CodeDOM
{
    /// <summary>
    /// Represents an un-named method that can be assigned to a delegate.
    /// </summary>
    /// <remarks>
    /// An AnonymousMethod distinguishes between having zero parameters and unspecified parameters (no parens
    /// at all).  If the parameters are unspecified, the anonymous method is convertible to a delegate with any
    /// parameter list not containing out parameters.  It's possible that the empty parens will be needed in
    /// some cases to prevent ambiguity, such as when resolving a method reference that is passed an anonymous
    /// method as a parameter, and multiple matches are found that have delegates with different numbers of
    /// parameters (one of them having zero).
    /// </remarks>
    public class AnonymousMethod : Expression, IParameters, IBlock
    {
        #region /* FIELDS */

        protected ChildList<ParameterDecl> _parameters;
        protected Block _body;

        #endregion

        #region /* CONSTRUCTORS */

        /// <summary>
        /// Create an <see cref="AnonymousMethod"/>.
        /// </summary>
        public AnonymousMethod(CodeObject body, params ParameterDecl[] parameters)
        {
            // Allow derived classes to pass any non-Block code object, in which case it will
            // be wrapped in a Block.
            Body = ((body == null || body is Block) ? (Block)body : new Block(body));

            // Allow the parameter collection to be null (unspecified parameters) if null is specifically passed in (for AnonymousMethod)
            if (parameters != null)
                CreateParameters().AddRange(parameters);
        }

        /// <summary>
        /// Create an <see cref="AnonymousMethod"/>.
        /// </summary>
        public AnonymousMethod(params ParameterDecl[] parameters)
            : this(new Block(), parameters)
        { }

        #endregion

        #region /* PROPERTIES */

        /// <summary>
        /// The <see cref="Block"/> body.
        /// </summary>
        public Block Body
        {
            get { return _body; }
            set
            {
                _body = value;
                if (_body != null)
                {
                    _body.Parent = this;
                    ReformatBlock();
                }
            }
        }

        /// <summary>
        /// The parameters of the anonymous method (if any).
        /// </summary>
        public ChildList<ParameterDecl> Parameters
        {
            get { return _parameters; }
        }

        /// <summary>
        /// True if the anonymous method has any parameters.
        /// </summary>
        public bool HasParameters
        {
            get { return (_parameters != null && _parameters.Count > 0); }
        }

        /// <summary>
        /// The number of parameters the anonymous method has.
        /// </summary>
        public int ParameterCount
        {
            get { return (_parameters != null ? _parameters.Count : 0); }
        }

        /// <summary>
        /// Always <c>true</c>.
        /// </summary>
        public bool HasHeader
        {
            get { return true; }
        }

        /// <summary>
        /// Always <c>false</c>.
        /// </summary>
        public bool IsTopLevel
        {
            get { return false; }
        }

        #endregion

        #region /* METHODS */

        /// <summary>
        /// Create a body if one doesn't exist yet.
        /// </summary>
        public Block CreateBody()
        {
            if (_body == null)
                Body = new Block();
            return _body;
        }

        /// <summary>
        /// Create the list of <see cref="ParameterDecl"/>s, or return the existing one.
        /// </summary>
        public ChildList<ParameterDecl> CreateParameters()
        {
            if (_parameters == null)
                _parameters = new ChildList<ParameterDecl>(this);
            return _parameters;
        }

        /// <summary>
        /// Add one or more <see cref="ParameterDecl"/>s.
        /// </summary>
        public void AddParameters(params ParameterDecl[] parameterDecls)
        {
            CreateParameters().AddRange(parameterDecls);
        }

        /// <summary>
        /// Add a <see cref="CodeObject"/> to the <see cref="Block"/> body.
        /// </summary>
        public void Add(CodeObject obj)
        {
            CreateBody().Add(obj);
        }

        /// <summary>
        /// Add multiple <see cref="CodeObject"/>s to the <see cref="Block"/> body.
        /// </summary>
        public void Add(params CodeObject[] objects)
        {
            CreateBody();
            foreach (CodeObject obj in objects)
                _body.Add(obj);
        }

        /// <summary>
        /// Add a code object to the block statement, and also assign the specified comment to the object.
        /// </summary>
        /// <param name="obj">The object to be added.</param>
        /// <param name="comment">The comment text.</param>
        public void Add(CodeObject obj, string comment)
        {
            obj.AttachAnnotation(new Comment(comment));
            Add(obj);
        }

        /// <summary>
        /// Insert a <see cref="CodeObject"/> at the specified index.
        /// </summary>
        /// <param name="index">The index at which to insert.</param>
        /// <param name="obj">The object to be inserted.</param>
        public void Insert(int index, CodeObject obj)
        {
            CreateBody().Insert(index, obj);
        }

        /// <summary>
        /// Remove the specified <see cref="CodeObject"/> from the body.
        /// </summary>
        public void Remove(CodeObject obj)
        {
            if (_body != null)
                _body.Remove(obj);
        }

        /// <summary>
        /// Remove all objects from the body.
        /// </summary>
        public void RemoveAll()
        {
            if (_body != null)
                _body.RemoveAll();
        }

        /// <summary>
        /// Determine the return type of the anonymous method (never null - will be 'void' instead).
        /// </summary>
        public virtual TypeRefBase GetReturnType()
        {
            TypeRefBase returnTypeRef = null;
            ScanReturnTypes(ref returnTypeRef, _body);
            return (returnTypeRef ?? TypeRef.VoidRef);
        }

        protected void ScanReturnTypes(ref TypeRefBase returnTypeRef, Block body)
        {
            // Recursively scan all bodies for Return statements
            if (body != null)
            {
                foreach (CodeObject codeObject in body)
                {
                    if (codeObject is Return)
                    {
                        Expression expression = ((Return)codeObject).Expression;
                        TypeRefBase typeRef = (expression == null ? TypeRef.VoidRef : expression.EvaluateType(true));
                        returnTypeRef = (returnTypeRef == null ? typeRef : TypeRef.GetCommonType(returnTypeRef, typeRef));
                    }
                    else if (codeObject is BlockStatement)
                        ScanReturnTypes(ref returnTypeRef, ((BlockStatement)codeObject).Body);
                }
            }
        }

        /// <summary>
        /// Get the parent delegate expression.
        /// </summary>
        public virtual Expression GetParentDelegateExpression()
        {
            // Determine the delegate type to which the anonymous method is being assigned
            Expression delegateExpression = null;
            CodeObject parent = _parent;
            while (parent is Conditional || parent is IfNullThen)
                parent = parent.Parent;
            if (parent is VariableDecl)
            {
                // If the anonymous method is being used to initialize a variable, get the delegate type from the variable's type
                delegateExpression = ((VariableDecl)parent).Type;
            }
            else if (parent is Assignment)
            {
                // If the anonymous method is being assigned to a variable, get the delegate type from the variable's type
                delegateExpression = ((Assignment)parent).Left.EvaluateType();
            }
            else if (parent is Cast)
            {
                // If the anonymous method is being cast, get the delegate type from the cast
                delegateExpression = ((Cast)parent).Type;
            }
            else if (parent is Return)
            {
                // If the anonymous method is being returned from a method, get the delegate type from the method return type
                CodeObject parentMethod = parent.FindParentMethod();
                if (parentMethod is MethodDeclBase)
                    delegateExpression = ((MethodDeclBase)parentMethod).ReturnType;
                else if (parentMethod is AnonymousMethod)
                {
                    // If the anonymous method is being returned from another anonymous method, recursively get the parent
                    // delegate expression of the parent anonymous method, then get the return type of that delegate.
                    delegateExpression = ((AnonymousMethod)parentMethod).GetParentDelegateExpression();
                    if (delegateExpression != null)
                        delegateExpression = delegateExpression.GetDelegateReturnType();
                }
            }
            else if (parent is YieldReturn)
            {
                // If the anonymous method is being returned from an iterator method using 'yield return', get the delegate
                // type from the method return type, minus the IEnumerable<> wrapper.
                CodeObject parentMethod = parent.FindParentMethod();
                if (parentMethod is MethodDeclBase)
                {
                    delegateExpression = ((MethodDeclBase)parentMethod).ReturnType;
                    TypeRefBase typeRefBase = delegateExpression.SkipPrefixes() as TypeRefBase;
                    if (typeRefBase != null && typeRefBase.IsSameGenericType(TypeRef.IEnumerable1Ref))
                        delegateExpression = typeRefBase.TypeArguments[0];
                }
            }
            else if (parent is Initializer)
            {
                // Handle collection initializers
                if (parent.Parent is Initializer && parent.Parent.Parent is NewObject)
                {
                    TypeRefBase typeRefBase = ((NewObject)parent.Parent.Parent).EvaluateType();

                    // Handle a Dictionary where the value is a delegate type
                    if (typeRefBase.IsSameGenericType(TypeRef.Dictionary2Ref))
                        delegateExpression = typeRefBase.TypeArguments[1];
                }
            }
            return delegateExpression;
        }

        /// <summary>
        /// Deep-clone the code object.
        /// </summary>
        public override CodeObject Clone()
        {
            AnonymousMethod clone = (AnonymousMethod)base.Clone();
            clone._parameters = ChildListHelpers.Clone(_parameters, clone);
            clone.CloneField(ref clone._body, _body);
            return clone;
        }

        #endregion

        #region /* PARSING */

        /// <summary>
        /// The token used to parse the code object.
        /// </summary>
        public const string ParseToken = DelegateDecl.ParseToken;

        /// <summary>
        /// The token used to parse the start of the parameter list of an anonymous method.
        /// </summary>
        public const string ParseTokenStart = ParameterDecl.ParseTokenStart;

        /// <summary>
        /// The token used to parse the end of the parameter list of an anonymous method.
        /// </summary>
        public const string ParseTokenEnd = ParameterDecl.ParseTokenEnd;

        internal static new void AddParsePoints()
        {
            // Use a parse-priority of 100 (DelegateDecl uses 0)
            Parser.AddParsePoint(ParseToken, 100, Parse);
        }

        /// <summary>
        /// Parse an <see cref="AnonymousMethod"/>.
        /// </summary>
        public static AnonymousMethod Parse(Parser parser, CodeObject parent, ParseFlags flags)
        {
            return new AnonymousMethod(parser, parent);
        }

        protected AnonymousMethod(Parser parser, CodeObject parent)
            : base(parser, parent)
        {
            Token startingToken = parser.Token;  // Get the object starting token
            parser.NextToken();                  // Move past 'delegate'

            // Parse the parameter declarations
            bool isEndFirstOnLine;
            _parameters = ParameterDecl.ParseList(parser, this, ParseTokenStart, ParseTokenEnd, true, out isEndFirstOnLine);
            IsEndFirstOnLine = isEndFirstOnLine;

            // If the body is indented less than the parent object, set the NoIndentation flag to prevent it from
            // being formatted relative to the parent object.
            if (parser.CurrentTokenIndentedLessThan(startingToken))
                SetFormatFlag(FormatFlags.NoIndentation, true);

            new Block(out _body, parser, this, true);  // Parse the body
        }

        #endregion

        #region /* RESOLVING */

        /// <summary>
        /// Resolve all child symbolic references, using the specified <see cref="ResolveCategory"/> and <see cref="ResolveFlags"/>.
        /// </summary>
        public override CodeObject Resolve(ResolveCategory resolveCategory, ResolveFlags flags)
        {
            ChildListHelpers.Resolve(_parameters, ResolveCategory.CodeObject, flags);
            if (_body != null)
                _body.Resolve(ResolveCategory.CodeObject, flags);
            return this;
        }

        /// <summary>
        /// Resolve child code objects that match the specified name, moving up the tree until a complete match is found.
        /// </summary>
        public override void ResolveRefUp(string name, Resolver resolver)
        {
            if (_body != null)
            {
                _body.ResolveRef(name, resolver);
                if (resolver.HasCompleteMatch) return;  // Abort if we found a match
            }
            ChildListHelpers.ResolveRef(_parameters, name, resolver);
            if (_parent != null && !resolver.HasCompleteMatch)
                _parent.ResolveRefUp(name, resolver);
        }

        /// <summary>
        /// Resolve child code objects that match the specified name are valid goto targets, moving up the tree until a complete match is found.
        /// </summary>
        public override void ResolveGotoTargetUp(string name, Resolver resolver)
        {
            if (_body != null)
            {
                _body.ResolveGotoTargetUp(name, resolver);
                if (resolver.HasCompleteMatch) return;  // Abort if we found a match
            }
            if (_parent != null)
                _parent.ResolveGotoTargetUp(name, resolver);
        }

        /// <summary>
        /// Returns true if the code object is an <see cref="UnresolvedRef"/> or has any <see cref="UnresolvedRef"/> children.
        /// </summary>
        public override bool HasUnresolvedRef()
        {
            if (_body.HasUnresolvedRef())
                return true;
            if (ChildListHelpers.HasUnresolvedRef(_parameters))
                return true;
            return base.HasUnresolvedRef();
        }

        /// <summary>
        /// Determine if the parameters of the anonymous method are compatible with those of the specified delegate type.
        /// </summary>
        public bool AreParametersCompatible(TypeRef toTypeRef)
        {
            ICollection delegateParameters = toTypeRef.GetDelegateParameters();

            // If we have parameters (even if we have 0), then we match normally
            if (_parameters != null)
            {
                // Get the method and delegate parameters
                ChildList<ParameterDecl> parameters = Parameters;
                int delegateParameterCount = (delegateParameters != null ? delegateParameters.Count : 0);

                // The parameter counts must match ('params' are NOT expanded in this situation)
                if ((parameters != null ? parameters.Count : 0) != delegateParameterCount)
                    return false;

                // For each delegate parameter, verify that its type and ref/out modifiers *match* those of
                // the method parameter (no conversions are allowed).
                for (int i = 0; i < delegateParameterCount; ++i)
                {
                    // Get the type of the delegate parameter, and any modifiers
                    bool isRef, isOut;
                    TypeRefBase delegateParameterRef = ParameterRef.GetParameterType(delegateParameters, i, out isRef, out isOut, toTypeRef);

                    // Check if the parameter types and modifiers match
                    ParameterDecl parameterDecl = parameters[i];
                    TypeRefBase parameterTypeRef = parameterDecl.EvaluateType();
                    if (parameterTypeRef == null || !parameterTypeRef.IsSameRef(delegateParameterRef) || parameterDecl.IsRef != isRef || parameterDecl.IsOut != isOut)
                        return false;
                }
            }
            else
            {
                // If we don't have any parameters (they were "unspecified"), then any count and types are allowed
                // to match, except that we have to ensure that there aren't any 'out' parameters.
                if (delegateParameters is List<ParameterDecl>)
                    return Enumerable.All(((List<ParameterDecl>)delegateParameters), delegate(ParameterDecl parameterDecl) { return !parameterDecl.IsOut; });
                if (delegateParameters is ParameterInfo[])
                    return Enumerable.All(((ParameterInfo[])delegateParameters), delegate(ParameterInfo parameterInfo) { return !ParameterInfoUtil.IsOut(parameterInfo); });
            }
            return true;
        }

        /// <summary>
        /// Determine if the return type of the anonymous method is compatible with that of the specified delegate type.
        /// </summary>
        public virtual bool AreReturnTypesCompatible(TypeRef toTypeRef)
        {
            // The types of *all* of the return statements in the block must be convertible to the delegate's return type
            TypeRefBase delegateReturnType = toTypeRef.GetDelegateReturnType();
            if (delegateReturnType == null)
                return false;
            delegateReturnType = delegateReturnType.EvaluateTypeArgumentTypes(toTypeRef);
            return ScanReturnStatements(delegateReturnType, delegateReturnType.IsSameRef(TypeRef.VoidRef), _body);
        }

        protected bool ScanReturnStatements(TypeRefBase delegateReturnType, bool isVoidReturn, Block body)
        {
            // Recursively scan all bodies for Return statements
            if (body != null)
            {
                foreach (CodeObject codeObject in body)
                {
                    if (codeObject is Return)
                    {
                        Expression expression = ((Return)codeObject).Expression;
                        if (expression == null)
                        {
                            // If the return doesn't have an expression, the return type of the delegate must be 'void'
                            if (!isVoidReturn)
                                return false;
                        }
                        else
                        {
                            // Otherwise, the type of the expression must be convertible to the return type of the delegate
                            TypeRefBase typeRefBase = expression.EvaluateType();
                            if (typeRefBase != null)
                            {
                                if (!typeRefBase.IsImplicitlyConvertibleTo(delegateReturnType))
                                    return false;
                            }
                        }
                    }
                    else if (codeObject is BlockStatement)
                    {
                        if (!ScanReturnStatements(delegateReturnType, isVoidReturn, ((BlockStatement)codeObject).Body))
                            return false;
                    }
                }
            }
            return true;
        }

        /// <summary>
        /// Evaluate the type of the <see cref="Expression"/>.
        /// </summary>
        /// <returns>The resulting <see cref="TypeRef"/> or <see cref="UnresolvedRef"/>.</returns>
        public override TypeRefBase EvaluateType(bool withoutConstants)
        {
            return new AnonymousMethodRef(this);
        }

        #endregion

        #region /* FORMATTING */

        /// <summary>
        /// True if the anonymous method has braces by default.
        /// </summary>
        public virtual bool HasBracesDefault
        {
            get { return true; }
        }

        /// <summary>
        /// True if the anonymous method is delimited by braces.
        /// </summary>
        public bool HasBraces
        {
            get { return (_body != null && _body.HasBraces); }
            set
            {
                CreateBody().HasBraces = value;

                // If we just turned on braces, force a terminator on any expression in the body
                if (value && _body.Count > 0)
                    _body[0].HasTerminator = true;
            }
        }

        /// <summary>
        /// Reformat the <see cref="Block"/> body.
        /// </summary>
        public virtual void ReformatBlock()
        {
            if (_body != null)
            {
                if (!_body.IsGroupingSet)
                    _body.SetFormatFlag(FormatFlags.Grouping, HasBracesDefault);
                IsSingleLine = (IsSingleLineDefault && _body.IsSingleLineDefault && _body.Count < 2);
                if (_body.Count == 0)
                    _body.SetNewLines(0);
            }
        }

        /// <summary>
        /// Determines if the code object only requires a single line for display.
        /// </summary>
        public override bool IsSingleLine
        {
            get { return (base.IsSingleLine && (_body == null || (!_body.IsFirstOnLine && _body.IsSingleLine))); }
            set
            {
                // Make sure there's a body, and set its IsFirstOnLine and IsSingleLine properties appropriately
                CreateBody();
                _body.IsFirstOnLine = !value;
                _body.IsSingleLine = value;
            }
        }

        #endregion

        #region /* RENDERING */

        protected override void AsTextAfter(CodeWriter writer, RenderFlags flags)
        {
            if (!flags.HasFlag(RenderFlags.Description))
            {
                base.AsTextAfter(writer, flags);
                if (_body != null)
                {
                    // Increase the indent level for the body (unless disabled)
                    if (!HasNoIndentation)
                        writer.BeginIndentOnNewLineRelativeToParentOffset(this, true);
                    _body.AsText(writer, flags);
                    if (!HasNoIndentation)
                        writer.EndIndentation(this);
                }
            }
        }

        public override void AsText(CodeWriter writer, RenderFlags flags)
        {
            // This method is overridden so that any parens around the anonymous method will be rendered with the close
            // paren after the body (after AsTextAfter).  While we're at it, prefix support is omitted.
            int newLines = NewLines;
            if (newLines > 0)
            {
                if (!flags.HasFlag(RenderFlags.SuppressNewLine))
                    writer.WriteLines(newLines);
            }
            else if (flags.HasFlag(RenderFlags.PrefixSpace))
                writer.Write(" ");

            RenderFlags passFlags = (flags & RenderFlags.PassMask);
            AsTextBefore(writer, passFlags | RenderFlags.IsPrefix);
            UpdateLineCol(writer, flags);

            // Increase the indent level for any newlines that occur within the expression
            bool increaseIndent = flags.HasFlag(RenderFlags.IncreaseIndent);
            if (increaseIndent)
                writer.BeginIndentOnNewLine(this);

            bool hasParens = HasParens;
            if (hasParens)
                writer.Write(ParseTokenStartGroup);
            AsTextExpression(writer, passFlags | (flags & (RenderFlags.Attribute | RenderFlags.HasDotPrefix | RenderFlags.Declaration)));
            if (HasTerminator && !flags.HasFlag(RenderFlags.Description))
            {
                writer.Write(Statement.ParseTokenTerminator);
                CheckForAlignment(writer);  // Check for alignment of any EOL comments
            }
            if (!flags.HasFlag(RenderFlags.NoEOLComments))
                AsTextEOLComments(writer, flags);

            AsTextAfter(writer, passFlags | (flags & RenderFlags.NoPostAnnotations));
            if (hasParens)
            {
                if (IsEndFirstOnLine)
                    writer.WriteLine();
                writer.Write(ParseTokenEndGroup);
            }

            if (increaseIndent)
                writer.EndIndentation(this);
        }

        public override void AsTextExpression(CodeWriter writer, RenderFlags flags)
        {
            RenderFlags passFlags = (flags & RenderFlags.PassMask);
            if (!flags.HasFlag(RenderFlags.Description))
            {
                writer.SetParentOffset();
                UpdateLineCol(writer, flags);
                writer.Write(ParseToken);
                bool showParens = (_parameters != null || HasInfixComments);
                if (showParens)
                    writer.Write(ParseTokenStart);
                AsTextInfixComments(writer, 0, flags);
                writer.WriteList(_parameters, passFlags, this);
                if (showParens)
                    writer.Write(ParseTokenEnd);
            }
            else
            {
                // If rendering as a description, just render as a delegate type
                GetReturnType().AsText(writer, passFlags | RenderFlags.IsPrefix);
                writer.Write(ParseToken);
                writer.Write(ParseTokenStart);
                writer.WriteList(_parameters, passFlags, this);
                if (IsEndFirstOnLine)
                    writer.WriteLine();
                writer.Write(ParseTokenEnd);
            }
        }

        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Common Development and Distribution License (CDDL)

About the Author

KenBeckett
Software Developer (Senior)
United States United States
I've been writing software since the late 70's, currently focusing mainly on C#.NET. I also like to travel around the world, and I own a Chocolate Factory (sadly, none of my employees are oompa loompas).

| Advertise | Privacy | Mobile
Web02 | 2.8.140709.1 | Last Updated 2 Dec 2012
Article Copyright 2012 by KenBeckett
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid