### Example data file for heterogeneous parsing Source: https://jeyben.github.io/fixedformat4j/usage/file-processing Sample data.txt file structure with 33-character lines used for the heterogeneous worked example. ```text X1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx K400xxx01xxxxxxxxxxxxxxxxxxxxxxxx K400xxx02xxxxxxxxxxxxxxxxxxxxxxxx K410xxx01xxxxxxxxxxxxxxxxxxxxxxxx X2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx K410xxx03xxxxxxxxxxxxxxxxxxxxxxxx SC410xxx03xxxxxxxxxxxxxxxxxxxxxxx FC412xxx02xxxxxxxxxxxxxxxxxxxxxxx SC411xxx03xxxxxxxxxxxxxxxxxxxxxxx SC412xxx03xxxxxxxxxxxxxxxxxxxxxxx FC412xxx03xxxxxxxxxxxxxxxxxxxxxxx ``` -------------------------------- ### Heterogeneous file quick start Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.io.read/FixedFormatReader.java.html Quick start for reading a heterogeneous fixed-format file with multiple record types. Uses LinePattern.prefix() to discriminate lines by prefix and retrieves each type from the ReadResult. ```java FixedFormatReader reader = FixedFormatReader.builder() .addMapping(HeaderRecord.class, LinePattern.prefix("HDR")) .addMapping(DetailRecord.class, LinePattern.prefix("DTL")) .build(); ReadResult result = reader.read(Path.of("data.txt")); List headers = result.get(HeaderRecord.class); List details = result.get(DetailRecord.class); ``` -------------------------------- ### Single record type quick start Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.io.read/FixedFormatReader.java.html Quick start for reading a fixed-format file with a single record type. Uses LinePattern.matchAll() to match every line and retrieves the parsed records by class. ```java FixedFormatReader reader = FixedFormatReader.builder() .addMapping(MyRecord.class, LinePattern.matchAll()) .build(); List records = reader.read(Path.of("data.txt")).get(MyRecord.class); ``` -------------------------------- ### Typed handlers quick start Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.io.read/FixedFormatReader.java.html Quick start for processing a fixed-format file with typed handlers, avoiding casts. Uses HandlerRegistry to register callbacks for each record type. ```java FixedFormatReader reader = FixedFormatReader.builder() .addMapping(HeaderRecord.class, LinePattern.prefix("HDR")) .addMapping(DetailRecord.class, LinePattern.prefix("DTL")) .build(); reader.process(Path.of("data.txt"), new HandlerRegistry() .on(HeaderRecord.class, this::onHeader) .on(DetailRecord.class, this::onDetail)); ``` -------------------------------- ### Try block start in openStream(Path, Charset) Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.io.read/FixedFormatReader.java.html Start of the try block that opens the file and delegates to the overloaded openStream(Reader) method. ```java try { ``` -------------------------------- ### Define record classes for heterogeneous example Source: https://jeyben.github.io/fixedformat4j/usage/file-processing POJO classes annotated with @Record for the heterogeneous file parsing example. ```java @Record(length = 33) public class X1Record { /* @Field getters */ } @Record(length = 33) public class X2Record { /* @Field getters */ } @Record(length = 33) public class K40001Record { /* @Field getters */ } @Record(length = 33) public class K40002Record { /* @Field getters */ } @Record(length = 33) public class K41001Record { /* @Field getters */ } @Record(length = 33) public class K41003Record { /* @Field getters */ } @Record(length = 33) public class OtherRecord { /* @Field getters */ } ``` -------------------------------- ### Create FixedFormatManager Source: https://jeyben.github.io/fixedformat4j/examples Entry point for all examples. Creates a FixedFormatManager instance to load and export fixed-format records. ```java FixedFormatManager manager = new FixedFormatManagerImpl(); ``` -------------------------------- ### Output of nested record example Source: https://jeyben.github.io/fixedformat4j/usage/nested-records The output produced by the example program. It shows the original text, the loaded values, and the exported string after modifying the nested record. ```text foo bar 001232008-10-21 foo bar 123 Tue Oct 21 00:00:00 CEST 2008 foo fubar 098762008-10-21 ``` -------------------------------- ### ReadResult usage example Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.io.read/ReadResult.java.html Shows how to retrieve typed lists of records from a ReadResult without casts. The example assumes a reader that has already read a file and returns a ReadResult. The get method is generic and returns a List of the requested type. ```java ReadResult result = reader.read(path); List headers = result.get(HeaderRecord.class); // no cast List details = result.get(DetailRecord.class); // no cast ``` -------------------------------- ### Javadoc start Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.annotation/Sign.java.html Line 115: Start of Javadoc comment. ```java /** ``` -------------------------------- ### Example: @Field with nullChar equal to paddingChar for all-zeros-is-null Source: https://jeyben.github.io/fixedformat4j/changelog Shows the idiomatic configuration where nullChar equals paddingChar to treat all zeros as null, e.g., for an optional numeric field with zero-padding. This combination is now supported and activates null-aware handling. ```java // All zeros means null (e.g. optional numeric with zero-padding) @Field(offset = 9, length = 5, align = Align.RIGHT, paddingChar = '0', nullChar = '0') public Integer getQuantity() { … } ``` -------------------------------- ### Example: @Field with nullChar equal to paddingChar for blank-is-null Source: https://jeyben.github.io/fixedformat4j/changelog Shows the idiomatic configuration where nullChar equals paddingChar to treat all spaces as null, e.g., for an optional date field. This combination is now supported and activates null-aware handling. ```java // All spaces means null (e.g. optional date) @Field(offset = 1, length = 8, paddingChar = ' ', nullChar = ' ') public Date getInvoiceDate() { … } ``` -------------------------------- ### resolveSetter: try block Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.format.impl/ClassMetadataCache.java.html Starts a try block to attempt to get the setter method. ```java try { ``` -------------------------------- ### process method Javadoc Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.io.read/FixedFormatReader.java.html Javadoc for the process method, including usage example with HandlerRegistry and thread-safety notes. ```java /** * Reads all records from {@code reader} and dispatches each to the handler registered * for its class in {@code registry}. * *

Classes not present in the registry are silently ignored — they are still parsed, * but no handler is invoked. Because the registry is supplied per call rather than stored * in the reader, the same {@link FixedFormatReader} instance is safe to use from multiple * threads with independent registries, provided that all configured strategies and the * {@link com.ancientprogramming.fixedformat4j.format.FixedFormatManager} are also thread-safe. * The built-in strategies and * {@link com.ancientprogramming.fixedformat4j.format.impl.FixedFormatManagerImpl} are thread-safe * and satisfy this requirement.

* *
{@code
 * reader.process(source, new HandlerRegistry()
 *     .on(HeaderRecord.class, this::onHeader)
 *     .on(DetailRecord.class, this::onDetail));
 * }
* * @param reader the source of lines; closed when this method returns * @param registry the typed handlers to invoke per matched class; must not be {@code null} * @throws FixedFormatIOException if an IO error occurs while reading */ ``` -------------------------------- ### Write homogeneous list with FixedFormatWriter Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.io.write/FixedFormatWriter.java.html Quick start for writing a homogeneous list of records to a file. The writer is built with default settings and writes all records to the specified path. ```java FixedFormatWriter writer = FixedFormatWriter.builder().build(); writer.write(Path.of("out.txt"), records); ``` -------------------------------- ### Sample freight line with shipment ID and four package weights Source: https://jeyben.github.io/fixedformat4j/examples Example input line for a freight record: shipment ID (6 chars) followed by four 6-character right-aligned zero-padded package weights. Unused trailing slots are "000000". ```text // positions: 1-6 = shipment ID, 7-12 = weight 1, 13-18 = weight 2, 19-24 = weight 3, 25-30 = weight 4 SHIP01002500010000000000000000 ``` -------------------------------- ### Load and export a fixed-format record with FixedFormatManager Source: https://jeyben.github.io/fixedformat4j Shows the minimal usage of FixedFormatManager to load a line into a record, modify a field, and export it back. The example uses a BasicRecord class annotated with @Record and @Field. The output demonstrates the resulting fixed-format string after modification. ```java FixedFormatManager manager = new FixedFormatManagerImpl(); String line = "string 001002008-05-29"; BasicRecord record = manager.load(BasicRecord.class, line); record.setIntegerData(100); System.out.println(manager.export(record)); // "string 001002008-05-29" ``` -------------------------------- ### @FixedFormatNumber usage example Source: https://jeyben.github.io/fixedformat4j/usage/annotations Configures an Integer field to use a prepended sign character with right alignment and zero padding. ```java @Field(offset = 1, length = 5, align = Align.RIGHT, paddingChar = '0') @FixedFormatNumber(sign = Sign.PREPEND) public Integer getBalance() { return balance; } ``` -------------------------------- ### Usage example with FixedFormatManager Source: https://jeyben.github.io/fixedformat4j/examples Shows how to export and load an InvoiceRecord using FixedFormatManager. The export produces "000000420000009999USD" and the load returns the original values. ```java FixedFormatManager manager = new FixedFormatManagerImpl(); InvoiceRecord record = new InvoiceRecord(); record.setInvoiceId(42); record.setAmountCents(9999); record.setCurrency("USD"); System.out.println(manager.export(record)); // "000000420000009999USD" InvoiceRecord loaded = manager.load(InvoiceRecord.class, "000000420000009999USD"); System.out.println(loaded.getInvoiceId()); // 42 System.out.println(loaded.getAmountCents()); // 9999 System.out.println(loaded.getCurrency()); // "USD" ``` -------------------------------- ### Write heterogeneous mixed-type list with FixedFormatWriter Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.io.write/FixedFormatWriter.java.html Quick start for writing a heterogeneous mixed-type list of records to a file. The list can contain different record types. ```java writer.write(Path.of("out.txt"), List.of(header, detail1, detail2, footer)); ``` -------------------------------- ### Maven: Add processor as provided-scope dependency Source: https://jeyben.github.io/fixedformat4j/usage/compile-time-validation Alternative Maven setup using a provided-scope dependency so javac discovers the processor via ServiceLoader. On JDK 23+ you must also pass -proc:full to javac (e.g., -proc:full). ```xml com.ancientprogramming.fixedformat4j fixedformat4j-processor 1.9.0 provided ``` -------------------------------- ### Configure excludeLines for pre-match filtering Source: https://jeyben.github.io/fixedformat4j/usage/file-processing Use this snippet to configure a FixedFormatReader to skip lines that are blank or start with '#' before pattern matching. Lines matching the predicate are silently skipped and do not trigger the unmatched-line strategy. ```java FixedFormatReader.builder() .addMapping(EmployeeRecord.class, LinePattern.matchAll()) .excludeLines(line -> line.isBlank() || line.startsWith("#")) .build(); ``` -------------------------------- ### Read file with multiple record types using discriminator patterns Source: https://jeyben.github.io/fixedformat4j/examples Register each record class with a prefix pattern (E for EmployeeRecord, M for ManagerRecord) so the reader can distinguish record types in the same file. The read method groups results by class, and get(Class) returns the list without casts. ```java FixedFormatReader reader = FixedFormatReader.builder() .addMapping(EmployeeRecord.class, LinePattern.prefix("E")) .addMapping(ManagerRecord.class, LinePattern.prefix("M")) .build(); ReadResult result = reader.read(Path.of("staff.txt")); List employees = result.get(EmployeeRecord.class); List managers = result.get(ManagerRecord.class); ``` -------------------------------- ### Read file with single record type using FixedFormatReader Source: https://jeyben.github.io/fixedformat4j/examples Build a FixedFormatReader with a single mapping for EmployeeRecord, exclude blank lines, and read all records from a file. The read method returns a ReadResult, and get(EmployeeRecord.class) retrieves the list without casts. ```java import com.ancientprogramming.fixedformat4j.io.read.LinePattern; FixedFormatReader reader = FixedFormatReader.builder() .addMapping(EmployeeRecord.class, LinePattern.matchAll()) .excludeLines(line -> line.isBlank()) .build(); List employees = reader.read(Path.of("employees.txt")) .get(EmployeeRecord.class); for (EmployeeRecord emp : employees) { System.out.println(emp.getName() + " — ID: " + emp.getEmployeeId()); } ``` -------------------------------- ### ParseErrorStrategy.java - functional interface with throwException() and skipAndLog() Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.io.read/ParseErrorStrategy.java.html Complete source of the ParseErrorStrategy functional interface. It defines the handle method and two static factory strategies: throwException() rethrows the wrapped FixedFormatException to abort processing, while skipAndLog() logs a WARN via SLF4J and continues. The Javadoc shows a lambda usage example for collecting errors. ```java /* * Copyright 2004 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.ancientprogramming.fixedformat4j.io.read; import com.ancientprogramming.fixedformat4j.exception.FixedFormatException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Strategy invoked when a matched line fails to parse into a record object. * *

Implement this interface to define custom error handling — for example, collecting * failed lines for a post-processing error report. Two built-in strategies are provided as * static factory methods: {@link #throwException()} and {@link #skipAndLog()}.

* *

Because this is a {@link FunctionalInterface}, a lambda can be passed wherever a * {@code ParseErrorStrategy} is expected:

*
{@code
 * .parseErrorStrategy((wrapped, line, lineNumber) ->
 *     errorList.add("Line " + lineNumber + ": " + wrapped.getMessage()))
 * }
* *

To abort processing on error, throw from the lambda. To skip the line, return normally.

* * @author Jacob von Eyben - https://eybenconsult.com * @since 1.8.0 */ @FunctionalInterface public interface ParseErrorStrategy { Logger LOG = LoggerFactory.getLogger(ParseErrorStrategy.class); /** * Handles a line that matched a pattern but could not be parsed into a record object. * *

Throw a {@link FixedFormatException} to abort processing, or return normally to * skip the record and continue with the next line.

* * @param wrapped a {@link FixedFormatException} wrapping the original parse failure; * the exception message includes the line number * @param line the raw content of the line, without any trailing line-ending characters * @param lineNumber the 1-based line number within the source being read */ void handle(FixedFormatException wrapped, String line, long lineNumber); /** * Returns a strategy that rethrows the parse exception immediately (fail-fast). * * @return a fail-fast strategy; never {@code null} */ static ParseErrorStrategy throwException() { return (wrapped, line, lineNumber) -> { throw wrapped; }; } /** * Returns a strategy that skips the line and logs details at WARN level via SLF4J. * Processing continues with the next line. * *

Note: logging only occurs if an SLF4J binding is present on the * classpath at runtime. If no binding is configured, the warning is silently discarded. * When guaranteed error visibility is required, use a custom lambda strategy instead.

* * @return a skip-and-log strategy; never {@code null} */ static ParseErrorStrategy skipAndLog() { return (wrapped, line, lineNumber) -> { LOG.warn("Error parsing line {}: {}", lineNumber, wrapped.getMessage()); }; } } ``` -------------------------------- ### datatype method: getter return type with Java record support Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.format.impl/FormatInstructionsBuilder.java.html The datatype method determines the return type of a getter method, allowing Java records. It throws FixedFormatException if the method name does not start with 'get' or 'is' and is not a Java record. ```java Class datatype(Method method, Field fieldAnno) { ``` ```java if (method.getName().startsWith("get") || method.getName().startsWith("is") ``` ```java || JavaRecordSupport.isJavaRecord(method.getDeclaringClass())) { ``` ```java return method.getReturnType(); ``` ```java } ``` ```java throw new FixedFormatException(format( ``` ```java "Cannot annotate method %s, with %s annotation. %s annotations must be placed on methods starting with 'get' or 'is'", ``` ```java method.getName(), fieldAnno.getClass().getName(), fieldAnno.getClass().getName())); ``` ```java } ``` -------------------------------- ### validateCount: get count Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.format.impl/RepeatingFieldSupport.java.html Gets the count from the field annotation. Mutation that removed the call to Field::count was killed. ```java int count = fieldAnnotation.count(); ``` -------------------------------- ### File layout for orders.txt Source: https://jeyben.github.io/fixedformat4j/examples Sample input file with header (HDR) and detail (DTL) lines. The header contains a date and company name; each detail contains an order ID, product, and amount in cents. ```plaintext HDR20260419ACME Corp DTL000142WIDGET-A 0000099900 DTL000143BOLT-SET 0000024999 ``` -------------------------------- ### Build heterogeneous reader with catch-all Source: https://jeyben.github.io/fixedformat4j/usage/file-processing Configures the reader to handle specific prefixes, positional patterns, and a catch-all mapping. ```java import com.ancientprogramming.fixedformat4j.io.read.LinePattern; FixedFormatReader reader = FixedFormatReader.builder() .addMapping(X1Record.class, LinePattern.prefix("X1")) .addMapping(X2Record.class, LinePattern.prefix("X2")) .addMapping(K40001Record.class, LinePattern.positional(new int[]{0, 1, 2, 3, 7, 8}, "K40001")) .addMapping(K40002Record.class, LinePattern.positional(new int[]{0, 1, 2, 3, 7, 8}, "K40002")) .addMapping(K41001Record.class, LinePattern.positional(new int[]{0, 1, 2, 3, 7, 8}, "K41001")) .addMapping(K41003Record.class, LinePattern.positional(new int[]{0, 1, 2, 3, 7, 8}, "K41003")) .addMapping(OtherRecord.class, LinePattern.matchAll()) .build(); ``` -------------------------------- ### validateCount: get return type Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.format.impl/RepeatingFieldSupport.java.html Gets the return type of the method. Mutation that removed the call to Method::getReturnType was killed. ```java Class returnType = method.getReturnType(); ``` -------------------------------- ### Open file and delegate to openStream(Reader) Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.io.read/FixedFormatReader.java.html Opens the file with Files.newInputStream, wraps it in an InputStreamReader with the given charset, and delegates to the overloaded openStream(Reader) method. The mutation report shows this line was killed by all five mutations, indicating it is covered by tests. ```java return openStream(new InputStreamReader(Files.newInputStream(path), charset)); ``` -------------------------------- ### Load and export with nullChar handling Source: https://jeyben.github.io/fixedformat4j/examples Shows loading records where all-spaces in the credit-limit slot produce null, and zero-padded values produce 0. Also demonstrates exporting null as five spaces and exporting a normal value with zero padding. ```java FixedFormatManager manager = new FixedFormatManagerImpl(); // All-spaces in the credit-limit slot → null AccountRecord r1 = manager.load(AccountRecord.class, "ACC01 "); System.out.println(r1.getCreditLimit()); // null // Zero-padded value → 0 (not null) AccountRecord r2 = manager.load(AccountRecord.class, "ACC0200000"); System.out.println(r2.getCreditLimit()); // 0 // Export null → five spaces r1.setAccountId("ACC03"); r1.setCreditLimit(null); System.out.println(manager.export(r1)); // "ACC03 " // Export a value normally r1.setCreditLimit(1500); System.out.println(manager.export(r1)); // "ACC0301500" ``` -------------------------------- ### Migration: Replace Align with RecordAlign in @Record annotation Source: https://jeyben.github.io/fixedformat4j/changelog Shows the required change from `Align.RIGHT` to `RecordAlign.RIGHT` for the `align` attribute of `@Record`. The before example uses the old enum, and the after example uses the new one. Records without an explicit `align` attribute are unaffected and default to `RecordAlign.LEFT`. ```java // Before (1.7.x) @Record(length = 20, align = Align.RIGHT) public class MyRecord { … } ``` ```java // After (1.8.0+) @Record(length = 20, align = RecordAlign.RIGHT) public class MyRecord { … } ``` -------------------------------- ### build method: result.add(buildDescriptor(...)) Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.format.impl/ClassMetadataCache.java.html Line 95 of the build method. Three mutations: substituted 1 with 0, removed call to buildDescriptor, and removed call to List.add. All were killed. ```java result.add(buildDescriptor(clazz, target, fieldAnnotation, true, scanner, instructionsBuilder, repeatingFieldSupport)); ``` -------------------------------- ### Returning result string Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.format.impl/FixedFormatManagerImpl.java.html Returning the final formatted string. ```java return result.toString(); ``` -------------------------------- ### Repeating fields with count attribute (array and List) Source: https://jeyben.github.io/fixedformat4j/changelog Maps three consecutive 5-character product codes starting at offset 1 to either a String[] or List using the new 'count' attribute. Each slot occupies 5 characters, starting at offset + length * index. ```java // Three 5-character product codes packed consecutively from position 1 @Field(offset = 1, length = 5, count = 3) public String[] getProductCodes() { return productCodes; } // Same field mapped to a List @Field(offset = 1, length = 5, count = 3) public List getProductCodes() { return productCodes; } ``` -------------------------------- ### Get Record annotation Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.format.impl/FormatInstructionsBuilder.java.html Retrieves the Record annotation from the declaring class. ```java Record recordAnno = declaringClass.getAnnotation(Record.class); ``` -------------------------------- ### process section comment Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.io.read/FixedFormatReader.java.html Comment separator indicating the start of the process section. ```java // --- process --- ``` -------------------------------- ### toString() Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.format/FormatInstructions.java.html Returns a string representation of the FormatInstructions object, including all its fields. ```APIDOC ## toString() ### Description Returns a string representation of the FormatInstructions object, including all its fields. ### Method GET ### Endpoint `FormatInstructions.toString()` ### Parameters None ### Response #### Success Response - **String** - A string representation of the object. #### Response Example ```java String str = formatInstructions.toString(); ``` ``` -------------------------------- ### FixedFormatReader usage with LinePattern.prefix Source: https://jeyben.github.io/fixedformat4j/changelog Builds a FixedFormatReader with two mappings (HeaderRecord and DetailRecord) using prefix patterns, reads from a Path, and retrieves typed lists without casting. Demonstrates the read() method returning a ReadResult. ```java import com.ancientprogramming.fixedformat4j.io.read.LinePattern; FixedFormatReader reader = FixedFormatReader.builder() .addMapping(HeaderRecord.class, LinePattern.prefix("HDR")) .addMapping(DetailRecord.class, LinePattern.prefix("DTL")) .build(); ReadResult result = reader.read(Path.of("data.txt")); List headers = result.get(HeaderRecord.class); // no cast List details = result.get(DetailRecord.class); // no cast ``` -------------------------------- ### Get actual type arguments Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.format.impl/RepeatingFieldSupport.java.html Retrieves the actual type arguments from the ParameterizedType using getActualTypeArguments(). ```java Type[] typeArgs = pt.getActualTypeArguments(); ``` -------------------------------- ### Build a strict heterogeneous file reader Source: https://jeyben.github.io/fixedformat4j/usage/file-processing Configures a reader for multiple record types that throws an exception if a line does not match any pattern. ```java FixedFormatReader reader = FixedFormatReader.builder() .addMapping(HeaderRecord.class, LinePattern.prefix("HDR")) .addMapping(DetailRecord.class, LinePattern.prefix("DTL")) .build(); ``` -------------------------------- ### Get return type of getter method Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.format.impl/RepeatingFieldSupport.java.html Retrieves the return type of the getter method using getReturnType(). ```java Class returnType = getter.getReturnType(); ``` -------------------------------- ### buildDescriptor: build FormatInstructions Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.format.impl/ClassMetadataCache.java.html Line 127 of buildDescriptor: builds FormatInstructions using FormatInstructionsBuilder.build. The call removal mutation was killed. ```java FormatInstructions formatInstructions = instructionsBuilder.build(target.annotationSource, fieldAnnotation, elementType, clazz); ``` -------------------------------- ### PositionsKey Constructor Initialization Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.io.read/RecordMappingIndex.java.html Constructor for the PositionsKey class, showing mutations related to variable assignment and hash code calculation. ```java PositionsKey(int[] positions) { ``` ```java this.positions = positions; ``` ```java this.hash = Arrays.hashCode(positions); ``` ```java } ``` -------------------------------- ### @FixedFormatBoolean usage example Source: https://jeyben.github.io/fixedformat4j/usage/annotations Configures a Boolean field to use 'Y' and 'N' instead of the default 'T' and 'F' representations. ```java @Field(offset = 1, length = 1) @FixedFormatBoolean(trueValue = "Y", falseValue = "N") public Boolean getActive() { return active; } ``` -------------------------------- ### @FixedFormatDecimal usage Source: https://jeyben.github.io/fixedformat4j/usage/annotations Example of using @FixedFormatDecimal with implicit decimal points where the last two digits represent the fractional part. ```java @Field(offset = 1, length = 6, align = Align.RIGHT, paddingChar = '0') @FixedFormatDecimal(decimals = 2, useDecimalDelimiter = false) public BigDecimal getAmount() { return amount; } ``` -------------------------------- ### Build FormatInstructions object Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.format.impl/FormatInstructionsBuilder.java.html Constructs a FormatInstructions object using the field annotation's attributes and resolved alignment. This is the return statement of the build method. ```java return new FormatInstructions(fieldAnno.length(), resolvedAlign, fieldAnno.paddingChar(), fieldAnno.nullChar(), fieldAnno.nullValue(), patternData, booleanData, numberData, decimalData, enumData); ``` -------------------------------- ### Get generic return type Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.format.impl/RepeatingFieldSupport.java.html Retrieves the generic return type of the method using getGenericReturnType(). This is used to handle parameterized types. ```java Type genericReturnType = method.getGenericReturnType(); ``` -------------------------------- ### Load and export ShipmentRecord with FixedFormatManager Source: https://jeyben.github.io/fixedformat4j/examples Shows loading a line into a ShipmentRecord, printing parsed values, updating the package weights, and exporting back to a fixed-width string. The output shows the resulting line. ```java FixedFormatManager manager = new FixedFormatManagerImpl(); String line = "SHIP01002500010000000000000000"; ShipmentRecord record = manager.load(ShipmentRecord.class, line); System.out.println(record.getShipmentId()); // "SHIP01" System.out.println(record.getPackageWeights()); // [2500, 1000, 0, 0] // Update the first two weights and export record.setPackageWeights(List.of(3200, 1800, 500, 0)); System.out.println(manager.export(record)); // "SHIP01003200001800000500000000" ``` -------------------------------- ### Check if return type is an array Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.format.impl/RepeatingFieldSupport.java.html Checks if the return type is an array using isArray(). If true, the method proceeds to get the component type. ```java if (returnType.isArray()) { ``` -------------------------------- ### Build a lenient heterogeneous file reader Source: https://jeyben.github.io/fixedformat4j/usage/file-processing Configures a reader that skips and logs lines that do not match any defined pattern. ```java FixedFormatReader reader = FixedFormatReader.builder() .addMapping(HeaderRecord.class, LinePattern.prefix("HDR")) .addMapping(DetailRecord.class, LinePattern.prefix("DTL")) .unmatchStrategy(UnmatchStrategy.skip()) .build(); ``` -------------------------------- ### Write lazy stream with FixedFormatWriter Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.io.write/FixedFormatWriter.java.html Quick start for writing a lazy stream of records, avoiding materializing all records in memory. The stream is closed automatically after writing. ```java try (Stream stream = repository.findAll()) { writer.write(Path.of("out.txt"), stream); } ``` -------------------------------- ### Build the FixedFormatReader with LinePattern mappings Source: https://jeyben.github.io/fixedformat4j/examples Constructs a FixedFormatReader by adding mappings for OrderHeader and OrderDetail using LinePattern.prefix("HDR") and LinePattern.prefix("DTL"). This reader is shared across the output shapes below. ```java import com.ancientprogramming.fixedformat4j.io.read.LinePattern; FixedFormatReader reader = FixedFormatReader.builder() .addMapping(OrderHeader.class, LinePattern.prefix("HDR")) .addMapping(OrderDetail.class, LinePattern.prefix("DTL")) .build(); ``` -------------------------------- ### Load and export OrderRecord with zero-padded integers Source: https://jeyben.github.io/fixedformat4j/examples Loads an OrderRecord from a line "WIDGET-A 000120000150" and prints the parsed values ("WIDGET-A", 12, 150). After setting quantity to 5, exports the record, producing "WIDGET-A 000050000150". Demonstrates the zero-padding on export. ```java String line = "WIDGET-A 000120000150"; OrderRecord record = manager.load(OrderRecord.class, line); System.out.println(record.getProductCode()); // "WIDGET-A" System.out.println(record.getQuantity()); // 12 System.out.println(record.getUnitPriceCents()); // 150 record.setQuantity(5); System.out.println(manager.export(record)); // "WIDGET-A 000050000150" ``` -------------------------------- ### build method: result.add(buildDescriptor(...)) inside loop Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.format.impl/ClassMetadataCache.java.html Line 99 of the build method. Seven mutations: removed call to buildDescriptor, removed call to List.add, substituted 0 with 1, replaced equality check with true, substituted 1 with 0, replaced equality check with false, and negated conditional. All were killed. ```java result.add(buildDescriptor(clazz, target, fields[i], i == 0, scanner, instructionsBuilder, repeatingFieldSupport)); ``` -------------------------------- ### openStream method return and factory comment Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.io.read/FixedFormatReader.java.html The code shows the return statement of the openStream method, which filters and maps the stream to the requested type. The following lines close the method and start a factory section comment. ```java return openStream(path).filter(clazz::isInstance).map(r -> (T) r); ``` ```java } ``` ```java ``` ```java // --- factory --- ``` -------------------------------- ### buildDescriptor: create FormatContext Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.format.impl/ClassMetadataCache.java.html Line 128 of buildDescriptor: creates a prototype FormatContext. Mutations killed include offset call removal, FormatContext constructor removal, and formatter call removal. ```java FormatContext protoContext = new FormatContext(fieldAnnotation.offset(), elementType, fieldAnnotation.formatter()); ``` -------------------------------- ### Build a single-type file reader Source: https://jeyben.github.io/fixedformat4j/usage/file-processing Configures a reader for a file containing only one type of record using matchAll. ```java FixedFormatReader reader = FixedFormatReader.builder() .addMapping(EmployeeRecord.class, LinePattern.matchAll()) .build(); ``` -------------------------------- ### RecordMappingIndex build method Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.io.read/RecordMappingIndex.java.html The build method constructs a new RecordMappingIndex instance using immutable copies of the matchAll and byPositions collections. ```java RecordMappingIndex build() { return new RecordMappingIndex(List.copyOf(matchAll), List.copyOf(byPositions.values())); } ``` -------------------------------- ### ShipmentRecord with @Field count for repeating package weights Source: https://jeyben.github.io/fixedformat4j/examples Java record class using @Field(count = 4) to map four consecutive 6-character integer slots starting at offset 7. The packageWeights field is a List. ```java @Record public class ShipmentRecord { private String shipmentId; private List packageWeights; @Field(offset = 1, length = 6) public String getShipmentId() { return shipmentId; } public void setShipmentId(String shipmentId) { this.shipmentId = shipmentId; } // Four consecutive 6-character integer slots starting at offset 7 @Field(offset = 7, length = 6, count = 4, align = Align.RIGHT, paddingChar = '0') public List getPackageWeights() { return packageWeights; } public void setPackageWeights(List packageWeights) { this.packageWeights = packageWeights; } } ``` -------------------------------- ### scan method: strip method prefix from getter name Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.format.impl/AnnotationScanner.java.html Line 59 of the scan method. Strips the method prefix (e.g., 'get') from the getter name to form the field key. Mutations include replacing the call and removing the getName call. ```java String key = stripMethodPrefix(getter.getName()); ``` -------------------------------- ### JavaRecordSupport.java components method with reflection and exception handling Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.format.impl/JavaRecordSupport.java.html The source displays a sequence of code fragments from the JavaRecordSupport.java file, each annotated with mutation coverage information. The fragments show a loop that iterates over record components, uses reflection to invoke getName and getType methods, adds ComponentInfo objects to a result list, and throws a FixedFormatException with a formatted message on ReflectiveOperationException. The code is incomplete as displayed, missing the method signature and surrounding context. ```java for (Object component : components) { ``` ```java String name = (String) component.getClass().getMethod("getName").invoke(component); ``` ```java Class type = (Class) component.getClass().getMethod("getType").invoke(component); ``` ```java result.add(new ComponentInfo(name, type)); ``` ```java } ``` ```java return result; ``` ```java } catch (ReflectiveOperationException e) { ``` ```java throw new FixedFormatException( ``` ```java format("unable to read record components of %s", recordClass.getName()), e); ``` ```java } ``` ```java } ``` ```java } ``` -------------------------------- ### Builder add Method Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.io.read/RecordMappingIndex.java.html Registers a pattern and mapping to the index, handling both depth-based and position-based bucket assignments. ```java Builder add(LinePattern pattern, RecordMapping mapping) { Objects.requireNonNull(pattern, "pattern must not be null"); Objects.requireNonNull(mapping, "mapping must not be null"); IndexedMapping im = new IndexedMapping(sequence++, pattern.depth(), mapping); if (pattern.depth() == 0) { matchAll.add(im); return this; } int[] positions = pattern.positions(); Bucket bucket = byPositions.computeIfAbsent(new PositionsKey(positions), k -> new Bucket(positions)); bucket.byLiteral.computeIfAbsent(pattern.literal(), k -> new ArrayList<>()).add(im); return this; } ``` -------------------------------- ### asString method implementation Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.format.impl/LocalDateTimeFormatter.java.html Formats a LocalDateTime object into a string based on provided format instructions. ```java /** {@inheritDoc} */ ``` ```java public String asString(LocalDateTime dateTime, FormatInstructions instructions) { ``` ```java if (dateTime == null) { ``` ```java return null; ``` ```java } ``` ```java return formatterForPattern(instructions.getFixedFormatPatternData().getPattern()).format(dateTime); ``` ```java } ``` -------------------------------- ### Iterate and dispatch records using getAll() Source: https://jeyben.github.io/fixedformat4j/usage/file-processing Use getAll() to retrieve a list of all parsed records in encounter order. This approach requires manual type checking and casting for each record type. ```java List all = reader.read(Path.of("data.txt")).getAll(); for (Object record : all) { if (record instanceof X1Record) { handleX1((X1Record) record); } else if (record instanceof X2Record) { handleX2((X2Record) record); } else if (record instanceof K40001Record) { handleK40001((K40001Record) record); } else if (record instanceof K40002Record) { handleK40002((K40002Record) record); } else if (record instanceof K41001Record) { handleK41001((K41001Record) record); } else if (record instanceof K41003Record) { handleK41003((K41003Record) record); } else if (record instanceof OtherRecord) { handleOther((OtherRecord) record); } } ``` -------------------------------- ### allMatches() Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.io.read/MultiMatchStrategy.java.html Returns a strategy that uses all matching mappings. This is a built-in strategy provided as a static factory method. ```APIDOC ## allMatches() ### Description Returns a strategy that uses all matching mappings. This is a built-in strategy provided as a static factory method. ### Method allMatches ### Signature `static MultiMatchStrategy allMatches()` ### Returns - **MultiMatchStrategy** - a strategy that uses all matches; never null ``` -------------------------------- ### Record-level alignment with @Record(align = ...) Source: https://jeyben.github.io/fixedformat4j/changelog Demonstrates the new `align` attribute on `@Record` to set a default alignment for all fields, avoiding repetition. Individual fields can still override with an explicit `@Field(align = ...)`. The example shows the 'before' (repeated alignment) and 'after' (record-level alignment) states. ```java // Before — alignment repeated on every field @Record(length = 20) public class MyRecord { @Field(offset = 1, length = 10, align = Align.RIGHT, paddingChar = '0') public Integer getField1() { … } @Field(offset = 11, length = 10, align = Align.RIGHT, paddingChar = '0') public Integer getField2() { … } } ``` ```java // After — alignment declared once at record level @Record(length = 20, align = Align.RIGHT) public class MyRecord { @Field(offset = 1, length = 10, paddingChar = '0') public Integer getField1() { … } @Field(offset = 11, length = 10, paddingChar = '0') public Integer getField2() { … } } ``` -------------------------------- ### Register custom type formatter via FixedFormatManagerImpl.builder() Source: https://jeyben.github.io/fixedformat4j/changelog Register a custom type-to-formatter mapping once at startup. Custom registrations shadow built-in formatters; last registration wins without exception. The builder is only available on the concrete implementation. ```java FixedFormatManager manager = FixedFormatManagerImpl.builder() .registerType(UUID.class, UUIDFormatter.class) .build(); ``` -------------------------------- ### builder() Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.io.write/FixedFormatWriter.java.html Returns a new FixedFormatWriterBuilder instance for configuring and creating a FixedFormatWriter. ```APIDOC ## builder() ### Description Returns a new FixedFormatWriterBuilder instance for configuring and creating a FixedFormatWriter. ### Method builder ### Parameters None ### Return Type FixedFormatWriterBuilder ``` -------------------------------- ### build method: for (int i = 0; i < fields.length; i++) { Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.format.impl/ClassMetadataCache.java.html Line 98 of the build method. Five mutations: changed conditional boundary, negated conditional, replaced comparison check with true, substituted 0 with 1, and replaced comparison check with false. All were killed. ```java for (int i = 0; i < fields.length; i++) { ``` -------------------------------- ### Build a FixedFormatWriter with optional charset and line separator Source: https://jeyben.github.io/fixedformat4j/changelog Create a FixedFormatWriter for writing fixed-format files. The charset and line separator are optional; defaults are UTF-8 and the system line separator. The writer manages resources and line delimiting, while formatting is delegated to FixedFormatManager.export(). ```java FixedFormatWriter writer = FixedFormatWriter.builder() .charset(StandardCharsets.ISO_8859_1) // optional; default UTF-8 .lineSeparator("\r\n") // optional; default System.lineSeparator() .build(); ``` -------------------------------- ### @FixedFormatPattern with Date Source: https://jeyben.github.io/fixedformat4j/usage/annotations Demonstrates using the default pattern versus a custom pattern for java.util.Date fields. ```java // Default pattern (yyyyMMdd) — no annotation needed @Field(offset = 1, length = 8) public Date getHireDate() { return hireDate; } // Custom pattern @Field(offset = 1, length = 10) @FixedFormatPattern("yyyy-MM-dd") public Date getHireDate() { return hireDate; } ``` -------------------------------- ### Catch block for formatter instantiation Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.format.impl/ByTypeFormatter.java.html Handles exceptions during formatter instantiation. ```java } catch (Exception e) { ``` -------------------------------- ### readWithMappingCallback method Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.io.read/FixedFormatReader.java.html Reads lines from a buffered reader, increments a line counter, and delegates each line to the processor. Throws FixedFormatIOException with a formatted message on IO errors. ```java private void readWithMappingCallback(Reader reader, BiConsumer, Object> callback) { BufferedReader buffered = toBuffered(reader); long[] lineCounter = {0L}; try (buffered) { String line; while ((line = buffered.readLine()) != null) { processor.processLine(line, ++lineCounter[0], callback); } } catch (IOException e) { throw new FixedFormatIOException(format("IO error reading line %d", lineCounter[0] + 1), e); } } ``` -------------------------------- ### asObject method implementation Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.format.impl/LocalDateTimeFormatter.java.html Parses a string into a LocalDateTime object using provided format instructions. ```java /** {@inheritDoc} */ ``` ```java public LocalDateTime asObject(String string, FormatInstructions instructions) throws FixedFormatException { ``` ```java if (string == null || string.isEmpty()) { ``` ```java return null; ``` ```java } ``` ```java String pattern = instructions.getFixedFormatPatternData().getPattern(); ``` ```java try { ``` ```java return LocalDateTime.parse(string, formatterForPattern(pattern)); ``` ```java } catch (DateTimeParseException e) { ``` ```java throw new FixedFormatException(String.format("Could not parse value[%s] by pattern[%s] to %s", string, pattern, LocalDateTime.class.getName()), e); ``` ```java } ``` ```java } ``` -------------------------------- ### buildDescriptor: comment about formatter unwrapping Source: https://jeyben.github.io/fixedformat4j/pit-reports/com.ancientprogramming.fixedformat4j.format.impl/ClassMetadataCache.java.html Lines 129-131 of buildDescriptor: comments explaining why the formatter is not unwrapped. No mutations listed. ```java // Deliberately NOT unwrapped via resolveConcreteFormatter: ByTypeFormatter resolves its ``` ```java // delegate lazily, keeping the unsupported-element-type failure at parse time ``` ```java // (wrapped in ParseException) exactly as before metadata caching. The custom registry is ```