Beispiel #1
0
        public override List <IDictionary <string, object> > QueryDynamicTable(string tableName, DynamicTableFilter filter, ICollection <DynamicTableSort> sorts, out int totalCount, string tableAlias = null, DynamicQueryCallbackProvider queryCallbacks = null, int?hitsPerPage = null, int?page = null)
        {
            var desc = DescribeTable(tableName, true, out _);

            using (Facade.UseConnection(out DbCommand cmd))
            {
                var colFx = new Func <List <TableColumnDefinition>, string, string, TableColumnResolveCallback, string>((cols, colName, alias, cb) =>
                {
                    var retVal = BrowseColumns(cols, colName, alias, cb, out var tmp);
                    if (tmp is AliasQualifiedColumn ali)
                    {
                        retVal = ali.FullQualifiedName;
                    }

                    return(retVal);
                });
                var rawQuery = filter != null?BuildWhereClause(desc, filter, cmd, 0, tableAlias, queryCallbacks) : null;

                var rawOrdering = (sorts != null && sorts.Count != 0) ? string.Join(", ", from t in sorts select $"{colFx(desc, t.ColumnName, tableAlias, queryCallbacks?.FQColumnQuery)} {t.SortOrder}") : null;
                var rawCols     = string.Join(", ", from t in desc select SyntaxProvider.FullQualifyColumn(tableAlias, t.ColumnName));
                var finalQuery  = $@"select {rawCols}{(!string.IsNullOrEmpty(queryCallbacks?.CustomQuerySelection) ? $", {queryCallbacks?.CustomQuerySelection}" : "")}, [$$totalRecordCount$$] = count(*) over() from [{tableName}] {(!string.IsNullOrEmpty(tableAlias) ? $"[{tableAlias}]" : "")} {queryCallbacks?.CustomQueryTablePart} {(!string.IsNullOrEmpty(rawQuery) ? $"where {rawQuery}" : "")} {(!string.IsNullOrEmpty(rawOrdering) ? $"order by {rawOrdering}" : "")}
{(hitsPerPage != null && page != null ? $@"offset {hitsPerPage * (page - 1)} rows
fetch next {hitsPerPage} rows only" : "")}";
                LogEnvironment.LogDebugEvent(null, finalQuery, (int)LogSeverity.Report, "ITVComponents.EFRepo.SqlServer.SqlDynamicDataAdapter");
                cmd.CommandText = finalQuery;
                var retVal = cmd.ExecuteReader().ToDictionaries(true).ToList();
                totalCount = 0;
                if (retVal.Count != 0)
                {
                    totalCount = Convert.ToInt32(retVal[0]["$$totalRecordCount$$"]);
                }

                return(retVal);
            }
        }
Beispiel #2
0
        private void ReloadOperators()
        {
            List <string> operatorList = new List <string>();

            SyntaxProvider?.GetComparisonOperators(operatorList);

            FillOperators(operatorList);
        }
Beispiel #3
0
        private string BrowseColumns(ICollection <TableColumnDefinition> desc, string columnName, string tableAlias, TableColumnResolveCallback getCustomColumnDef, out TableColumnDefinition def)
        {
            def = desc.FirstOrDefault(n => n.ColumnName.Equals(columnName, StringComparison.OrdinalIgnoreCase));
            if (def != null)
            {
                return(SyntaxProvider.FullQualifyColumn(tableAlias, def.ColumnName)); // $"{(tableAlias != null ? $"[{tableAlias}]." : "")}[{col.ColumnName}]";
            }

            return(getCustomColumnDef?.Invoke(columnName, out def));
        }
Beispiel #4
0
        public void TokenDefinition_IsOperator2()
        {
            string[] input =
            {
                "{ 3 }"
            };

            var syntaxRegex = RegexWrapper.DefaultWrap(SyntaxProvider.GetPattern());

            var tokenDefinition = new TokenDefinition(TokenType.SyntaxOperator, syntaxRegex);
            var tokensGenerated = _tokenizer.Tokenize(input).ToList();

            var operatorTokens = tokensGenerated.Where(t => t.TokenType == TokenType.SyntaxOperator).ToList();

            Assert.IsTrue(operatorTokens.Any());
            Assert.AreEqual(2, operatorTokens.Count());
            Assert.IsNotNull(operatorTokens.FirstOrDefault(t => t.Value == "{"));
            Assert.IsNotNull(operatorTokens.FirstOrDefault(t => t.Value == "}"));
        }
 private void ReportMissingMethod(SyntaxProvider syntaxProvider, Property property, string process)
 {
     syntaxProvider.Context.ReportDiagnostic(DiagnosticSeverity.Warning, $"{property} ({property.Type};{property.CollectionType}) has no {process} method associated with its type", property.DeclarationSyntax);
 }
        private bool TryReadProperty(string streamName, string dataName, Property property, List <Property> properties, CodeBuilder builder, SyntaxProvider syntaxProvider, Method readMethod = null)
        {
            if (readMethod is null)
            {
                syntaxProvider.Methods.TryGetReadMethod(property, out readMethod);
            }

            var context = new MethodBuildingContext(streamName, dataName, property, properties, builder, readMethod, syntaxProvider.Methods, syntaxProvider.Context);

            if (property.Reading.Execute(context))
            {
                return(true);
            }

            // Attributes behavior
            for (int i = 0; i < property.Attributes.Length; i++)
            {
                if (property.Attributes[i].ModifyDeserialization(context))
                {
                    property.Read.Execute(context);
                    return(true);
                }
            }

            // Default behavior
            if (readMethod is null)
            {
                ReportMissingMethod(syntaxProvider, property, "deserialization");
                return(false);
            }
            builder.Line($"{dataName} = {streamName}.{readMethod}();");

            property.Read.Execute(context);
            return(true);
        }
        private bool TryReadPropertyCollection(string streamName, string dataName, Property property, List <Property> properties, CodeBuilder builder, SyntaxProvider syntaxProvider)
        {
            // If there is a method for writing the whole collection, use it instead
            if (syntaxProvider.Methods.TryGetReadMethod(property, collection: true, out Method collectionReadMethod))
            {
                return(TryReadProperty(streamName, dataName, property, properties, builder, syntaxProvider, collectionReadMethod));
            }

            syntaxProvider.Methods.TryGetReadMethod(varInt, out Method readMethod);
            var context = new MethodBuildingContext(streamName, dataName, property, properties, builder, readMethod, syntaxProvider.Methods, syntaxProvider.Context);

            if (property.Reading.Execute(context))
            {
                return(true);
            }

            // Attributes behavior
            for (int i = 0; i < property.Attributes.Length; i++)
            {
                if (property.Attributes[i].ModifyCollectionPrefixDeserialization(context))
                {
                    goto LOOP;
                }
            }

            // Default behavior
            if (readMethod is null)
            {
                ReportMissingMethod(syntaxProvider, property, "deserialization");
                return(false);
            }
            string getLength = $"{streamName}.{readMethod}()";

            builder.Line($"{dataName} = {property.NewCollection(getLength)};");

LOOP:
            builder.Statement($"for (int i = 0; i < {dataName}.{property.Length}; i++)");

            context = new MethodBuildingContext(streamName, dataName + "[i]", property, properties, builder, readMethod, syntaxProvider.Methods, syntaxProvider.Context);

            // Attributes behavior
            for (int i = 0; i < property.Attributes.Length; i++)
            {
                if (property.Attributes[i].ModifyDeserialization(context))
                {
                    goto END_LOOP;
                }
            }

            // Default behavior
            if (readMethod is null)
            {
                ReportMissingMethod(syntaxProvider, property, "deserialization");
                return(false);
            }
            builder.Line($"{dataName}[i] = {streamName}.{readMethod}();");

END_LOOP:
            builder.EndScope();

            property.Read.Execute(context);
            return(true);
        }
        private bool TryCreateReadingMethod(CodeBuilder builder, List <Property> properties, SyntaxProvider syntaxProvider)
        {
            string streamName = "stream";

            foreach (Property property in properties)
            {
                if (property.IsCollection)
                {
                    if (!TryReadPropertyCollection(streamName, property.Name, property, properties, builder, syntaxProvider))
                    {
                        return(false);
                    }
                }
                else
                {
                    if (!TryReadProperty(streamName, property.Name, property, properties, builder, syntaxProvider))
                    {
                        return(false);
                    }
                }
            }

            return(true);
        }
 private bool TryCreatePopulateMethod(CodeBuilder builder, List <Property> properties, SyntaxProvider syntaxProvider)
 {
     return(TryCreateReadingMethod(builder, properties, syntaxProvider));
 }
        private bool TrySerializeProperty(string streamName, Property property, List <Property> properties, CodeBuilder builder, SyntaxProvider syntaxProvider, Method writeMethod = null)
        {
            if (writeMethod is null)
            {
                syntaxProvider.Methods.TryGetWriteMethod(property, out writeMethod);
            }

            var context = new MethodBuildingContext(streamName, property.Name, property, properties, builder, writeMethod, syntaxProvider.Methods, syntaxProvider.Context);

            if (property.Writing.Execute(context))
            {
                return(true);
            }

            // Attributes behavior
            for (int i = 0; i < property.Attributes.Length; i++)
            {
                if (property.Attributes[i].ModifySerialization(context))
                {
                    property.Written.Execute(context);
                    return(true);
                }
            }

            // Default behavior
            if (writeMethod is null)
            {
                ReportMissingMethod(syntaxProvider, property, "serialization");
                return(false);
            }
            builder.Line($"{streamName}.{writeMethod}({property});");

            property.Written.Execute(context);
            return(true);
        }
        private string ProcessClass(INamedTypeSymbol classSymbol, List <Property> fields, SyntaxProvider syntaxProvider)
        {
            fields.Sort((a, b) => a.Order.CompareTo(b.Order));

            string @namespace = classSymbol.ContainingNamespace.ToDisplayString();
            string className  = classSymbol.IsGenericType ? $"{classSymbol.Name}<{string.Join(", ", classSymbol.TypeParameters.Select(parameter => parameter.Name))}>" : classSymbol.Name;

            var  interfaces  = classSymbol.AllInterfaces;
            bool clientbound = interfaces.Any(@interface => @interface.Name == Vocabulary.ClientboundInterface);
            bool serverbound = interfaces.Any(@interface => @interface.Name == Vocabulary.ServerboundInterface);

            var methods = classSymbol.GetMembers().OfType <IMethodSymbol>();

            var source = new CodeBuilder();

            var usings = new HashSet <string>();

            foreach (SyntaxReference declaration in classSymbol.DeclaringSyntaxReferences)
            {
                SyntaxNode root = declaration.GetSyntax().GetRoot();
                foreach (var usingDirective in root.DescendantNodes().OfType <UsingDirectiveSyntax>())
                {
                    usings.Add(usingDirective.Name.ToString());
                }
            }

            usings.Add("Obsidian.Net");
            usings.Add("Obsidian.Utilities");
            usings.Add("System.Runtime.CompilerServices");

            foreach (string @using in usings.OrderBy(s => s))
            {
                source.Using(@using);
            }

            source.Line();

            source.Namespace(@namespace);
            source.Type(classSymbol);

            var bodySource = CodeBuilder.WithIndentationOf(source.Indentation + 1);

            // Serialize(MinecraftStream stream)
            bool createSerializationMethod =
                clientbound &&
                !methods.Any(m => m.Name == "Serialize" && m.Parameters.Length == 1 && m.Parameters[0].Type.Name == "MinecraftStream") &&
                TryCreateSerializationMethod(bodySource, fields, syntaxProvider);

            if (createSerializationMethod)
            {
                source.XmlSummary("Serializes data from this packet into <see cref=\"MinecraftStream\"/>.\n<b>AUTOGENERATED</b>");
                source.XmlParam("stream", "Target stream that this packet's data is written to.");

                source.Method("public void Serialize(MinecraftStream stream)");
                source.Append(bodySource);
                source.EndScope();
            }

            bodySource = CodeBuilder.WithIndentationOf(source.Indentation + 1);

            if (serverbound)
            {
                if (createSerializationMethod)
                {
                    source.Line();
                }

                // Deserialize(byte[] data)
                if (!methods.Any(m => m.Name == "Deserialize" && m.Parameters.Length == 1 && m.Parameters[0].Type.ToDisplayString() == "byte[]"))
                {
                    source.XmlSummary($"Deserializes byte data into <see cref=\"{classSymbol.Name}\"/> packet.\n<b>AUTOGENERATED</b>");
                    source.XmlParam("data", "Data used to populate the packet.");
                    source.XmlReturns("Deserialized packet.");

                    source.Method($"public static {className} Deserialize(byte[] data)");
                    source.Line("using var stream = new MinecraftStream(data);");
                    source.Line("return Deserialize(stream);");
                    source.EndScope();
                }

                // Deserialize(MinecraftStream stream)
                if (!methods.Any(m => m.Name == "Deserialize" && m.Parameters.Length == 1 && m.Parameters[0].Type.Name == "MinecraftStream"))
                {
                    source.Line();
                    source.XmlSummary($"Deserializes data from a <see cref=\"MinecraftStream\"/> into <see cref=\"{classSymbol.Name}\"/> packet.\n<b>AUTOGENERATED</b>");
                    source.XmlParam("stream", "Stream that is read from to populate the packet.");
                    source.XmlReturns("Deserialized packet.");

                    source.Method($"public static {className} Deserialize(MinecraftStream stream)");
                    source.Line($"var packet = new {className}();");
                    source.Line("packet.Populate(stream);");
                    source.Line("return packet;");
                    source.EndScope();
                }
            }

            bodySource = CodeBuilder.WithIndentationOf(source.Indentation + 1);

            if (serverbound && TryCreatePopulateMethod(bodySource, fields, syntaxProvider))
            {
                // Populate(byte[] data)
                if (!methods.Any(m => m.Name == "Populate" && m.Parameters.Length == 1 && m.Parameters[0].Type.ToDisplayString() == "byte[]"))
                {
                    source.Line();
                    source.XmlSummary($"Populates this packet with data from a <see cref=\"byte\"/>[] buffer.");
                    source.XmlParam("data", "Data used to populate this packet.");
                    source.Method("public void Populate(byte[] data)");
                    source.Line("using var stream = new MinecraftStream(data);");
                    source.Line("Populate(stream);");
                    source.EndScope();
                }

                // Populate(MinecraftStream stream)
                if (!methods.Any(m => m.Name == "Populate" && m.Parameters.Length == 1 && m.Parameters[0].Type.Name == "MinecraftStream"))
                {
                    source.Line();
                    source.XmlSummary("Populates this packet with data from a <see cref=\"MinecraftStream\"/>.");
                    source.XmlParam("stream", "Stream used to populate this packet.");

                    source.Method("public void Populate(MinecraftStream stream)");
                    source.Append(bodySource);
                    source.EndScope();
                }
            }

            bodySource.Clear();

            source.EndScope(); // End of type
            source.EndScope(); // End of namespace

            return(source.ToString());
        }
Beispiel #12
0
        private bool TryCreateDeserializationMethod(CodeBuilder builder, string className, List <Field> fields, SyntaxProvider syntaxProvider)
        {
            builder.Line($"var packet = new {className}();");
            foreach (var field in fields)
            {
                string elementType = field.TypeName, elementName = field.Name;
                if (field.IsArray)
                {
                    elementName += "[i]";
                    string lengthProperty;
                    if (field.TypeName.EndsWith("[]"))
                    {
                        lengthProperty = "Length";
                        elementType    = field.TypeName.Substring(0, field.TypeName.Length - 2);
                    }
                    else
                    {
                        lengthProperty = "Count";
                        elementType    = field.TypeName.Substring(5, field.TypeName.Length - 6);
                    }

                    if (field.IsVarLength)
                    {
                        elementType += "_Var";
                    }
                    if (field.IsAbsolute)
                    {
                        elementType += "_Abs";
                    }

                    string countValue;
                    if (field.FixedLength >= 0)
                    {
                        countValue = field.FixedLength.ToString();
                    }
                    else if (field.CountType is not null)
                    {
                        if (!syntaxProvider.ReadMethods.TryGetValue(field.CountType, out string readCountMethod))
                        {
                            // CountType has no read method
                            syntaxProvider.Context.ReportDiagnostic(DiagnosticDescriptors.Create(DiagnosticSeverity.Warning, $"{field.Name} ({field.TypeName})({elementType}) has no deserialization method associated with its count type", field.Declaration));
                            return(false);
                        }

                        countValue = $"stream.{readCountMethod}();";
                    }
                    else
                    {
                        countValue = "stream.ReadVarInt()";
                    }
                    builder.Line(field.TypeName.EndsWith("[]") ? $"packet.{field.Name} = new {elementType}[{countValue}];" : $"packet.{field.Name} = new {field.TypeName}({countValue});");

                    builder.Statement($"for (int i = 0; i < packet.{field.Name}.{lengthProperty}; i++)");
                }

                if (TryGetMethod(elementType, syntaxProvider.ReadMethods, out string methodName))
                {
                    methodName = $"stream.{methodName}()";

                    if (field.OriginalType is not null)
                    {
                        if (field.IsGeneric)
                        {
                            string tempName = $"temp{field.Name}";
                            builder.Line($"var {tempName} = {methodName};");
                            methodName = $"System.Runtime.CompilerServices.Unsafe.As<{field.ActualType}, {field.OriginalType}>(ref {tempName})";
                        }
                        else
                        {
                            methodName = $"({field.ActualType}){methodName}";
                        }
                    }

                    builder.Line($"packet.{elementName} = {methodName};");
                }
                else
                {
                    // Creating serialization method failed
#if DEBUG
                    syntaxProvider.Context.ReportDiagnostic(DiagnosticDescriptors.Create(DiagnosticSeverity.Warning, $"{field.Name} ({field.TypeName})({elementType}) has no deserialization method associated with it", field.Declaration));
#endif
                    return(false);
                }

                if (field.IsArray)
                {
                    // End the for loop
                    builder.EndScope();
                }
            }
            builder.Line("return packet;");
            return(true);
        }
Beispiel #13
0
        public override void AlterOrCreateTable(string tableName, TableColumnDefinition[] columns, bool forceDeleteColumn = false, bool useTransaction = true, bool whatIf = false)
        {
            var lenFx = new Func <TableColumnDefinition, string>(def =>
            {
                var ln = def.DataLength ?? -1;
                return($"{(def.Type.LengthRequired ? $"({(ln != -1 ? ln.ToString() : "max")})" : "")}");
            });
            var pos = 1;

            foreach (var col in columns)
            {
                if (col.Type == null)
                {
                    col.Type = SyntaxProvider.GetAppropriateType(col, true);
                }

                if (col.Position == 0)
                {
                    col.Position = pos++;
                }
            }

            bool transactionCreated = false;

            if (useTransaction && (Facade.CurrentTransaction == null))
            {
                if (!whatIf)
                {
                    Facade.BeginTransaction();
                }
                else
                {
                    LogEnvironment.LogEvent("Table-Modification would start a new transaction!", LogSeverity.Report, "ITVComponents.EFRepo.SqlServer.SqlDynamicDataAdapter");
                }

                transactionCreated = true;
            }

            try
            {
                List <string> dropIndices       = new List <string>();
                List <string> addIndices        = new List <string>();
                List <string> constraintChanges = new List <string>();
                var           cmd = "";
                if (TableExists(tableName))
                {
                    var           orig      = DescribeTable(tableName, false, out var definitionEditable);
                    var           differ    = DynamicTableHelper.CompareDefinitions(orig, columns);
                    List <string> addCols   = new List <string>();
                    List <string> dropCols  = new List <string>();
                    List <string> alterCols = new List <string>();
                    foreach (var item in differ)
                    {
                        if (item.Table2Def == null && !forceDeleteColumn)
                        {
                            if (!whatIf)
                            {
                                throw new InvalidOperationException("Column removal must be explicitly forced!");
                            }
                            else
                            {
                                LogEnvironment.LogEvent("Table-Modification would fail due to a column-removal with forceDeleteColumn=false!", LogSeverity.Error, "ITVComponents.EFRepo.SqlServer.SqlDynamicDataAdapter");
                            }
                        }

                        if (item.Table2Def == null && !definitionEditable)
                        {
                            if (!whatIf)
                            {
                                throw new InvalidOperationException("Column removal is not supported on this table, because the index-configuration is not supported!");
                            }
                            else
                            {
                                LogEnvironment.LogEvent("Table-Modification would fail due to a column-removal on a table with an unsupported index-configuration!", LogSeverity.Error, "ITVComponents.EFRepo.SqlServer.SqlDynamicDataAdapter");
                            }
                        }

                        if (item.Table2Def == null)
                        {
                            dropCols.Add($"[{item.ColumnName}]");
                        }

                        if (item.Table1Def != null && item.Table2Def != null)
                        {
                            if (!item.Table1Def.DataType.Equals(item.Table2Def.DataType, StringComparison.OrdinalIgnoreCase))
                            {
                                if (!whatIf)
                                {
                                    throw new InvalidOperationException("Changing the DataType is not supported!");
                                }
                                else
                                {
                                    LogEnvironment.LogEvent("Table-Modification would fail, because DataTypes must not be altered!", LogSeverity.Error, "ITVComponents.EFRepo.SqlServer.SqlDynamicDataAdapter");
                                }
                            }

                            if (item.Table1Def.DataLength != item.Table2Def.DataLength || item.Table1Def.Nullable != item.Table2Def.Nullable)
                            {
                                alterCols.Add($"[{item.ColumnName}] {item.Table2Def.DataType}{lenFx(item.Table2Def)} {(item.Table2Def.Nullable ? "" : "NOT ")}NULL");
                            }

                            if ((item.Table1Def.HasIndex != item.Table2Def.HasIndex || item.Table1Def.IsUniqueKey != item.Table2Def.IsUniqueKey) && item.Table1Def.IsForeignKey == item.Table2Def.IsForeignKey && !item.Table2Def.IsForeignKey)
                            {
                                if (item.Table1Def.IsPrimaryKey != item.Table2Def.IsPrimaryKey)
                                {
                                    if (!whatIf)
                                    {
                                        throw new InvalidOperationException("Primary keys must not be altered!");
                                    }
                                    else
                                    {
                                        LogEnvironment.LogEvent("Table-Modification would fail, because Primary-Key Columns may not be altered!", LogSeverity.Error, "ITVComponents.EFRepo.SqlServer.SqlDynamicDataAdapter");
                                    }
                                }

                                if (!definitionEditable)
                                {
                                    if (!whatIf)
                                    {
                                        throw new InvalidOperationException("Index-Changes not supported on this table. Use Management studio to alter table.");
                                    }
                                    else
                                    {
                                        LogEnvironment.LogEvent("Table-Modification would fail, because index-configuration of the table is not supported!", LogSeverity.Error, "ITVComponents.EFRepo.SqlServer.SqlDynamicDataAdapter");
                                    }
                                }

                                if (!item.Table1Def.IsPrimaryKey)
                                {
                                    if (item.Table1Def.IsUniqueKey)
                                    {
                                        dropIndices.Add($"UQ_{tableName}_{item.ColumnName}");
                                    }
                                    else if (item.Table1Def.HasIndex)
                                    {
                                        dropIndices.Add($"CX_{tableName}_{item.ColumnName}");
                                    }

                                    if (item.Table2Def.IsUniqueKey)
                                    {
                                        addIndices.Add($"Create UNIQUE INDEX [UQ_{tableName}_{item.ColumnName}] on [{tableName}] ([{item.ColumnName}] ASC)");
                                    }
                                    else if (item.Table2Def.HasIndex)
                                    {
                                        addIndices.Add($"Create NONCLUSTERED INDEX [CX_{tableName}_{item.ColumnName}] on [{tableName}] ({item.ColumnName})");
                                    }
                                }
                            }

                            if (item.Table2Def.IsForeignKey != item.Table1Def.IsForeignKey)
                            {
                                if (!definitionEditable)
                                {
                                    if (!whatIf)
                                    {
                                        throw new InvalidOperationException("Constraint-Changes not supported on this table. Use Management studio to alter table.");
                                    }
                                    else
                                    {
                                        LogEnvironment.LogEvent("Table-Modification would fail, because index-configuration of the table is not supported!", LogSeverity.Error, "ITVComponents.EFRepo.SqlServer.SqlDynamicDataAdapter");
                                    }
                                }

                                if (item.Table1Def.IsForeignKey)
                                {
                                    constraintChanges.Add($"Alter Table [{tableName}] drop constraint [FK_{tableName}_{item.Table1Def.Position}_{item.Table1Def.RefTable}_{item.Table1Def.RefColumn}]");
                                }
                                else
                                {
                                    constraintChanges.Add($@"Alter Table [{tableName}] add constraint [FK_{tableName}_{item.Table2Def.Position}_{item.Table2Def.RefTable}_{item.Table2Def.RefColumn}] Foreign Key ({item.Table2Def.ColumnName})
references [{item.Table2Def.RefTable}] ([{item.Table2Def.RefColumn}])");
                                }
                            }
                        }
                        else if (item.Table2Def != null)
                        {
                            addCols.Add($"[{item.ColumnName}] {item.Table2Def.DataType}{lenFx(item.Table2Def)} {(item.Table2Def.Nullable ? "" : "NOT ")}NULL");
                            if (!item.Table2Def.IsPrimaryKey && !item.Table2Def.IsForeignKey && (item.Table2Def.HasIndex || item.Table2Def.IsUniqueKey))
                            {
                                if (item.Table2Def.IsUniqueKey)
                                {
                                    addIndices.Add($"Create UNIQUE INDEX [UQ_{tableName}_{item.ColumnName}] on [{tableName}] ([{item.ColumnName}] ASC)");
                                }
                                else if (item.Table2Def.HasIndex)
                                {
                                    addIndices.Add($"Create NONCLUSTERED INDEX [CX_{tableName}_{item.ColumnName}] on [{tableName}] ({item.ColumnName})");
                                }
                            }

                            if (item.Table2Def.IsForeignKey)
                            {
                                constraintChanges.Add($@"Alter Table [{tableName}] add constraint [FK_{tableName}_{item.Table2Def.Position}_{item.Table2Def.RefTable}_{item.Table2Def.RefColumn}] Foreign Key ({item.Table2Def.ColumnName})
references [{item.Table2Def.RefTable}] ([{item.Table2Def.RefColumn}])");
                            }
                        }
                    }

                    if (dropCols.Count != 0)
                    {
                        cmd = $"alter table [{tableName}] drop column {string.Join(", ", dropCols)}";
                        if (!whatIf)
                        {
                            Facade.ExecuteSqlRaw(cmd);
                        }
                        else
                        {
                            LogEnvironment.LogEvent($"Table-Modification would execute the following SQL-Command: {{{cmd}}}", LogSeverity.Report, "ITVComponents.EFRepo.SqlServer.SqlDynamicDataAdapter");
                        }
                    }

                    if (alterCols.Count != 0)
                    {
                        foreach (var ac in alterCols)
                        {
                            cmd = $"alter table [{tableName}] alter column {ac}";
                            if (!whatIf)
                            {
                                Facade.ExecuteSqlRaw(cmd);
                            }
                            else
                            {
                                LogEnvironment.LogEvent($"Table-Modification would execute the following SQL-Command: {{{cmd}}}", LogSeverity.Report, "ITVComponents.EFRepo.SqlServer.SqlDynamicDataAdapter");
                            }
                        }
                    }

                    if (addCols.Count != 0)
                    {
                        cmd = $"alter table [{tableName}] add {string.Join(", ", addCols)}";
                        if (!whatIf)
                        {
                            Facade.ExecuteSqlRaw(cmd);
                        }
                        else
                        {
                            LogEnvironment.LogEvent($"Table-Modification would execute the following SQL-Command: {{{cmd}}}", LogSeverity.Report, "ITVComponents.EFRepo.SqlServer.SqlDynamicDataAdapter");
                        }
                    }
                }
                else
                {
                    cmd = $"CREATE TABLE [{tableName}] ({string.Join(", ", from t in columns select $"[{t.ColumnName}] {t.DataType}{lenFx(t)} {(t.Nullable ? "" : "NOT")} NULL {(t.IsPrimaryKey ? "IDENTITY(1,1) PRIMARY KEY" : "")}")})";
                    if (!whatIf)
                    {
                        Facade.ExecuteSqlRaw(cmd);
                    }
                    else
                    {
                        LogEnvironment.LogEvent($"Table-Modification would execute the following SQL-Command: {{{cmd}}}", LogSeverity.Report, "ITVComponents.EFRepo.SqlServer.SqlDynamicDataAdapter");
                    }

                    foreach (var item in columns)
                    {
                        if (!item.IsPrimaryKey && (item.HasIndex || item.IsUniqueKey))
                        {
                            if (item.IsUniqueKey)
                            {
                                addIndices.Add($"Create UNIQUE INDEX [UQ_{tableName}_{item.ColumnName}] on [{tableName}] ([{item.ColumnName}] ASC)");
                            }
                            else if (item.HasIndex)
                            {
                                addIndices.Add($"Create NONCLUSTERED INDEX [CX_{tableName}_{item.ColumnName}] on [{tableName}] ({item.ColumnName})");
                            }
                        }

                        if (item.IsForeignKey)
                        {
                            constraintChanges.Add($@"Alter Table [{tableName}] add constraint [FK_{tableName}_{item.Position}_{item.RefTable}_{item.RefColumn}] Foreign Key ({item.ColumnName})
references [{item.RefTable}] ([{item.RefColumn}])");
                        }
                    }
                }

                if (dropIndices.Count != 0)
                {
                    foreach (var di in dropIndices)
                    {
                        cmd = $"Drop INDEX IF EXISTS [{di}] on [{tableName}]";
                        if (!whatIf)
                        {
                            Facade.ExecuteSqlRaw(cmd);
                        }
                        else
                        {
                            LogEnvironment.LogEvent($"Table-Modification would execute the following SQL-Command: {{{cmd}}}", LogSeverity.Report, "ITVComponents.EFRepo.SqlServer.SqlDynamicDataAdapter");
                        }
                    }
                }

                if (addIndices.Count != 0)
                {
                    foreach (var ai in addIndices)
                    {
                        cmd = ai;
                        if (!whatIf)
                        {
                            Facade.ExecuteSqlRaw(cmd);
                        }
                        else
                        {
                            LogEnvironment.LogEvent($"Table-Modification would execute the following SQL-Command: {{{cmd}}}", LogSeverity.Report, "ITVComponents.EFRepo.SqlServer.SqlDynamicDataAdapter");
                        }
                    }
                }

                if (constraintChanges.Count != 0)
                {
                    foreach (var cc in constraintChanges)
                    {
                        cmd = cc;
                        if (!whatIf)
                        {
                            Facade.ExecuteSqlRaw(cmd);
                        }
                        else
                        {
                            LogEnvironment.LogEvent($"Table-Modification would execute the following SQL-Command: {{{cmd}}}", LogSeverity.Report, "ITVComponents.EFRepo.SqlServer.SqlDynamicDataAdapter");
                        }
                    }
                }

                if (transactionCreated)
                {
                    if (!whatIf)
                    {
                        Facade.CommitTransaction();
                    }
                    else
                    {
                        LogEnvironment.LogEvent($"Table-Modification would commit the started transaction.", LogSeverity.Report, "ITVComponents.EFRepo.SqlServer.SqlDynamicDataAdapter");
                    }
                }
            }
            catch
            {
                if (transactionCreated)
                {
                    if (!whatIf)
                    {
                        Facade.RollbackTransaction();
                    }
                    else
                    {
                        LogEnvironment.LogEvent($"Table-Modification would rollback the started transaction.", LogSeverity.Report, "ITVComponents.EFRepo.SqlServer.SqlDynamicDataAdapter");
                    }
                }

                throw;
            }
        }
        private bool CreateSerializationMethod(StringBuilder builder, List <Field> fields, SyntaxProvider syntaxProvider)
        {
            builder.AppendCode("using var packetStream = new MinecraftStream();");
            foreach (var field in fields)
            {
                string elementType = field.TypeName, elementName = field.Name;
                if (field.IsArray)
                {
                    elementName += "[i]";
                    string lengthProperty;
                    if (field.TypeName.EndsWith("[]"))
                    {
                        lengthProperty = "Length";
                        elementType    = field.TypeName.Substring(0, field.TypeName.Length - 2);
                    }
                    else
                    {
                        lengthProperty = "Count";
                        elementType    = field.TypeName.Substring(5, field.TypeName.Length - 6);
                    }

                    if (field.IsVarLength)
                    {
                        elementType += "_Var";
                    }
                    if (field.IsAbsolute)
                    {
                        elementType += "_Abs";
                    }

                    if (field.FixedLength < 0)
                    {
                        builder.AppendCode($"packetStream.WriteVarInt({field.Name}.{lengthProperty});");
                    }

                    builder.AppendCode($"for (int i = 0; i < {field.Name}.{lengthProperty}; i++)");
                    builder.AppendCode("{");
                    builder.Append('\t');
                }

                if (field.OriginalType is not null)
                {
                    if (field.IsGeneric)
                    {
                        elementName = $"({field.OriginalType})(object){elementName}";
                    }
                    else
                    {
                        elementName = $"({field.OriginalType}){elementName}";
                    }
                }

                if (TryGetMethod(elementType, syntaxProvider.WriteMethods, out string methodName))
                {
                    builder.AppendCode($"packetStream.{methodName}({elementName});");
                }
                else
                {
                    // creating serialization method failed
#if DEBUG
                    syntaxProvider.Context.ReportDiagnostic(DiagnosticDescriptors.Create(DiagnosticSeverity.Warning, $"{field.Name} ({field.TypeName})({elementType}) has no serialization method associated with it", field.Declaration));
#endif
                    return(false);
                }

                if (field.IsArray)
                {
                    // end for loop
                    builder.AppendCode("}");
                }
            }
            builder.AppendCode("stream.Lock.Wait();");
            builder.AppendCode("stream.WriteVarInt(Id.GetVarIntLength() + (int)packetStream.Length);");
            builder.AppendCode("stream.WriteVarInt(Id);");
            builder.AppendCode("packetStream.Position = 0;");
            builder.AppendCode("packetStream.CopyTo(stream);");
            builder.AppendCode("stream.Lock.Release();");
            return(true);
        }
Beispiel #15
0
        public override List <TableColumnDefinition> DescribeTable(string tableName, bool ignoreUnknownTypes, out bool definitionEditable)
        {
            var tmp  = SqlQuery <TableColumnDefinition>(@"SELECT c.COLUMN_NAME ColumnName, c.DATA_TYPE DataType, c.CHARACTER_MAXIMUM_LENGTH DataLength, convert(bit,case c.IS_NULLABLE when 'Yes' then 1 else 0 end) Nullable,
c.ordinal_position Position,
convert(bit, case when ic.object_id is not null then 1 else 0 end) IsIdentity,
convert(bit, case when rc.column_name is not null then 1 else 0 end) IsForeignKey,
rc.table_name RefTable,
rc.column_name RefColumn,
convert(bit, case when k.column_name is not null then 1 else 0 end) HasIndex,
convert(bit, case when pk.type='PK' then 1 else 0 end) IsPrimaryKey,
convert(bit, case when pk.type='UQ' then 1 else 0 end) IsUniqueKey,
convert(bit, case when count(cc.referenced_column_id) > 0 then 1 else 0 end) HasReferences
FROM INFORMATION_SCHEMA.COLUMNS c
left outer join information_schema.KEY_COLUMN_USAGE k on k.COLUMN_NAME = c.column_name and k.table_name = c.TABLE_NAME
left outer join sys.identity_columns ic on object_Name(ic.object_id) = c.table_name and ic.name = c.COLUMN_NAME
left outer join sys.foreign_key_columns fc on object_name(fc.parent_object_id) = c.table_name and fc.parent_column_id = c.ORDINAL_POSITION
left outer join sys.key_constraints pk on object_name(pk.object_id) = k.CONSTRAINT_NAME and object_name(pk.parent_object_id) = c.table_name
left outer join information_schema.COLUMNS rc on rc.table_name = object_name(fc.referenced_object_id) and rc.ORDINAL_POSITION = fc.referenced_column_id
left outer join sys.foreign_key_columns cc on object_name(cc.referenced_object_id) = c.table_name and cc.referenced_column_id = c.ORDINAL_POSITION
WHERE c.TABLE_NAME = @p0 
group by c.COLUMN_NAME, c.DATA_TYPE, c.CHARACTER_MAXIMUM_LENGTH, convert(bit,case c.IS_NULLABLE when 'Yes' then 1 else 0 end) ,
c.ordinal_position,
convert(bit, case when ic.object_id is not null then 1 else 0 end),
convert(bit, case when rc.column_name is not null then 1 else 0 end),
rc.table_name,
rc.column_name,
convert(bit,case when k.column_name is not null then 1 else 0 end),
convert(bit, case when pk.type='PK' then 1 else 0 end),
pk.type
ORDER BY c.ORDINAL_POSITION", tableName);
            var tmp2 = (from t in tmp group t by new { t.ColumnName, t.DataLength, t.DataType, t.Nullable, t.Position, t.RefTable, t.RefColumn, t.HasReferences })
                       .ToList();

            tmp.Clear();
            definitionEditable = true;
            foreach (var t in tmp2)
            {
                tmp.Add(new TableColumnDefinition
                {
                    ColumnName    = t.Key.ColumnName,
                    DataLength    = t.Key.DataLength,
                    DataType      = t.Key.DataType,
                    Nullable      = t.Key.Nullable,
                    Position      = t.Key.Position,
                    RefTable      = t.Key.RefTable,
                    RefColumn     = t.Key.RefColumn,
                    HasReferences = t.Key.HasReferences,
                    HasIndex      = t.All(n => n.HasIndex),
                    IsForeignKey  = t.All(n => n.IsForeignKey),
                    IsIdentity    = t.All(n => n.IsIdentity),
                    IsPrimaryKey  = t.All(n => n.IsPrimaryKey),
                    IsUniqueKey   = t.All(n => n.IsUniqueKey),
                    Type          = SyntaxProvider.GetAppropriateType(t.First(), !ignoreUnknownTypes)
                });
                definitionEditable &= t.Count() == 1;
            }
            if (ignoreUnknownTypes)
            {
                tmp = tmp.Where(n => n.Type != null).ToList();
            }

            return(tmp);
        }
        private string ProcessClass(INamedTypeSymbol classSymbol, List <Field> fields, SyntaxProvider syntaxProvider)
        {
            fields.Sort((a, b) => a.Index.CompareTo(b.Index));

            string @namespace = classSymbol.ContainingNamespace.ToDisplayString();
            string className  = classSymbol.IsGenericType ? $"{classSymbol.Name}<{string.Join(", ", classSymbol.TypeParameters.Select(parameter => parameter.Name))}>" : classSymbol.Name;

            var source = new StringBuilder();

            foreach (SyntaxReference declaration in classSymbol.DeclaringSyntaxReferences)
            {
                SyntaxNode root = declaration.GetSyntax().GetRoot();
                foreach (SyntaxNode usingDirective in root.DescendantNodes().Where(node => node is UsingDirectiveSyntax))
                {
                    source.Append(usingDirective.GetText().ToString());
                }
            }

            var bodySource = new StringBuilder();

            source.Append($@"using Obsidian.Net;
using Obsidian.Util.Extensions;

namespace {@namespace}
{{
    public ");

            if (classSymbol.IsAbstract)
            {
                source.Append("abstract ");
            }

            source.Append("partial ");

            source.Append(classSymbol.IsValueType ? "struct " : "class ");

            source.Append(className);

            source.Append(@"
    {
");

            string classOffset = "\t\t";

            // Serialize(MinecraftStream stream)
            if (CreateSerializationMethod(bodySource, fields, syntaxProvider))
            {
                source.AppendXML("summary", $"Serializes data from this packet into <see cref=\"MinecraftStream\"/>.\n<b>AUTOGENERATED</b>");
                source.AppendXML("param", @"name=""stream""", "Target stream that this packet's data is written to.", true);
                source.Append($"{classOffset}public void Serialize(MinecraftStream stream)\n{classOffset}{{\n");
                source.Append(bodySource.ToString());
                source.Append($"{classOffset}}}\n\n");
            }
            bodySource.Clear();

            if (!classSymbol.IsAbstract && CreateDeserializationMethod(bodySource, className, fields, syntaxProvider))
            {
                // Deserialize(byte[] data)
                source.AppendXML("summary", $"Deserializes byte data into <see cref=\"{classSymbol.Name}\"/> packet.\n<b>AUTOGENERATED</b>");
                source.AppendXML("param", @"name=""data""", "Data used to populate the packet.", true);
                source.AppendXML("returns", "Deserialized packet.", true);
                source.Append($"{classOffset}public static {className} Deserialize(byte[] data)\n{classOffset}{{\n");
                source.AppendCode("using var stream = new MinecraftStream(data);");
                source.AppendCode("return Deserialize(stream);");
                source.Append($"{classOffset}}}\n\n");

                // Deserialize(MinecraftStream stream)
                source.AppendXML("summary", $"Deserializes data from <see cref=\"MinecraftStream\"/> into <see cref=\"{classSymbol.Name}\"/> packet.\n<b>AUTOGENERATED</b>");
                source.AppendXML("param", @"name=""stream""", "Stream that is read from to populate the packet.", true);
                source.AppendXML("returns", "Deserialized packet.", true);
                source.Append($"{classOffset}public static {className} Deserialize(MinecraftStream stream)\n{classOffset}{{\n");
                source.Append(bodySource.ToString());
                source.Append($"{classOffset}}}");
            }
            bodySource.Clear();

            source.Append(@"
    }
}
");

            return(source.ToString());
        }
        private bool CreateDeserializationMethod(StringBuilder builder, string className, List <Field> fields, SyntaxProvider syntaxProvider)
        {
            builder.AppendCode($"var packet = new {className}();");
            foreach (var field in fields)
            {
                string elementType = field.TypeName, elementName = field.Name;
                if (field.IsArray)
                {
                    elementName += "[i]";
                    string lengthProperty;
                    if (field.TypeName.EndsWith("[]"))
                    {
                        lengthProperty = "Length";
                        elementType    = field.TypeName.Substring(0, field.TypeName.Length - 2);
                    }
                    else
                    {
                        lengthProperty = "Count";
                        elementType    = field.TypeName.Substring(5, field.TypeName.Length - 6);
                    }

                    if (field.IsVarLength)
                    {
                        elementType += "_Var";
                    }
                    if (field.IsAbsolute)
                    {
                        elementType += "_Abs";
                    }

                    string countValue = field.FixedLength >= 0 ? field.FixedLength.ToString() : "stream.ReadVarInt()";
                    builder.AppendCode(field.TypeName.EndsWith("[]") ? $"packet.{field.Name} = new {elementType}[{countValue}];" : $"packet.{field.Name} = new {field.TypeName}({countValue});");

                    builder.AppendCode($"for (int i = 0; i < packet.{field.Name}.{lengthProperty}; i++)");
                    builder.AppendCode("{");
                    builder.Append('\t');
                }

                if (TryGetMethod(elementType, syntaxProvider.ReadMethods, out string methodName))
                {
                    methodName = "stream." + methodName;

                    if (field.OriginalType is not null)
                    {
                        if (field.IsGeneric)
                        {
                            methodName = $"({field.ActualType})(object){methodName}";
                        }
                        else
                        {
                            methodName = $"({field.ActualType}){methodName}";
                        }
                    }

                    builder.AppendCode($"packet.{elementName} = {methodName}();");
                }
                else
                {
                    // creating serialization method failed
#if DEBUG
                    syntaxProvider.Context.ReportDiagnostic(DiagnosticDescriptors.Create(DiagnosticSeverity.Warning, $"{field.Name} ({field.TypeName})({elementType}) has no deserialization method associated with it", field.Declaration));
#endif
                    return(false);
                }

                if (field.IsArray)
                {
                    // end for loop
                    builder.AppendCode("}");
                }
            }
            builder.AppendCode("return packet;");
            return(true);
        }
Beispiel #18
0
        private bool TryCreateSerializationMethod(CodeBuilder builder, List <Field> fields, SyntaxProvider syntaxProvider)
        {
            builder.Line("using var packetStream = new MinecraftStream();");
            foreach (var field in fields)
            {
                string elementType = field.TypeName, elementName = field.Name;
                if (field.IsArray)
                {
                    elementName += "[i]";
                    string lengthProperty;
                    if (field.TypeName.EndsWith("[]"))
                    {
                        lengthProperty = "Length";
                        elementType    = field.TypeName.Substring(0, field.TypeName.Length - 2);
                    }
                    else
                    {
                        lengthProperty = "Count";
                        elementType    = field.TypeName.Substring(5, field.TypeName.Length - 6);
                    }

                    if (field.IsVarLength)
                    {
                        elementType += "_Var";
                    }
                    if (field.IsAbsolute)
                    {
                        elementType += "_Abs";
                    }

                    if (field.FixedLength < 0)
                    {
                        if (field.CountType is null)
                        {
                            builder.Line($"packetStream.WriteVarInt({field.Name}.{lengthProperty});");
                        }
                        else
                        {
                            if (!syntaxProvider.WriteMethods.TryGetValue(field.CountType, out string writeCountMethod))
                            {
                                // CountType has no write method
                                syntaxProvider.Context.ReportDiagnostic(DiagnosticDescriptors.Create(DiagnosticSeverity.Warning, $"{field.Name} ({field.TypeName})({elementType}) has no serialization method associated with its count type", field.Declaration));
                                return(false);
                            }

                            builder.Line($"packetStream.{writeCountMethod}(({field.CountType}){field.Name}.{lengthProperty});");
                        }
                    }

                    builder.Statement($"for (int i = 0; i < {field.Name}.{lengthProperty}; i++)");
                }

                if (field.OriginalType is not null)
                {
                    if (field.IsGeneric)
                    {
                        string tempName = $"temp{field.Name}";
                        builder.Line($"var {tempName} = {elementName};");
                        elementName = $"System.Runtime.CompilerServices.Unsafe.As<{field.ActualType}, {field.OriginalType}>(ref {tempName})";
                    }
                    else
                    {
                        elementName = $"({field.OriginalType}){elementName}";
                    }
                }

                if (TryGetMethod(elementType, syntaxProvider.WriteMethods, out string methodName))
                {
                    builder.Line($"packetStream.{methodName}({elementName});");
                }
                else
                {
                    // Creating serialization method failed
#if DEBUG
                    syntaxProvider.Context.ReportDiagnostic(DiagnosticDescriptors.Create(DiagnosticSeverity.Warning, $"{field.Name} ({field.TypeName})({elementType}) has no serialization method associated with it", field.Declaration));
#endif
                    return(false);
                }

                if (field.IsArray)
                {
                    // End the for loop
                    builder.EndScope();
                }
            }
            builder.Line("stream.Lock.Wait();");
            builder.Line("stream.WriteVarInt(Id.GetVarIntLength() + (int)packetStream.Length);");
            builder.Line("stream.WriteVarInt(Id);");
            builder.Line("packetStream.Position = 0;");
            builder.Line("packetStream.CopyTo(stream);");
            builder.Line("stream.Lock.Release();");
            return(true);
        }
        private bool TryCreateSerializationMethod(CodeBuilder builder, List <Property> properties, SyntaxProvider syntaxProvider)
        {
            string streamName = "packetStream";

            builder.Line($"using var {streamName} = new MinecraftStream();");
            foreach (Property property in properties)
            {
                if (property.IsCollection)
                {
                    if (!TrySerializePropertyCollection(streamName, property, properties, builder, syntaxProvider))
                    {
                        return(false);
                    }
                }
                else
                {
                    if (!TrySerializeProperty(streamName, property, properties, builder, syntaxProvider))
                    {
                        return(false);
                    }
                }
            }
            builder.Line();
            builder.Line("stream.Lock.Wait();");
            builder.Line($"stream.WriteVarInt(Id.GetVarIntLength() + (int){streamName}.Length);");
            builder.Line("stream.WriteVarInt(Id);");
            builder.Line($"{streamName}.Position = 0;");
            builder.Line($"{streamName}.CopyTo(stream);");
            builder.Line("stream.Lock.Release();");
            return(true);
        }
Beispiel #20
0
        private string ProcessClass(INamedTypeSymbol classSymbol, List <Field> fields, SyntaxProvider syntaxProvider)
        {
            fields.Sort((a, b) => a.Index.CompareTo(b.Index));

            string @namespace = classSymbol.ContainingNamespace.ToDisplayString();
            string className  = classSymbol.IsGenericType ? $"{classSymbol.Name}<{string.Join(", ", classSymbol.TypeParameters.Select(parameter => parameter.Name))}>" : classSymbol.Name;

            var  attributes  = classSymbol.GetAttributes();
            bool isReadOnly  = attributes.Any(attribute => attribute.AttributeClass.Name == serverOnly);
            bool isWriteOnly = attributes.Any(attribute => attribute.AttributeClass.Name == clientOnly);

            var source = new CodeBuilder();

            foreach (SyntaxReference declaration in classSymbol.DeclaringSyntaxReferences)
            {
                SyntaxNode root = declaration.GetSyntax().GetRoot();
                foreach (SyntaxNode usingDirective in root.DescendantNodes().Where(node => node is UsingDirectiveSyntax))
                {
                    source.Append(usingDirective.GetText().ToString());
                }
            }

            source.Using("Obsidian.Net");
            source.Using("Obsidian.Util.Extensions");
            source.Using("System.Runtime.CompilerServices");
            source.Line();

            source.Namespace(@namespace);
            source.Type(classSymbol);

            var bodySource = CodeBuilder.WithIndentationOf(source.Indentation + 1);

            // Serialize(MinecraftStream stream)
            bool createSerializationMethod = !isReadOnly && TryCreateSerializationMethod(bodySource, fields, syntaxProvider);

            if (createSerializationMethod)
            {
                source.XmlSummary("Serializes data from this packet into <see cref=\"MinecraftStream\"/>.\n<b>AUTOGENERATED</b>");
                source.XmlParam("stream", "Target stream that this packet's data is written to.");

                source.Method("public void Serialize(MinecraftStream stream)");
                source.Append(bodySource);
                source.EndScope();
            }

            bodySource = CodeBuilder.WithIndentationOf(source.Indentation + 1);

            if (!isWriteOnly && !classSymbol.IsAbstract && TryCreateDeserializationMethod(bodySource, className, fields, syntaxProvider))
            {
                if (createSerializationMethod)
                {
                    source.Line();
                }

                // Deserialize(byte[] data)
                source.XmlSummary($"Deserializes byte data into <see cref=\"{classSymbol.Name}\"/> packet.\n<b>AUTOGENERATED</b>");
                source.XmlParam("data", "Data used to populate the packet.");
                source.XmlReturns("Deserialized packet.");

                source.Method($"public static {className} Deserialize(byte[] data)");
                source.Line("using var stream = new MinecraftStream(data);");
                source.Line("return Deserialize(stream);");
                source.EndScope();

                source.Line();

                // Deserialize(MinecraftStream stream)
                source.XmlSummary($"Deserializes data from <see cref=\"MinecraftStream\"/> into <see cref=\"{classSymbol.Name}\"/> packet.\n<b>AUTOGENERATED</b>");
                source.XmlParam("stream", "Stream that is read from to populate the packet.");
                source.XmlReturns("Deserialized packet.");

                source.Method($"public static {className} Deserialize(MinecraftStream stream)");
                source.Append(bodySource);
                source.EndScope();
            }
            bodySource.Clear();

            source.EndScope(); // EOF type
            source.EndScope(); // EOF namespace

            return(source.ToString());
        }
        private bool TrySerializePropertyCollection(string streamName, Property property, List <Property> properties, CodeBuilder builder, SyntaxProvider syntaxProvider)
        {
            // If there is a method for writing the whole collection, use it instead
            if (syntaxProvider.Methods.TryGetWriteMethod(property, collection: true, out Method collectionWriteMethod))
            {
                return(TrySerializeProperty(streamName, property, properties, builder, syntaxProvider, collectionWriteMethod));
            }

            syntaxProvider.Methods.TryGetWriteMethod(varInt, out Method writeMethod);

            var context = new MethodBuildingContext(streamName, property.Name, property, properties, builder, writeMethod, syntaxProvider.Methods, syntaxProvider.Context);

            if (property.Writing.Execute(context))
            {
                return(true);
            }

            // Attributes behavior
            for (int i = 0; i < property.Attributes.Length; i++)
            {
                if (property.Attributes[i].ModifyCollectionPrefixSerialization(context))
                {
                    goto LOOP;
                }
            }

            // Default behavior
            if (writeMethod is null)
            {
                ReportMissingMethod(syntaxProvider, property, "serialization");
                return(false);
            }
            builder.Line($"{streamName}.{writeMethod}({property}.{property.Length});");

LOOP:
            property.Written.Execute(context);

            // Begin the for loop
            builder.Statement($"for (int i = 0; i < {property}.{property.Length}; i++)");
            syntaxProvider.Methods.TryGetWriteMethod(property, out writeMethod);
            context = new MethodBuildingContext(streamName, property.Name + "[i]", property, properties, builder, writeMethod, syntaxProvider.Methods, syntaxProvider.Context);

            // Attributes behavior
            for (int i = 0; i < property.Attributes.Length; i++)
            {
                if (property.Attributes[i].ModifySerialization(context))
                {
                    goto END_LOOP;
                }
            }

            // Default behavior
            if (writeMethod is null)
            {
                ReportMissingMethod(syntaxProvider, property, "serialization");
                return(false);
            }
            builder.Line($"{streamName}.{writeMethod}({property}[i]);");

END_LOOP:
            builder.EndScope();

            property.Written.Execute(context);
            return(true);
        }