### Get Table Definitions and Data Records Source: https://github.com/trinitek/tpsparser/blob/master/README.md Demonstrates how to open a TPS file and retrieve table definitions and data record payloads using the TpsFile class. Ensure the FileStream is properly disposed. ```csharp using TpsParser; using var fs = new FileStream("contacts.tps", FileMode.Open); var tpsFile = new TpsFile(fs); var tableDefs = tpsFile.GetTableDefinitions(); var firstTableDef = tableDefs.First(); int tableNumber = firstTableDef.Key; TableDefinition tableDef = firstTableDef.Value; var dataRecords = tpsFile.GetDataRecordPayloads(table: tableNumber); ``` -------------------------------- ### Retrieve Table Definitions with TpsFile.GetTableDefinitions Source: https://context7.com/trinitek/tpsparser/llms.txt Shows how to get table schema information, including fields and memos, from a TPS file. Iterates through table definitions and their fields, printing details like name, type, offset, and length. ```csharp using TpsParser; using var fs = new FileStream("contacts.tps", FileMode.Open, FileAccess.Read); var tpsFile = new TpsFile(fs); IReadOnlyDictionary tableDefs = tpsFile.GetTableDefinitions(); foreach (var (tableNumber, tableDef) in tableDefs) { Console.WriteLine($"Table #{tableNumber} Fields: {tableDef.Fields.Length} Memos: {tableDef.Memos.Length}"); foreach (var field in tableDef.Fields) { Console.WriteLine($" [{field.Index}] {field.Name,-20} {field.TypeCode} offset={field.Offset} len={field.Length}"); } } // Table #1 Fields: 8 Memos: 1 // [0] ID Long offset=0 len=4 // [1] FNAME FString offset=4 len=30 // [2] LNAME FString offset=34 len=30 // ... ``` -------------------------------- ### Parse Nested GROUP Structures Source: https://github.com/trinitek/tpsparser/blob/master/README.md Demonstrates how to access nested group structures within a data record. The `ClaGroup` type represents these structures and can contain various value types, including other nested groups. The example shows iterating through the values of a nested group. ```csharp ClaGroup address = (ClaGroup)firstContactRow.Values["ADDRESS"]; foreach (var value in address.GetValues()) { FieldDefinitionPointer field = value.Field; IClaObject value = value.Value; Console.WriteLine($"{field.Name} : {value.GetType().Name} : {value.ToString()}"); } /* * Output: * ------- * LINE1 : ClaString : Mulberry Lane * LINE2 : ClaString : Apt 2299 * CITY : ClaString : Atlanta * STATE : ClaString : GA * POSTCODE : ClaString: 30303 */ ``` -------------------------------- ### Open and Read a TPS File with TpsFile Source: https://context7.com/trinitek/tpsparser/llms.txt Demonstrates how to open unencrypted and encrypted TPS files using TpsFile. Specifies encoding options for content and metadata, and how to handle errors. Shows how to inspect the file header. ```csharp using TpsParser; using System.Text; // ── Unencrypted file ────────────────────────────────────────────── using var fs = new FileStream("contacts.tps", FileMode.Open, FileAccess.Read); var tpsFile = new TpsFile( stream: fs, encodingOptions: new EncodingOptions { ContentEncoding = Encoding.Latin1, // text field & MEMO encoding MetadataEncoding = Encoding.Latin1 // table/field name encoding }, errorHandlingOptions: ErrorHandlingOptions.Default); // ── Encrypted file ──────────────────────────────────────────────── using var fsEnc = new FileStream("encrypted.tps", FileMode.Open, FileAccess.Read); var key = new Key("s3cr3t!"); var encFile = new TpsFile(fsEnc, key); // ── Inspect the file header ─────────────────────────────────────── TpsFileHeader header = tpsFile.GetFileHeader(); Console.WriteLine($"Is TopSpeed file: {header.IsTopSpeedFile}"); // Is TopSpeed file: True ``` -------------------------------- ### TpsFile - Open and read a TPS file Source: https://context7.com/trinitek/tpsparser/llms.txt Demonstrates how to instantiate TpsFile for both unencrypted and encrypted TPS files, and how to access basic file header information. ```APIDOC ## TpsFile — Open and read a TPS file `TpsFile` is the entry point for low-level file access. It accepts a `Stream` and optional `EncodingOptions` and `ErrorHandlingOptions`. For encrypted files, pass a `Key` constructed from the password string. All parsing is lazy and cached on first access. ```csharp using TpsParser; using System.Text; // ── Unencrypted file ────────────────────────────────────────────── using var fs = new FileStream("contacts.tps", FileMode.Open, FileAccess.Read); var tpsFile = new TpsFile( stream: fs, encodingOptions: new EncodingOptions { ContentEncoding = Encoding.Latin1, // text field & MEMO encoding MetadataEncoding = Encoding.Latin1 // table/field name encoding }, errorHandlingOptions: ErrorHandlingOptions.Default); // ── Encrypted file ──────────────────────────────────────────────── using var fsEnc = new FileStream("encrypted.tps", FileMode.Open, FileAccess.Read); var key = new Key("s3cr3t!"); var encFile = new TpsFile(fsEnc, key); // ── Inspect the file header ─────────────────────────────────────── TpsFileHeader header = tpsFile.GetFileHeader(); Console.WriteLine($"Is TopSpeed file: {header.IsTopSpeedFile}"); // Is TopSpeed file: True ``` ``` -------------------------------- ### Basic TpsParser Data Access Source: https://github.com/trinitek/tpsparser/blob/master/README.md Shows how to establish a connection to TPS files using `TpsDbConnection` and execute a basic SELECT query. The `Folder` property in `TpsConnectionStringBuilder` specifies the directory containing the TPS files. ```cs using TpsParser.Data; var csBuilder = new TpsConnectionStringBuilder { Folder = "c:/path/to/folder" }; var conn = new TpsDbConnection(csBuilder.ConnectionString); conn.Open(); // Opens "c:/path/to/folder/contacts.tps" to the default table "UNNAMED" var command = new TpsDbCommand("SELECT * FROM contacts", conn); var reader = command.ExecuteReader(); ``` -------------------------------- ### Enumerate Raw Data Records with TpsFile.GetDataRecordPayloads Source: https://context7.com/trinitek/tpsparser/llms.txt Demonstrates how to enumerate raw data records for a specific table number. Each record's raw binary content is provided as a payload, which can then be decoded. Counts the total number of records found. ```csharp using TpsParser; using System.Linq; using var fs = new FileStream("contacts.tps", FileMode.Open, FileAccess.Read); var tpsFile = new TpsFile(fs); var tableDefs = tpsFile.GetTableDefinitions(); var (tableNum, tableDef) = tableDefs.First(); var dataRecords = tpsFile.GetDataRecordPayloads(table: tableNum); int count = 0; foreach (var record in dataRecords) { count++; Console.WriteLine($"Record #{record.RecordNumber} payload bytes: {record.Content.Length}"); } Console.WriteLine($"Total records: {count}"); // Record #1 payload bytes: 156 // ... // Total records: 2418 ``` -------------------------------- ### Control Parser Resilience with ErrorHandlingOptions Source: https://context7.com/trinitek/tpsparser/llms.txt Configure ErrorHandlingOptions to manage parser behavior during RLE page decompression. 'Strict' throws on anomalies, while custom options allow skipping or allowing undersized pages. ```csharp using TpsParser; // Strict: throw on any decompression anomaly var strictFile = new TpsFile(stream, errorHandlingOptions: ErrorHandlingOptions.Strict); // Lenient: skip oversized pages, allow undersized pages var lenientOptions = new ErrorHandlingOptions { ThrowOnRleDecompressionError = false, RleOversizedDecompressionBehavior = RleSizeMismatchBehavior.Skip, RleUndersizedDecompressionBehavior = RleSizeMismatchBehavior.Allow }; using var fs = new FileStream("possibly-corrupt.tps", FileMode.Open, FileAccess.Read); var tpsFile = new TpsFile(fs, errorHandlingOptions: lenientOptions); // Iterate records; corrupt pages are silently skipped int recovered = tpsFile.GetDataRecordPayloads(table: 1).Count(); Console.WriteLine($"Recovered {recovered} records despite page errors."); ``` -------------------------------- ### ErrorHandlingOptions Source: https://context7.com/trinitek/tpsparser/llms.txt Control parser resilience when RLE page decompression encounters size mismatches. Options range from lenient to strict, with fine-grained control available. ```APIDOC ## ErrorHandlingOptions — Control parser resilience Controls behavior when RLE page decompression encounters size mismatches. `Default` is lenient; `Strict` throws on any anomaly. Fine-grained control is available via individual properties. ```csharp using TpsParser; // Strict: throw on any decompression anomaly var strictFile = new TpsFile(stream, errorHandlingOptions: ErrorHandlingOptions.Strict); // Lenient: skip oversized pages, allow undersized pages var lenientOptions = new ErrorHandlingOptions { ThrowOnRleDecompressionError = false, RleOversizedDecompressionBehavior = RleSizeMismatchBehavior.Skip, RleUndersizedDecompressionBehavior = RleSizeMismatchBehavior.Allow }; using var fs = new FileStream("possibly-corrupt.tps", FileMode.Open, FileAccess.Read); var tpsFile = new TpsFile(fs, errorHandlingOptions: lenientOptions); // Iterate records; corrupt pages are silently skipped int recovered = tpsFile.GetDataRecordPayloads(table: 1).Count(); Console.WriteLine($"Recovered {recovered} records despite page errors."); ``` ``` -------------------------------- ### Key Source: https://context7.com/trinitek/tpsparser/llms.txt Generates a 64-byte key block from a password string, used for decrypting encrypted TPS files. This key can be passed to the `TpsFile` constructor. ```APIDOC ## Key — Decrypt an encrypted TPS file `Key` hashes a password string into a 64-byte key block used to decrypt TPS files. Pass it to the `TpsFile` constructor that accepts a `Key` parameter. ```csharp using TpsParser; using System.Text; // Default encoding is Latin1; specify explicitly for non-Latin passwords var key = new Key("MyP@ssw0rd", Encoding.UTF8); using var fs = new FileStream("secure.tps", FileMode.Open, FileAccess.Read); var tpsFile = new TpsFile(fs, key); // The file is decrypted in memory; use it normally from here Table table = Table.MaterializeFromFile(tpsFile); Console.WriteLine($"Decrypted rows: {table.Rows.Count()}"); ``` ``` -------------------------------- ### Configure Text Encoding with EncodingOptions Source: https://context7.com/trinitek/tpsparser/llms.txt Use EncodingOptions to specify how string content and metadata are decoded. Register code-page providers for non-Latin encodings like 'windows-1252'. ```csharp using TpsParser; using System.Text; // Register code-page provider for Windows-1252 and similar encodings Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); var opts = new EncodingOptions { ContentEncoding = Encoding.GetEncoding("windows-1252"), // field values MetadataEncoding = Encoding.GetEncoding("windows-1252") // table/field names }; using var fs = new FileStream("western-europe.tps", FileMode.Open, FileAccess.Read); var tpsFile = new TpsFile(fs, encodingOptions: opts); Table table = Table.MaterializeFromFile(tpsFile); Console.WriteLine(table.Rows.First().Values["DESCRIPTION"].ToString()); // Straße, Größe, Ärger — correctly decoded from Windows-1252 ``` -------------------------------- ### Create Field Iterator Nodes for TPS Table Source: https://context7.com/trinitek/tpsparser/llms.txt Builds a compiled read plan mapping field definitions to byte offsets. Pass this once per table definition and reuse for performance. ```csharp using TpsParser; using System.Linq; using System.Collections.Immutable; var tableDef = tpsFile.GetTableDefinitions().First().Value; // Request all fields in the table ImmutableHashSet allIndexes = [.. tableDef.Fields.Select(f => f.Index)]; ImmutableArray nodes = FieldValueReader.CreateFieldIteratorNodes(tableDef.Fields, allIndexes); // Or request only specific fields by index ImmutableArray subsetNodes = FieldValueReader.CreateFieldIteratorNodes(tableDef.Fields, [0, 1, 2]); ``` -------------------------------- ### Specifying Table Names in TpsParser Source: https://github.com/trinitek/tpsparser/blob/master/README.md Illustrates how to select from specific tables within a TPS file that contains multiple tables, using the `\!` separator. This mirrors Clarion's `FILE` declaration syntax. ```cs /* * FooContact FILE,DRIVER('TOPSPEED'),NAME('Contacts\!Foo') */ command.CommandText = "SELECT * FROM contacts\!foo" ``` -------------------------------- ### Read Memos and Blobs with TpsFile.GetTpsMemos Source: https://context7.com/trinitek/tpsparser/llms.txt Retrieve `MEMO` and `BLOB` records from a TPS file using `GetTpsMemos`. Results are `ITpsMemo` instances, which can be `TpsTextMemo` or `TpsBlob`. ```csharp using TpsParser; using TpsParser.Memos; using var fs = new FileStream("contacts.tps", FileMode.Open, FileAccess.Read); var tpsFile = new TpsFile(fs); var tableDefs = tpsFile.GetTableDefinitions(); int tableNumber = tableDefs.First().Key; // All memos for the table foreach (ITpsMemo memo in tpsFile.GetTpsMemos(table: tableNumber)) { Console.WriteLine($"Memo for record #{memo.RecordNumber}: {memo}"); } // Memos for a specific record only foreach (ITpsMemo memo in tpsFile.GetTpsMemos(table: tableNumber, owningRecord: 42)) { if (memo is TpsTextMemo text) Console.WriteLine($"Text memo: {text}"); else if (memo is TpsBlob blob) Console.WriteLine($"Blob memo: {blob.ToArray().Length} bytes"); } ``` -------------------------------- ### Query TPS Files using ADO.NET Interface Source: https://context7.com/trinitek/tpsparser/llms.txt Utilize TpsDbConnection, TpsDbCommand, and TpsDataReader for ADO.NET-based querying of TPS files. Supports single and multi-table files, with a virtual __RECORD_NUMBER column. ```csharp using TpsParser.Data; using System.Data; var csBuilder = new TpsConnectionStringBuilder { Folder = @"C:\ClarionApp\Data", FlattenCompoundStructureResults = true }; using var conn = new TpsDbConnection(csBuilder.ConnectionString); conn.Open(); // Single-table file: opens "C:\ClarionApp\Data\invoices.tps" using var cmd = new TpsDbCommand("SELECT * FROM invoices", conn); using var reader = cmd.ExecuteReader(); while (reader.Read()) { long recNo = (long)reader["__RECORD_NUMBER"]; int invoiceId = (int)reader["INVOICE_ID"]; string customer = (string)reader["CUSTOMER"]; // DATE and TIME columns return nullable DateOnly / TimeOnly object rawDate = reader["INVOICE_DATE"]; string dateStr = rawDate is DBNull ? "(null)" : ((DateOnly)rawDate).ToString("yyyy-MM-dd"); Console.WriteLine($"#{recNo} Invoice {invoiceId} Customer: {customer} Date: {dateStr}"); } // Multi-table file: "orders.tps" contains tables "Header" and "Lines" cmd.CommandText = @"SELECT * FROM orders\!Header"; using var reader2 = cmd.ExecuteReader(); ``` -------------------------------- ### TpsDbConnection, TpsDbCommand, TpsDataReader Source: https://context7.com/trinitek/tpsparser/llms.txt Provides an ADO.NET interface for querying TPS files. TpsDbConnection opens the connection, TpsDbCommand executes queries, and TpsDataReader iterates through results. ```APIDOC ## TpsDbConnection + TpsDbCommand + TpsDataReader — ADO.NET query interface `TpsDbConnection` opens a connection to a folder of TPS files. `TpsDbCommand` accepts `SELECT * FROM ` (and optionally `\!` for multi-table files). `TpsDataReader` iterates rows forward-only and exposes a virtual `__RECORD_NUMBER` column. ```csharp using TpsParser.Data; using System.Data; var csBuilder = new TpsConnectionStringBuilder { Folder = @"C:\ClarionApp\Data", FlattenCompoundStructureResults = true }; using var conn = new TpsDbConnection(csBuilder.ConnectionString); conn.Open(); // Single-table file: opens "C:\ClarionApp\Data\invoices.tps" using var cmd = new TpsDbCommand("SELECT * FROM invoices", conn); using var reader = cmd.ExecuteReader(); while (reader.Read()) { long recNo = (long)reader["__RECORD_NUMBER"]; int invoiceId = (int)reader["INVOICE_ID"]; string customer = (string)reader["CUSTOMER"]; // DATE and TIME columns return nullable DateOnly / TimeOnly object rawDate = reader["INVOICE_DATE"]; string dateStr = rawDate is DBNull ? "(null)" : ((DateOnly)rawDate).ToString("yyyy-MM-dd"); Console.WriteLine($"#{recNo} Invoice {invoiceId} Customer: {customer} Date: {dateStr}"); } // Multi-table file: "orders.tps" contains tables "Header" and "Lines" cmd.CommandText = @"SELECT * FROM orders\!Header"; using var reader2 = cmd.ExecuteReader(); ``` ``` -------------------------------- ### Build TpsParser Connection String with TpsConnectionStringBuilder Source: https://context7.com/trinitek/tpsparser/llms.txt Use TpsConnectionStringBuilder to construct connection strings for TpsDbConnection. Specify data source, encoding, and error handling behaviors. ```csharp using TpsParser.Data; using System.Text; Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); var csBuilder = new TpsConnectionStringBuilder { Folder = @"C:\App\Data", FlattenCompoundStructureResults = true, ContentEncoding = "windows-1252", MetadataEncoding = "windows-1252", ErrorHandling = ErrorHandling.Default, ErrorHandlingRleOversizedDecompressionBehavior = RleSizeMismatchBehavior.Skip, ErrorHandlingRleUndersizedDecompressionBehavior = RleSizeMismatchBehavior.Allow }; Console.WriteLine(csBuilder.ConnectionString); // Data Source=C:\App\Data;FlattenCompoundStructureResults=True;ContentEncoding=windows-1252;... ``` -------------------------------- ### TpsFile.GetTableDefinitions — Retrieve table schema Source: https://context7.com/trinitek/tpsparser/llms.txt Shows how to retrieve table schema information, including field and memo definitions, from a TPS file. ```APIDOC ## TpsFile.GetTableDefinitions — Retrieve table schema Returns a `IReadOnlyDictionary` keyed by table number. `TableDefinition` exposes `Fields` (an `ImmutableArray`) and `Memos` (an `ImmutableArray`). ```csharp using TpsParser; using var fs = new FileStream("contacts.tps", FileMode.Open, FileAccess.Read); var tpsFile = new TpsFile(fs); IReadOnlyDictionary tableDefs = tpsFile.GetTableDefinitions(); foreach (var (tableNumber, tableDef) in tableDefs) { Console.WriteLine($"Table #{tableNumber} Fields: {tableDef.Fields.Length} Memos: {tableDef.Memos.Length}"); foreach (var field in tableDef.Fields) { Console.WriteLine($" [{field.Index}] {field.Name,-20} {field.TypeCode} offset={field.Offset} len={field.Length}"); } } // Table #1 Fields: 8 Memos: 1 // [0] ID Long offset=0 len=4 // [1] FNAME FString offset=4 len=30 // [2] LNAME FString offset=34 len=30 // ... ``` ``` -------------------------------- ### EncodingOptions Source: https://context7.com/trinitek/tpsparser/llms.txt Configure text encoding for string content and metadata (table/field names). Defaults to Encoding.Latin1. Useful for files created in non-Latin locales. ```APIDOC ## EncodingOptions — Configure text encoding Controls how string content and metadata (table/field names) are decoded. Defaults to `Encoding.Latin1` for both. Use `Windows-1252` or other code pages when TPS files were created on a non-Latin locale. ```csharp using TpsParser; using System.Text; // Register code-page provider for Windows-1252 and similar encodings Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); var opts = new EncodingOptions { ContentEncoding = Encoding.GetEncoding("windows-1252"), // field values MetadataEncoding = Encoding.GetEncoding("windows-1252") // table/field names }; using var fs = new FileStream("western-europe.tps", FileMode.Open, FileAccess.Read); var tpsFile = new TpsFile(fs, encodingOptions: opts); Table table = Table.MaterializeFromFile(tpsFile); Console.WriteLine(table.Rows.First().Values["DESCRIPTION"].ToString()); // Straße, Größe, Ärger — correctly decoded from Windows-1252 ``` ``` -------------------------------- ### Decrypt TPS Files with Key Source: https://context7.com/trinitek/tpsparser/llms.txt Generate a decryption key from a password using `Key` for encrypted TPS files. Specify encoding for non-Latin passwords. ```csharp using TpsParser; using System.Text; // Default encoding is Latin1; specify explicitly for non-Latin passwords var key = new Key("MyP@ssw0rd", Encoding.UTF8); using var fs = new FileStream("secure.tps", FileMode.Open, FileAccess.Read); var tpsFile = new TpsFile(fs, key); // The file is decrypted in memory; use it normally from here Table table = Table.MaterializeFromFile(tpsFile); Console.WriteLine($"Decrypted rows: {table.Rows.Count()}"); ``` -------------------------------- ### TpsConnectionStringBuilder Source: https://context7.com/trinitek/tpsparser/llms.txt Builds a connection string for TpsDbConnection, allowing configuration of data source, encoding, and error handling options. ```APIDOC ## TpsConnectionStringBuilder — Build a TpsParser.Data connection string `TpsConnectionStringBuilder` constructs the connection string for `TpsDbConnection`. `Data Source` (alias: `Folder`) is the path to the directory containing TPS files. Additional options control encoding and error handling. ```csharp using TpsParser.Data; using System.Text; Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); var csBuilder = new TpsConnectionStringBuilder { Folder = @"C:\App\Data", FlattenCompoundStructureResults = true, ContentEncoding = "windows-1252", MetadataEncoding = "windows-1252", ErrorHandling = ErrorHandling.Default, ErrorHandlingRleOversizedDecompressionBehavior = RleSizeMismatchBehavior.Skip, ErrorHandlingRleUndersizedDecompressionBehavior = RleSizeMismatchBehavior.Allow }; Console.WriteLine(csBuilder.ConnectionString); // Data Source=C:\App\Data;FlattenCompoundStructureResults=True;ContentEncoding=windows-1252;... ``` ``` -------------------------------- ### Access Table Data with Table Class Source: https://github.com/trinitek/tpsparser/blob/master/README.md Provides a simpler way to access TPS file data by materializing the file into a Table object. This abstracts low-level file structures, allowing access to rows and values by column names. MEMOs and BLOBs are stored separately. ```csharp using TpsParser; using var fs = new FileStream("contacts.tps", FileMode.Open); var tpsFile = new TpsFile(fs); // Gets the first table in the file by default. Table contactsTable = Table.MaterializeFromFile(tpsFile); Row firstContactRow = contactsTable.Rows.First(); // Look up values by their column names. IClaString firstName = (IClaString)firstContactRow.Values["FNAME"]; Console.WriteLine(firstName.ToString()); // MEMOs and BLOBs are in a separate dictionary. if (firstContactRow.Memos.TryGetValue("NOTES", out IClaMemo? maybeNotes) { Console.WriteLine(maybeNotes.ToString()); } ``` -------------------------------- ### Read Field Values from Data Records Source: https://github.com/trinitek/tpsparser/blob/master/README.md Illustrates parsing a data record into field values using FieldValueReader. This involves creating field iterator nodes and then retrieving the value for each field. The resulting values implement IClaObject. ```csharp ImmutableArray nodes = FieldValueReader.CreateFieldIteratorNodes( fieldDefinitions: tableDef.Fields, requestedFieldIndexes: [.. tableDef.Fields.Select(f => f.Index)]); List rows = []; foreach (var dataRecord in dataRecords) { var row = new IClaObject[tableDef.Fields.Length]; for (int i = 0; i < row.Length; i++) { var node = nodes[i]; row[i] = FieldValueReader.GetValue(node, dataRecord).Value; } rows.Add(row); } ``` -------------------------------- ### Accessing Array Structures in TpsParser Source: https://github.com/trinitek/tpsparser/blob/master/README.md Demonstrates how to access and iterate through array structures like `ClaArray` in TpsParser. Ensure the `TAGS` value is correctly cast to `ClaArray`. ```cs ClaArray tagNumbers = (ClaArray)firstContactRow.Values["TAGS"]; Console.WriteLine($"TypeCode : {tagNumbers.TypeCode}"); Console.WriteLine($"Count : {tagNumbers.Count}"); for (int i = 0; i < tagNumbers.Count; i++) { FieldEnumerationResult value = tagNumbers[i]; FieldDefinitionPointer field = value.Field; ClaLong value = (ClaLong)value.Value; Console.WriteLine($"{i} : {value.ToString()}"); } /* * Output: * ------- * TypeCode : Long <-- This is a Clarion LONG, equivalent to Int32 * Count : 6 * 0 : 36 * 1 : 88 * 2 : 294 * 3 : 506 * 4 : 311 * 5 : 9286 */ ``` -------------------------------- ### Enumerate All Field Values in TPS Record Source: https://context7.com/trinitek/tpsparser/llms.txt A convenience wrapper that decodes all fields from a record at once and yields FieldEnumerationResult instances. Useful for loading entire records into dictionaries. ```csharp using TpsParser; using TpsParser.TypeModel; using System.Linq; var tableDef = tpsFile.GetTableDefinitions().First().Value; var nodes = FieldValueReader.CreateFieldIteratorNodes( tableDef.Fields, [.. tableDef.Fields.Select(f => f.Index)]); var allRows = tpsFile .GetDataRecordPayloads(table: 1) .Select(record => FieldValueReader.EnumerateValues(nodes, record) .ToDictionary( r => r.FieldDefinition.Name, r => r.Value)) .ToList(); Console.WriteLine($"Loaded {allRows.Count} rows"); Console.WriteLine($"First row FNAME: {allRows[0]["FNAME"]}"); // Loaded 2418 rows // First row FNAME: Alice ``` -------------------------------- ### TpsFile.GetDataRecordPayloads — Enumerate raw data records Source: https://context7.com/trinitek/tpsparser/llms.txt Explains how to enumerate raw data records for a specific table number within a TPS file. ```APIDOC ## TpsFile.GetDataRecordPayloads — Enumerate raw data records Returns a lazy `IEnumerable` for a given table number. Each payload carries the raw binary content of one record, which can then be decoded with `FieldValueReader`. ```csharp using TpsParser; using System.Linq; using var fs = new FileStream("contacts.tps", FileMode.Open, FileAccess.Read); var tpsFile = new TpsFile(fs); var tableDefs = tpsFile.GetTableDefinitions(); var (tableNum, tableDef) = tableDefs.First(); var dataRecords = tpsFile.GetDataRecordPayloads(table: tableNum); int count = 0; foreach (var record in dataRecords) { count++; Console.WriteLine($"Record #{record.RecordNumber} payload bytes: {record.Content.Length}"); } Console.WriteLine($"Total records: {count}"); // Record #1 payload bytes: 156 // ... // Total records: 2418 ``` ``` -------------------------------- ### Materialize TPS Table to Table Object Source: https://context7.com/trinitek/tpsparser/llms.txt High-level function to load an entire TPS table, including fields and memos, into a Table object. Rows expose data as dictionaries keyed by column name. ```csharp using TpsParser; using TpsParser.TypeModel; using var fs = new FileStream("contacts.tps", FileMode.Open, FileAccess.Read); var tpsFile = new TpsFile(fs); // Gets the first table by default; pass tableNumber: N to select a specific one Table contactsTable = Table.MaterializeFromFile(tpsFile); Console.WriteLine($"Table name: {contactsTable.Name}"); Console.WriteLine($"Row count : {contactsTable.Rows.Count()}"); foreach (Row row in contactsTable.Rows) { var firstName = (IClaString)row.Values["FNAME"]; var lastName = (IClaString)row.Values["LNAME"]; var dob = (ClaDate)row.Values["DOB"]; Console.Write($"#{row.RecordNumber} {firstName} {lastName}"); if (dob.ToDateOnly().Value is DateOnly d) Console.Write($" born {d:yyyy-MM-dd}"); // MEMOs are in a separate dictionary if (row.Memos.TryGetValue("NOTES", out ITpsMemo? notes)) Console.Write($" notes: {notes}"); Console.WriteLine(); } // Table name: CONTACT // Row count : 2418 // #1 Alice Smith born 1985-03-14 notes: Prefers email contact. ``` -------------------------------- ### TpsFile.GetTpsMemos Source: https://context7.com/trinitek/tpsparser/llms.txt Retrieves MEMO and BLOB records from a TPS file for a specified table. It can filter memos by record number and/or memo definition index. ```APIDOC ## TpsFile.GetTpsMemos — Read MEMO and BLOB records Returns memos for a given table, optionally filtered by record number and/or memo definition index. Results are `ITpsMemo` instances — either `TpsTextMemo` or `TpsBlob`. ```csharp using TpsParser; using TpsParser.Memos; using var fs = new FileStream("contacts.tps", FileMode.Open, FileAccess.Read); var tpsFile = new TpsFile(fs); var tableDefs = tpsFile.GetTableDefinitions(); int tableNumber = tableDefs.First().Key; // All memos for the table foreach (ITpsMemo memo in tpsFile.GetTpsMemos(table: tableNumber)) { Console.WriteLine($"Memo for record #{memo.RecordNumber}: {memo}"); } // Memos for a specific record only foreach (ITpsMemo memo in tpsFile.GetTpsMemos(table: tableNumber, owningRecord: 42)) { if (memo is TpsTextMemo text) Console.WriteLine($"Text memo: {text}"); else if (memo is TpsBlob blob) Console.WriteLine($"Blob memo: {blob.ToArray().Length} bytes"); } ``` ``` -------------------------------- ### Flattening Compound Structures in TpsParser Source: https://github.com/trinitek/tpsparser/blob/master/README.md Enables flattening of compound data structures (`ClaGroup`, `ClaArray`) by setting `FlattenCompoundStructureResults` to `true` in the connection string builder. This changes how nested fields are accessed. ```cs csBuilder.FlattenCompoundStructureResults = true; ``` ```plaintext // Without flattening... reader["A"] --> string reader["B"] --> ClaArray of 4 ClaFString reader["C"] --> ClaGroup of (ClaLong, ClaDate) // With flattening... reader["A"] --> string reader["B[0]"] --> string reader["B[1]"] --> string reader["B[2]"] --> string reader["B[3]"] --> string reader["C.X"] --> int reader["C.Y"] --> DateOnly ``` -------------------------------- ### Case-Insensitive Field Lookup in TpsParser Source: https://context7.com/trinitek/tpsparser/llms.txt Use `GetFieldValueCaseInsensitive` for flexible field retrieval. It throws `ArgumentException` if `isRequired` is true and the column is missing, otherwise returns null. ```csharp using TpsParser; Table table = Table.MaterializeFromFile(tpsFile); Row row = table.Rows.First(); // Case-insensitive lookup — "fname", "FNAME", "Fname" all work IClaObject? value = row.GetFieldValueCaseInsensitive("fname", isRequired: false); Console.WriteLine(value?.ToString()); // Alice // Required lookup — throws if column is absent try { IClaObject required = row.GetFieldValueCaseInsensitive("NONEXISTENT", isRequired: true)!; } catch (ArgumentException ex) { Console.WriteLine(ex.Message); // Could not find column by case insensitive name 'NONEXISTENT'. Available columns are [ID, FNAME, LNAME, ...] } ``` -------------------------------- ### ClaArray.GetValues Source: https://context7.com/trinitek/tpsparser/llms.txt Enumerates the elements of a `ClaArray`, which represents a one-dimensional Clarion array. Elements can also be accessed directly by index. ```APIDOC ## ClaArray.GetValues — Enumerate array elements `ClaArray` represents a one-dimensional Clarion array. Use `GetValues()` for a full enumeration or index into it with `array[index]`. All elements share the same `FieldTypeCode`. ```csharp using TpsParser; using TpsParser.TypeModel; Table table = Table.MaterializeFromFile(tpsFile); Row row = table.Rows.First(); ClaArray tags = (ClaArray)row.Values["TAGS"]; Console.WriteLine($"TypeCode : {tags.TypeCode}"); // Long Console.WriteLine($"Count : {tags.Count}"); // 6 for (ushort i = 0; i < tags.Count; i++) { FieldEnumerationResult element = tags[i]; ClaLong tagId = (ClaLong)element.Value; Console.WriteLine($" [{i}] = {tagId.ToInt32().Value}"); } // TypeCode : Long // Count : 6 // [0] = 36 // [1] = 88 // [2] = 294 // [3] = 506 // [4] = 311 // [5] = 9286 ``` ``` -------------------------------- ### Enumerate Array Elements with ClaArray.GetValues Source: https://context7.com/trinitek/tpsparser/llms.txt Access elements of a Clarion one-dimensional array using `GetValues()` or direct indexing. All elements share the same `FieldTypeCode`. ```csharp using TpsParser; using TpsParser.TypeModel; Table table = Table.MaterializeFromFile(tpsFile); Row row = table.Rows.First(); ClaArray tags = (ClaArray)row.Values["TAGS"]; Console.WriteLine($"TypeCode : {tags.TypeCode}"); // Long Console.WriteLine($"Count : {tags.Count}"); // 6 for (ushort i = 0; i < tags.Count; i++) { FieldEnumerationResult element = tags[i]; ClaLong tagId = (ClaLong)element.Value; Console.WriteLine($" [{i}] = {tagId.ToInt32().Value}"); } // TypeCode : Long // Count : 6 // [0] = 36 // [1] = 88 // [2] = 294 // [3] = 506 // [4] = 311 // [5] = 9286 ``` -------------------------------- ### Recursively Flatten Compound Nodes for ADO.NET Source: https://context7.com/trinitek/tpsparser/llms.txt Expands GROUP and ARRAY nodes into leaf-level scalar fields with fully-qualified names. Used internally by TpsDbCommand when FlattenCompoundStructureResults is true. ```csharp using TpsParser; using System.Linq; using System.Collections.Immutable; var tableDef = tpsFile.GetTableDefinitions().First().Value; var nodes = FieldValueReader.CreateFieldIteratorNodes( tableDef.Fields, [.. tableDef.Fields.Select(f => f.Index)]); // Expand groups and arrays into scalar leaf nodes ImmutableArray flat = [.. FieldValueReader.RecursivelyFlattenNodes(nodes)]; foreach (var node in flat) { Console.WriteLine(node.DefinitionPointer.Name); } // ID // FNAME // ADDRESS.LINE1 // ADDRESS.LINE2 // ADDRESS.CITY // ADDRESS.STATE // ADDRESS.POSTCODE // TAGS[0] // TAGS[1] // TAGS[2] // ... ``` -------------------------------- ### Enumerate Group Sub-fields with ClaGroup.GetValues Source: https://context7.com/trinitek/tpsparser/llms.txt Iterate through sub-fields of a Clarion `GROUP` type using `GetValues()`. Each sub-field is returned as a `FieldEnumerationResult`. ```csharp using TpsParser; using TpsParser.TypeModel; Table table = Table.MaterializeFromFile(tpsFile); Row row = table.Rows.First(); ClaGroup address = (ClaGroup)row.Values["ADDRESS"]; Console.WriteLine($"Sub-field count: {address.Count}"); foreach (FieldEnumerationResult subField in address.GetValues()) { Console.WriteLine($" {subField.FieldDefinition.Name,-12} {subField.Value.GetType().Name,-12} = {subField.Value}"); } // Sub-field count: 5 // LINE1 ClaFString = Mulberry Lane // LINE2 ClaFString = Apt 2299 // CITY ClaFString = Atlanta // STATE ClaFString = GA // POSTCODE ClaFString = 30303 ``` -------------------------------- ### Row.GetFieldValueCaseInsensitive Source: https://context7.com/trinitek/tpsparser/llms.txt Retrieves a field's value from a row using its column name, ignoring case. It can optionally throw an exception if the column is required and not found. ```APIDOC ## Row.GetFieldValueCaseInsensitive — Case-insensitive field lookup Looks up a field value by column name, ignoring case. Throws `ArgumentException` if `isRequired` is `true` and the column does not exist; returns `null` otherwise. ```csharp using TpsParser; Table table = Table.MaterializeFromFile(tpsFile); Row row = table.Rows.First(); // Case-insensitive lookup — "fname", "FNAME", "Fname" all work IClaObject? value = row.GetFieldValueCaseInsensitive("fname", isRequired: false); Console.WriteLine(value?.ToString()); // Alice // Required lookup — throws if column is absent try { IClaObject required = row.GetFieldValueCaseInsensitive("NONEXISTENT", isRequired: true)!; } catch (ArgumentException ex) { Console.WriteLine(ex.Message); // Could not find column by case insensitive name 'NONEXISTENT'. Available columns are [ID, FNAME, LNAME, ...] } ``` ``` -------------------------------- ### ClaGroup.GetValues Source: https://context7.com/trinitek/tpsparser/llms.txt Enumerates the sub-fields of a `ClaGroup` object, which represents a Clarion `GROUP` composite type. Each sub-field is returned as a `FieldEnumerationResult`. ```APIDOC ## ClaGroup.GetValues — Enumerate GROUP sub-fields `ClaGroup` represents a Clarion `GROUP` composite type. Call `GetValues()` to enumerate each sub-field as a `FieldEnumerationResult`, or index into the group directly. ```csharp using TpsParser; using TpsParser.TypeModel; Table table = Table.MaterializeFromFile(tpsFile); Row row = table.Rows.First(); ClaGroup address = (ClaGroup)row.Values["ADDRESS"]; Console.WriteLine($"Sub-field count: {address.Count}"); foreach (FieldEnumerationResult subField in address.GetValues()) { Console.WriteLine($" {subField.FieldDefinition.Name,-12} {subField.Value.GetType().Name,-12} = {subField.Value}"); } // Sub-field count: 5 // LINE1 ClaFString = Mulberry Lane // LINE2 ClaFString = Apt 2299 // CITY ClaFString = Atlanta // STATE ClaFString = GA // POSTCODE ClaFString = 30303 ``` ``` -------------------------------- ### Read Single Field Value from TPS Record Source: https://context7.com/trinitek/tpsparser/llms.txt Decodes a single field from a DataRecordPayload using a pre-built FieldIteratorNode. Returns a FieldEnumerationResult with the decoded value. ```csharp using TpsParser; using TpsParser.TypeModel; var tableDef = tpsFile.GetTableDefinitions().First().Value; var nodes = FieldValueReader.CreateFieldIteratorNodes( tableDef.Fields, [.. tableDef.Fields.Select(f => f.Index)]); foreach (var record in tpsFile.GetDataRecordPayloads(table: 1)) { for (int i = 0; i < nodes.Length; i++) { FieldEnumerationResult result = FieldValueReader.GetValue(nodes[i], record); string fieldName = result.FieldDefinition.Name; IClaObject value = result.Value; string display = value switch { ClaLong l => l.ToInt32().Value.ToString()!, IClaString s => s.ToString()!, ClaDate d => d.ToDateOnly().Value?.ToString("yyyy-MM-dd") ?? "(null)", ClaGroup g => $"GROUP({g.Count} fields)", ClaArray a => $"ARRAY[{a.Count}]", _ => value.GetType().Name }; Console.WriteLine($"{fieldName}: {display}"); } } // ID: 1 // FNAME: Alice // LNAME: Smith // DOB: 1985-03-14 // ... ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.