/// <summary>
        /// Creates a field that holds <see cref="QueryTypeProperties{T}"/> for the module.
        /// </summary>
        protected FieldDeclarationSyntax CreatePropertiesField(
            Module module, string resultClassName, FieldDeclarationSyntax propsField, SortType?sortType)
        {
            var queryTypePropertiesType = SyntaxEx.GenericName("QueryTypeProperties", resultClassName);

            var propertiesInitializer = SyntaxEx.ObjectCreation(
                queryTypePropertiesType,
                SyntaxEx.Literal(module.Name),
                SyntaxEx.Literal(module.Prefix),
                module.QueryType == null
                    ? (ExpressionSyntax)SyntaxEx.NullLiteral()
                    : SyntaxEx.MemberAccess("QueryType", module.QueryType.ToString()),
                sortType == null
                    ? (ExpressionSyntax)SyntaxEx.NullLiteral()
                    : SyntaxEx.MemberAccess("SortType", sortType.ToString()),
                CreateTupleListExpression(GetBaseParameters(module)),
                propsField == null ? (ExpressionSyntax)SyntaxEx.NullLiteral() : (NamedNode)propsField,
                resultClassName == "object"
                    ? (ExpressionSyntax)SyntaxEx.LambdaExpression("_", SyntaxEx.NullLiteral())
                    : SyntaxEx.MemberAccess(resultClassName, "Parse"));

            return(SyntaxEx.FieldDeclaration(
                       new[] { SyntaxKind.PrivateKeyword, SyntaxKind.StaticKeyword, SyntaxKind.ReadOnlyKeyword },
                       queryTypePropertiesType, ClassNameBase + "Properties", propertiesInitializer));
        }
        protected override TypeSyntax GenerateMethodResultType()
        {
            if (m_voidResult)
            {
                return(Syntax.ParseTypeName("void"));
            }

            var resultType = Syntax.ParseTypeName(ResultClassName);

            if (m_listResult)
            {
                resultType = SyntaxEx.GenericName("IEnumerable", resultType);
            }

            return(resultType);
        }
        /// <summary>
        /// Creates an entry method, that is used to execute query for normal modules
        /// or that can be used as a base for a query for query modules.
        /// </summary>
        protected void GenerateMethod(
            Module module, IEnumerable <Parameter> methodParameters, string resultClassName,
            FieldDeclarationSyntax propsField, string fileName, bool nullableParameters, SortType?sortType)
        {
            var propertiesField = CreatePropertiesField(module, resultClassName, propsField, sortType);

            ExpressionSyntax queryParameters = SyntaxEx.Invocation(
                SyntaxEx.MemberAccess("QueryParameters", SyntaxEx.GenericName("Create", resultClassName)));

            var queryParametersLocal = SyntaxEx.LocalDeclaration("var", "queryParameters", queryParameters);

            var documentationElements = new List <XmlElementSyntax>();

            var summary = SyntaxEx.DocumentationSummary(module.Description);

            documentationElements.Add(summary);

            var parameters = new List <ParameterSyntax>();
            IList <StatementSyntax> statements = new List <StatementSyntax>();

            statements.Add(queryParametersLocal);

            methodParameters = methodParameters
                               .Where(p => !p.Deprecated)
                               .Where(p => p.Name != "continue")
                               .OrderByDescending(p => p.Required);

            foreach (var methodParameter in methodParameters)
            {
                var nullable      = nullableParameters && !methodParameter.Required;
                var typeName      = Wiki.TypeManager.GetTypeName(methodParameter, ClassNameBase, nullable, false);
                var parameterName = GetPropertyName(methodParameter.Name);

                // this type cannot be processed
                if (typeName == null)
                {
                    continue;
                }

                var parameter = SyntaxEx.Parameter(typeName, parameterName, nullable ? SyntaxEx.NullLiteral() : null);

                parameters.Add(parameter);

                ExpressionSyntax valueExpression = (NamedNode)parameter;

                if (nullable && typeName.EndsWith("?"))
                {
                    valueExpression = SyntaxEx.MemberAccess(valueExpression, "Value");
                }

                ExpressionSyntax newQueryParameters;
                if (typeName == "System.IO.Stream")
                {
                    newQueryParameters = SyntaxEx.Invocation(
                        SyntaxEx.MemberAccess(queryParametersLocal, "AddFile"),
                        SyntaxEx.Literal(methodParameter.Name),
                        valueExpression);
                }
                else
                {
                    newQueryParameters = SyntaxEx.Invocation(
                        SyntaxEx.MemberAccess(queryParametersLocal, "AddSingleValue"),
                        SyntaxEx.Literal(methodParameter.Name),
                        SyntaxEx.Invocation(SyntaxEx.MemberAccess(valueExpression, "ToQueryString")));
                }
                var queryParametersAssignment = SyntaxEx.Assignment(queryParametersLocal, newQueryParameters);

                if (nullable)
                {
                    var assignmentWithCheck = SyntaxEx.If(
                        SyntaxEx.NotEquals((NamedNode)parameter, SyntaxEx.NullLiteral()), queryParametersAssignment);

                    statements.Add(assignmentWithCheck);
                }
                else
                {
                    statements.Add(queryParametersAssignment);
                }

                var parameterDocumentation = SyntaxEx.DocumentationParameter(parameterName, methodParameter.Description);

                documentationElements.Add(parameterDocumentation);
            }

            statements = GenerateMethodBody(
                SyntaxEx.ObjectCreation(
                    SyntaxEx.GenericName("QueryProcessor", resultClassName),
                    SyntaxFactory.IdentifierName("m_wiki"),
                    (NamedNode)propertiesField), (NamedNode)queryParametersLocal, statements);

            var modifiers = new List <SyntaxKind> {
                SyntaxKind.PublicKeyword
            };

            if (statements == null)
            {
                modifiers.Add(SyntaxKind.AbstractKeyword);
            }

            var method = SyntaxEx.MethodDeclaration(
                modifiers, GenerateMethodResultType(), ClassNameBase, parameters, statements)
                         .WithLeadingTrivia(SyntaxFactory.Trivia(SyntaxEx.DocumentationComment(documentationElements)));

            AddMembersToClass(fileName, propertiesField, method);
        }
        protected override void GenerateInternal(Module module)
        {
            m_selectClassName  = ClassNameBase + "Select";
            m_whereClassName   = ClassNameBase + "Where";
            m_orderByClassName = ClassNameBase + "OrderBy";

            var parameters = module.Parameters.ToList();

            var sortParameters = RemoveAndReturnByNames(parameters, "sort", "dir");

            var methodParameters = parameters.RemoveAndReturn(p => p.Required);

            // don't belong anywhere, are used in a special way
            RemoveAndReturnByNames(parameters, "continue", "offset", "limit", "prop");

            var whereParameters = parameters;

            var selectClass  = GenerateSelect(module.PropertyGroups, module.Name == "revisions");
            var whereClass   = GenerateWhere(whereParameters);
            var orderByClass = GenerateOrderBy(sortParameters, module.PropertyGroups.SelectMany(g => g.Properties));

            var codeUnit = SyntaxEx.CompilationUnit(
                SyntaxEx.NamespaceDeclaration(Wiki.EntitiesNamespace, selectClass, whereClass, orderByClass),
                "System", "System.Globalization", "System.Xml.Linq", "LinqToWiki", "LinqToWiki.Collections",
                "LinqToWiki.Internals");

            Wiki.Files.Add(ClassNameBase, codeUnit);

            string queryTypeName = "WikiQuery";
            var    queryTypeGenericParameters = new List <string> {
                m_whereClassName, m_selectClassName
            };

            if (orderByClass != null)
            {
                queryTypeName += "Sortable";
                queryTypeGenericParameters.Insert(1, m_orderByClassName);
            }

            if (module.Generator)
            {
                queryTypeName += "Generator";
                queryTypeGenericParameters.Insert(0, Wiki.Names.Page);
            }

            m_queryType = SyntaxEx.GenericName(queryTypeName, queryTypeGenericParameters);

            SortType?sortType = null;

            var dirParameter = sortParameters.SingleOrDefault(p => p.Name == "dir");

            if (dirParameter != null)
            {
                var type = (EnumParameterType)dirParameter.Type;
                if (type.Values.Any(x => x == "ascending"))
                {
                    sortType = SortType.Ascending;
                }
                else if (type.Values.Any(x => x == "newer"))
                {
                    sortType = SortType.Newer;
                }
            }

            GenerateMethod(module, methodParameters, m_selectClassName, m_selectProps, MethodClassName, false, sortType);
        }