Meilenstein 1 vor Implementierung der Spezifikation

This commit is contained in:
2026-03-26 01:03:55 +01:00
parent fc4386ac99
commit 1b4b22b073
27 changed files with 873 additions and 374 deletions

View File

@@ -0,0 +1,163 @@
package de.gecheckt.asv.cli;
import de.gecheckt.asv.domain.model.InputFile;
import de.gecheckt.asv.parser.DefaultInputFileParser;
import de.gecheckt.asv.parser.DefaultSegmentLineTokenizer;
import de.gecheckt.asv.parser.InputFileParseException;
import de.gecheckt.asv.parser.InputFileParser;
import de.gecheckt.asv.parser.SegmentLineTokenizer;
import de.gecheckt.asv.validation.DefaultInputFileValidator;
import de.gecheckt.asv.validation.InputFileValidator;
import de.gecheckt.asv.validation.field.DefaultFieldValidator;
import de.gecheckt.asv.validation.field.FieldValidator;
import de.gecheckt.asv.validation.model.ValidationResult;
import de.gecheckt.asv.validation.structure.DefaultStructureValidator;
import de.gecheckt.asv.validation.structure.StructureValidator;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* Haupteinstiegspunkt für die ASV Format Validator CLI-Anwendung.
*
* Diese Anwendung validiert Dateien gegen ein segmentorientiertes Dateiformat.
* Sie nimmt einen Dateipfad als Kommandozeilenargument entgegen, parst die Datei,
* validiert sie und gibt die Ergebnisse auf der Konsole aus.
*/
public class AsvValidatorApplication {
private static final int EXIT_CODE_SUCCESS = 0;
private static final int EXIT_CODE_INVALID_ARGUMENTS = 1;
private static final int EXIT_CODE_FILE_ERROR = 2;
private static final int EXIT_CODE_VALIDATION_ERRORS = 3;
private static final Logger logger = LogManager.getLogger(AsvValidatorApplication.class);
private final InputFileParser parser;
private final InputFileValidator validator;
private final ValidationResultPrinter printer;
/**
* Konstruktor für einen AsvValidatorApplication mit Standardkomponenten.
*/
public AsvValidatorApplication() {
// Initialize all required components
SegmentLineTokenizer tokenizer = new DefaultSegmentLineTokenizer();
this.parser = new DefaultInputFileParser(tokenizer);
StructureValidator structureValidator = new DefaultStructureValidator();
FieldValidator fieldValidator = new DefaultFieldValidator();
this.validator = new DefaultInputFileValidator(structureValidator, fieldValidator);
this.printer = new ValidationResultPrinter();
}
/**
* Konstruktor für einen AsvValidatorApplication mit den bereitgestellten Komponenten.
* Dieser Konstruktor unterstützt Dependency Injection für bessere Testbarkeit.
*
* @param parser der Parser zum Parsen von Eingabedateien
* @param validator der Validator zum Validieren geparster Dateien
* @param printer der Printer zum Anzeigen von Validierungsergebnissen
*/
public AsvValidatorApplication(InputFileParser parser, InputFileValidator validator, ValidationResultPrinter printer) {
this.parser = parser;
this.validator = validator;
this.printer = printer;
}
/**
* Haupteinstiegspunkt für die Anwendung.
*
* @param args Kommandozeilenargumente - erwartet genau ein Argument: den Dateipfad
*/
public static void main(String[] args) {
AsvValidatorApplication app = new AsvValidatorApplication();
int exitCode = app.run(args);
System.exit(exitCode);
}
/**
* Führt die Anwendung mit den bereitgestellten Argumenten aus.
*
* @param args Kommandozeilenargumente
* @return Exit-Code (0 für Erfolg, ungleich 0 für Fehler)
*/
public int run(String[] args) {
// Validate command line arguments
if (args.length != 1) {
printUsage();
return EXIT_CODE_INVALID_ARGUMENTS;
}
String filePath = args[0];
try {
// Parse the file
InputFile inputFile = parseFile(filePath);
// Validate the parsed file
ValidationResult result = validator.validate(inputFile);
// Output results
printer.printToConsole(result);
// Return appropriate exit code based on validation results
return result.hasErrors() ? EXIT_CODE_VALIDATION_ERRORS : EXIT_CODE_SUCCESS;
} catch (IOException e) {
logger.error("Fehler beim Lesen der Datei: {}", e.getMessage(), e);
System.err.println("Fehler beim Lesen der Datei: " + e.getMessage());
return EXIT_CODE_FILE_ERROR;
} catch (InputFileParseException e) {
logger.error("Fehler beim Parsen der Datei: {}", e.getMessage(), e);
System.err.println("Fehler beim Parsen der Datei: " + e.getMessage());
return EXIT_CODE_FILE_ERROR;
} catch (Exception e) {
logger.error("Unerwarteter Fehler während der Validierung: {}", e.getMessage(), e);
System.err.println("Unerwarteter Fehler während der Validierung: " + e.getMessage());
return EXIT_CODE_FILE_ERROR;
}
}
/**
* Parst eine Datei unter dem gegebenen Pfad.
*
* @param filePath Pfad zur zu parsenden Datei
* @return geparstes InputFile-Objekt
* @throws IOException wenn die Datei nicht gelesen werden kann
* @throws InputFileParseException wenn die Datei nicht geparst werden kann
*/
private InputFile parseFile(String filePath) throws IOException, InputFileParseException {
Path path = Paths.get(filePath);
if (!Files.exists(path)) {
throw new IOException("File does not exist: " + filePath);
}
if (!Files.isRegularFile(path)) {
throw new IOException("Path is not a regular file: " + filePath);
}
if (!Files.isReadable(path)) {
throw new IOException("File is not readable: " + filePath);
}
String fileContent = Files.readString(path, StandardCharsets.UTF_8);
return parser.parse(path.getFileName().toString(), fileContent);
}
/**
* Gibt Nutzungsinformationen auf der Konsole aus.
*/
private void printUsage() {
System.out.println("ASV Format Validator");
System.out.println("Verwendung: java -jar asv-format-validator.jar <datei-pfad>");
System.out.println(" <datei-pfad> Pfad zur zu validierenden Datei");
}
}

View File

@@ -0,0 +1,67 @@
package de.gecheckt.asv.cli;
import de.gecheckt.asv.validation.model.ValidationError;
import de.gecheckt.asv.validation.model.ValidationResult;
/**
* Printer-Klasse zum Ausgeben von ValidationResult-Objekten auf der Konsole.
* Diese Klasse trennt die Darstellungslogik vom Datenmodell.
*/
public class ValidationResultPrinter {
/**
* Gibt ein ValidationResult formatiert auf der Konsole aus.
*
* @param result das auszugebende ValidationResult (darf nicht null sein)
*/
public void printToConsole(ValidationResult result) {
if (result == null) {
throw new IllegalArgumentException("ValidationResult must not be null");
}
System.out.println("=== Validierungsergebnis ===");
if (result.hasErrors()) {
System.out.println("Fehler (" + result.getErrors().size() + "):");
result.getErrors().forEach(error -> System.out.println(" [ERROR] " + formatError(error)));
}
if (result.hasWarnings()) {
System.out.println("Warnungen (" + result.getWarnings().size() + "):");
result.getWarnings().forEach(error -> System.out.println(" [WARNING] " + formatError(error)));
}
if (result.hasInfos()) {
System.out.println("Informationen (" + result.getInfos().size() + "):");
result.getInfos().forEach(error -> System.out.println(" [INFO] " + formatError(error)));
}
if (!result.hasErrors() && !result.hasWarnings() && !result.hasInfos()) {
System.out.println("Keine Probleme gefunden.");
}
System.out.println("============================");
}
/**
* Formatiert einen ValidationError für die Konsolenausgabe.
*
* @param error der zu formatierende Fehler
* @return formatierte Zeichenkettendarstellung des Fehlers
*/
private String formatError(ValidationError error) {
var sb = new StringBuilder();
sb.append(error.description());
sb.append(" (Code: ").append(error.errorCode()).append(")");
sb.append(" im Segment '").append(error.segmentName()).append("' Position ").append(error.segmentPosition());
sb.append(", Feld '").append(error.fieldName()).append("' Position ").append(error.fieldPosition());
error.getActualValue().ifPresent(value ->
sb.append(", Ist-Wert: '").append(value).append("'"));
error.getExpectedRule().ifPresent(rule ->
sb.append(", Erwartet: '").append(rule).append("'"));
return sb.toString();
}
}

View File

@@ -4,7 +4,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
/**
* Represents a message in an input file.
@@ -92,7 +91,7 @@ public record Message(int messagePosition, List<Segment> segments) {
return segments.stream()
.filter(segment -> segmentName.equals(segment.segmentName()))
.collect(Collectors.toList());
.toList();
}
/**

View File

@@ -5,29 +5,28 @@ import de.gecheckt.asv.domain.model.InputFile;
import de.gecheckt.asv.domain.model.Message;
import de.gecheckt.asv.domain.model.Segment;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
/**
* Default implementation of InputFileParser.
* Standardimplementierung von InputFileParser.
*/
public class DefaultInputFileParser implements InputFileParser {
private final SegmentLineTokenizer tokenizer;
/**
* Constructs a DefaultInputFileParser with the specified tokenizer.
* Konstruktor für einen DefaultInputFileParser mit dem angegebenen Tokenizer.
*
* @param tokenizer the tokenizer to use for parsing segment lines
* @param tokenizer der für das Parsen von Segmentzeilen zu verwendende Tokenizer
*/
public DefaultInputFileParser(SegmentLineTokenizer tokenizer) {
this.tokenizer = tokenizer;
}
@Override
public InputFile parse(String fileName, String fileContent) throws IOException {
public InputFile parse(String fileName, String fileContent) throws InputFileParseException {
if (fileName == null || fileName.isEmpty()) {
throw new IllegalArgumentException("File name must not be null or empty");
}
@@ -58,7 +57,8 @@ public class DefaultInputFileParser implements InputFileParser {
return new InputFile(fileName, messages);
} catch (Exception e) {
throw new IOException("Error parsing file: " + fileName, e);
// Wrap all exceptions as parsing exceptions
throw new InputFileParseException("Error parsing file: " + fileName, e);
}
}
}

View File

@@ -5,8 +5,8 @@ import java.util.ArrayList;
import java.util.List;
/**
* Default implementation of SegmentLineTokenizer that uses '+' as field separator
* and assumes the first token is the segment name.
* Standardimplementierung von SegmentLineTokenizer, die '+' als Feldtrennzeichen verwendet
* und annimmt, dass das erste Token der Segmentname ist.
*/
public class DefaultSegmentLineTokenizer implements SegmentLineTokenizer {

View File

@@ -0,0 +1,30 @@
package de.gecheckt.asv.parser;
/**
* Exception thrown when an input file cannot be parsed due to format or content issues.
* This exception is used specifically for parsing-related problems, as opposed to
* I/O issues which would be represented by IOException.
*/
public class InputFileParseException extends Exception {
private static final long serialVersionUID = 1L;
/**
* Constructs a new InputFileParseException with the specified detail message.
*
* @param message the detail message
*/
public InputFileParseException(String message) {
super(message);
}
/**
* Constructs a new InputFileParseException with the specified detail message and cause.
*
* @param message the detail message
* @param cause the cause of this exception
*/
public InputFileParseException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -1,20 +1,19 @@
package de.gecheckt.asv.parser;
import de.gecheckt.asv.domain.model.InputFile;
import java.io.IOException;
/**
* Interface for parsing input files into the domain model.
* Interface für das Parsen von Eingabedateien in das Domänenmodell.
*/
public interface InputFileParser {
/**
* Parses the content of a file into an InputFile domain object.
* Parst den Inhalt einer Datei in ein InputFile-Domänenobjekt.
*
* @param fileName the name of the file to parse
* @param fileContent the content of the file to parse
* @return the parsed InputFile domain object
* @throws IOException if there is an error reading or parsing the file
* @param fileName der Name der zu parsenden Datei
* @param fileContent der Inhalt der zu parsenden Datei
* @return das geparste InputFile-Domänenobjekt
* @throws InputFileParseException wenn beim Parsen des Dateiinhalts ein Fehler auftritt
*/
InputFile parse(String fileName, String fileContent) throws IOException;
InputFile parse(String fileName, String fileContent) throws InputFileParseException;
}

View File

@@ -4,23 +4,23 @@ import de.gecheckt.asv.domain.model.Field;
import java.util.List;
/**
* Interface for splitting a segment line into its components.
* Interface für das Aufteilen einer Segmentzeile in ihre Bestandteile.
*/
public interface SegmentLineTokenizer {
/**
* Extracts the segment name from a segment line.
* Extrahiert den Segmentnamen aus einer Segmentzeile.
*
* @param segmentLine the line to extract the segment name from
* @return the segment name
* @param segmentLine die Zeile, aus der der Segmentname extrahiert werden soll
* @return der Segmentname
*/
String extractSegmentName(String segmentLine);
/**
* Splits a segment line into fields.
* Teilt eine Segmentzeile in Felder auf.
*
* @param segmentLine the line to split into fields
* @return the list of fields
* @param segmentLine die in Felder aufzuteilende Zeile
* @return die Liste der Felder
*/
List<Field> tokenizeFields(String segmentLine);
}

View File

@@ -10,13 +10,14 @@ import de.gecheckt.asv.validation.model.ValidationResult;
import de.gecheckt.asv.validation.structure.StructureValidator;
/**
* Default implementation of InputFileValidator that orchestrates multiple specialized validators.
* Standardimplementierung von InputFileValidator, die mehrere spezialisierte Validatoren orchestriert.
*
* This orchestrator executes validators in a predefined order:
* 1. StructureValidator - validates structural integrity
* 2. FieldValidator - validates field-specific rules
* Dieser Orchestrator führt Validatoren in einer vordefinierten Reihenfolge aus:
* 1. StructureValidator - validiert die strukturelle Integrität
* 2. FieldValidator - validiert feldspezifische Regeln
*
* Additional validators can be added in the future by extending this class or modifying the validation sequence.
* Zusätzliche Validatoren können in Zukunft hinzugefügt werden, indem diese Klasse erweitert
* oder die Validierungssequenz modifiziert wird.
*/
public class DefaultInputFileValidator implements InputFileValidator {
@@ -24,10 +25,10 @@ public class DefaultInputFileValidator implements InputFileValidator {
private final FieldValidator fieldValidator;
/**
* Constructs a DefaultInputFileValidator with the required validators.
* Konstruktor für einen DefaultInputFileValidator mit den erforderlichen Validatoren.
*
* @param structureValidator the structure validator to use (must not be null)
* @param fieldValidator the field validator to use (must not be null)
* @param structureValidator der zu verwendende Strukturvalidator (darf nicht null sein)
* @param fieldValidator der zu verwendende Feldvalidator (darf nicht null sein)
*/
public DefaultInputFileValidator(StructureValidator structureValidator, FieldValidator fieldValidator) {
this.structureValidator = Objects.requireNonNull(structureValidator, "structureValidator must not be null");

View File

@@ -4,17 +4,17 @@ import de.gecheckt.asv.domain.model.InputFile;
import de.gecheckt.asv.validation.model.ValidationResult;
/**
* Interface for orchestrating the validation of an ASV input file.
* This validator coordinates multiple specialized validators to perform a complete validation.
* Interface für die Orchestrierung der Validierung einer ASV-Eingabedatei.
* Dieser Validator koordiniert mehrere spezialisierte Validatoren, um eine vollständige Validierung durchzuführen.
*/
public interface InputFileValidator {
/**
* Validates the given input file using all configured validators.
* Validiert die gegebene Eingabedatei unter Verwendung aller konfigurierten Validatoren.
*
* @param inputFile the input file to validate (must not be null)
* @return a validation result containing all errors found by all validators
* @throws IllegalArgumentException if inputFile is null
* @param inputFile die zu validierende Eingabedatei (darf nicht null sein)
* @return ein Validierungsergebnis, das alle von allen Validatoren gefundenen Fehler enthält
* @throws IllegalArgumentException wenn inputFile null ist
*/
ValidationResult validate(InputFile inputFile);
}

View File

@@ -1,60 +0,0 @@
package de.gecheckt.asv.validation.example;
import de.gecheckt.asv.validation.model.ValidationError;
import de.gecheckt.asv.validation.model.ValidationResult;
import de.gecheckt.asv.validation.model.ValidationSeverity;
import java.util.Arrays;
import java.util.List;
/**
* Beispielanwendung zur Demonstration der Verwendung der Validierungsmodelle.
*/
public class ValidationExample {
public static void main(String[] args) {
// Erstelle einige Beispielvalidierungsfehler
ValidationError error1 = new ValidationError(
"FORMAT001",
"Ungültiges Datumsformat",
ValidationSeverity.ERROR,
"KOPF",
1,
"ERSTELLUNGSDATUM",
3,
"2023-99-99",
"YYYY-MM-DD"
);
ValidationError warning1 = new ValidationError(
"LENGTH001",
"Feldlänge überschritten",
ValidationSeverity.WARNING,
"POSITION",
5,
"ARTIKELBEZEICHNUNG",
2,
"Extrem langer Artikelname, der die maximale Feldlänge überschreitet",
"Max. 30 Zeichen"
);
ValidationError info1 = new ValidationError(
"OPTIONAL001",
"Optionales Feld ist leer",
ValidationSeverity.INFO,
"FUSS",
100,
"BEMERKUNG",
1,
null,
"Freitextfeld für zusätzliche Informationen"
);
// Erstelle ein ValidationResult-Objekt
List<ValidationError> validationErrors = Arrays.asList(error1, warning1, info1);
ValidationResult result = new ValidationResult(validationErrors);
// Gib das Ergebnis auf der Konsole aus
result.printToConsole();
}
}

View File

@@ -13,12 +13,10 @@ import de.gecheckt.asv.validation.model.ValidationSeverity;
* Default implementation of FieldValidator that checks general field rules.
*
* Rules checked:
* 1. Field.rawValue must not be null
* 2. Field.rawValue must not be empty
* 3. Field.rawValue must not consist only of whitespaces
* 4. fieldPosition must be positive
* 5. Field positions within a segment should be consecutive without gaps, starting at 1
* 6. If fieldName is set, it must not be empty or only whitespace
* 1. Field.rawValue must not be empty
* 2. Field.rawValue must not consist only of whitespaces
* 3. Field positions within a segment should be consecutive without gaps, starting at 1
* 4. If fieldName is set, it must not be empty or only whitespace
*/
public class DefaultFieldValidator implements FieldValidator {
@@ -73,65 +71,33 @@ public class DefaultFieldValidator implements FieldValidator {
var fieldPosition = field.fieldPosition();
var fieldName = field.getFieldName().orElse("");
// Rule 1: Field.rawValue must not be null
// (This is already enforced by the domain model, but we check for completeness)
if (rawValue == null) {
// Rule 2: Field.rawValue must not be empty
if (rawValue.isEmpty()) {
errors.add(createError(
"FIELD_001",
"Field raw value must not be null",
"FIELD_002",
"Field raw value must not be empty",
ValidationSeverity.ERROR,
segmentName,
segmentPosition,
fieldName,
fieldPosition,
"null",
"Non-null raw value required"
rawValue,
"Non-empty raw value required"
));
} else {
// Rule 2: Field.rawValue must not be empty
if (rawValue.isEmpty()) {
errors.add(createError(
"FIELD_002",
"Field raw value must not be empty",
ValidationSeverity.ERROR,
segmentName,
segmentPosition,
fieldName,
fieldPosition,
rawValue,
"Non-empty raw value required"
));
}
// Rule 3: Field.rawValue must not consist only of whitespaces
if (rawValue.trim().isEmpty() && !rawValue.isEmpty()) {
errors.add(createError(
"FIELD_003",
"Field raw value must not consist only of whitespaces",
ValidationSeverity.ERROR,
segmentName,
segmentPosition,
fieldName,
fieldPosition,
rawValue,
"Non-whitespace-only raw value required"
));
}
}
// Rule 4: fieldPosition must be positive
// (This is already enforced by the domain model, but we check for completeness)
if (fieldPosition <= 0) {
// Rule 3: Field.rawValue must not consist only of whitespaces
if (rawValue.trim().isEmpty() && !rawValue.isEmpty()) {
errors.add(createError(
"FIELD_004",
"Field position must be positive",
"FIELD_003",
"Field raw value must not consist only of whitespaces",
ValidationSeverity.ERROR,
segmentName,
segmentPosition,
fieldName,
fieldPosition,
String.valueOf(fieldPosition),
"Positive field position required"
rawValue,
"Non-whitespace-only raw value required"
));
}

View File

@@ -4,17 +4,17 @@ import de.gecheckt.asv.domain.model.InputFile;
import de.gecheckt.asv.validation.model.ValidationResult;
/**
* Interface for validating fields in an ASV input file.
* This validator checks general field rules without requiring specification details.
* Interface für die Validierung von Feldern in einer ASV-Eingabedatei.
* Dieser Validator prüft allgemeine Feldregeln ohne Angabe von Spezifikationsdetails.
*/
public interface FieldValidator {
/**
* Validates the fields in the given input file.
* Validiert die Felder in der gegebenen Eingabedatei.
*
* @param inputFile the input file to validate (must not be null)
* @return a validation result containing any field errors found
* @throws IllegalArgumentException if inputFile is null
* @param inputFile die zu validierende Eingabedatei (darf nicht null sein)
* @return ein Validierungsergebnis, das alle gefundenen Feldfehler enthält
* @throws IllegalArgumentException wenn inputFile null ist
*/
ValidationResult validate(InputFile inputFile);
}

View File

@@ -109,56 +109,6 @@ public final class ValidationResult {
return errors;
}
/**
* Gibt eine textbasierte Darstellung des Validierungsergebnisses auf der Konsole aus.
*/
public void printToConsole() {
System.out.println("=== Validierungsergebnis ===");
if (hasErrors()) {
System.out.println("Fehler (" + getErrors().size() + "):");
getErrors().forEach(error -> System.out.println(" [ERROR] " + formatError(error)));
}
if (hasWarnings()) {
System.out.println("Warnungen (" + getWarnings().size() + "):");
getWarnings().forEach(error -> System.out.println(" [WARNING] " + formatError(error)));
}
if (hasInfos()) {
System.out.println("Informationen (" + getInfos().size() + "):");
getInfos().forEach(error -> System.out.println(" [INFO] " + formatError(error)));
}
if (!hasErrors() && !hasWarnings() && !hasInfos()) {
System.out.println("Keine Probleme gefunden.");
}
System.out.println("============================");
}
/**
* Formatiert einen ValidationError für die Konsolenausgabe.
*
* @param error der zu formatierende Fehler
* @return formatierte Zeichenkette
*/
private String formatError(ValidationError error) {
var sb = new StringBuilder();
sb.append(error.description());
sb.append(" (Code: ").append(error.errorCode()).append(")");
sb.append(" im Segment '").append(error.segmentName()).append("' Position ").append(error.segmentPosition());
sb.append(", Feld '").append(error.fieldName()).append("' Position ").append(error.fieldPosition());
error.getActualValue().ifPresent(value ->
sb.append(", Ist-Wert: '").append(value).append("'"));
error.getExpectedRule().ifPresent(rule ->
sb.append(", Erwartet: '").append(rule).append("'"));
return sb.toString();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;

View File

@@ -4,17 +4,17 @@ import de.gecheckt.asv.domain.model.InputFile;
import de.gecheckt.asv.validation.model.ValidationResult;
/**
* Interface for validating the structural integrity of an ASV input file.
* This validator checks general structural rules without requiring specification details.
* Interface für die Validierung der strukturellen Integrität einer ASV-Eingabedatei.
* Dieser Validator prüft allgemeine Strukturregeln ohne Angabe von Spezifikationsdetails.
*/
public interface StructureValidator {
/**
* Validates the structural integrity of the given input file.
* Validiert die strukturelle Integrität der gegebenen Eingabedatei.
*
* @param inputFile the input file to validate (must not be null)
* @return a validation result containing any structural errors found
* @throws IllegalArgumentException if inputFile is null
* @param inputFile die zu validierende Eingabedatei (darf nicht null sein)
* @return ein Validierungsergebnis, das alle gefundenen Strukturfehler enthält
* @throws IllegalArgumentException wenn inputFile null ist
*/
ValidationResult validate(InputFile inputFile);
}