/
shvidkuli
/
kkrjava
Обзор
Документация
Войти
/
shvidkuli
/
kkrjava
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/main/java/com/montecarlo/utils/ConfigReader.java
218 строк
8 KB
kroc400
first_commit
30 апр 2026, 22:44
30 апр 2026, 22:44
2347fdb
Код
Авторство
О чём код?
package com.montecarlo.utils; import com.montecarlo.geometry.Arc; import com.montecarlo.geometry.FigureDebf; import com.montecarlo.geometry.Point; import com.montecarlo.geometry.Triangle; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.util.LinkedHashMap; import java.util.Map; /** * Reads an INI-formatted configuration file and constructs the application objects. * * <h3>INI format</h3> * <ul> * <li>Sections are enclosed in {@code [brackets]}.</li> * <li>Key-value pairs use {@code key=value} syntax.</li> * <li>Lines starting with {@code #} or {@code ;} (after optional whitespace) are comments.</li> * <li>Inline comments after a value are also stripped.</li> * <li>Blank lines are ignored.</li> * </ul> * * <h3>Expected sections</h3> * <pre> * [rectangle] — ax, ay, bx, by, cx, cy, dx, dy * [figure_points] — ex, ey, fx, fy * [experiments] — sizes (comma-separated list of N values) * </pre> * * <h3>Figure construction</h3> * From the loaded points the following objects are built automatically: * <ul> * <li><b>Triangle BEF</b>: vertices B, E, F.</li> * <li><b>Semicircle</b>: diameter F→E, centre = midpoint(F,E), radius = |FE|/2, * opened towards D (used as the reference point).</li> * </ul> */ public class ConfigReader { private static final Logger log = LogManager.getLogger(ConfigReader.class); /** Parsed INI data: section name → (key → value). */ private final Map<String, Map<String, String>> sections = new LinkedHashMap<>(); // ----------------------------------------------------------------------- // Parsing // ----------------------------------------------------------------------- /** * Loads and parses an INI file from the given path. * Must be called before any {@code read*()} method. * * @param path filesystem path to the INI file * @throws IOException if the file cannot be opened or read */ public void load(String path) throws IOException { sections.clear(); log.debug("Parsing configuration file: {}", path); String currentSection = null; try (BufferedReader reader = new BufferedReader(new FileReader(path))) { String raw; int lineNo = 0; while ((raw = reader.readLine()) != null) { lineNo++; String line = stripInlineComment(raw).trim(); if (line.isEmpty()) { continue; } if (line.startsWith("[") && line.contains("]")) { currentSection = line.substring(1, line.indexOf(']')).trim(); sections.put(currentSection, new LinkedHashMap<String, String>()); log.debug(" [{}]", currentSection); } else if (line.contains("=")) { if (currentSection == null) { log.warn("Line {} ignored (no active section): {}", lineNo, line); continue; } int eq = line.indexOf('='); String key = line.substring(0, eq).trim(); String value = line.substring(eq + 1).trim(); sections.get(currentSection).put(key, value); log.debug(" {} = {}", key, value); } } } log.info("Configuration loaded from: {}", path); } // ----------------------------------------------------------------------- // Figure construction // ----------------------------------------------------------------------- /** * Constructs and returns the {@link FigureDebf} described by the loaded configuration. * * <p>The triangle uses vertices B, E, F; the semicircle uses the chord F–E * as its diameter and point D as the reference that determines which half-plane * is "inside".</p> * * @return the configured figure * @throws RuntimeException if a required key or section is missing */ public FigureDebf readFigure() { // Rectangle vertices Point b = new Point(getDouble("rectangle", "bx"), getDouble("rectangle", "by")); Point d = new Point(getDouble("rectangle", "dx"), getDouble("rectangle", "dy")); // Additional figure points Point e = new Point(getDouble("figure_points", "ex"), getDouble("figure_points", "ey")); Point f = new Point(getDouble("figure_points", "fx"), getDouble("figure_points", "fy")); // Triangle BEF Triangle triangle = new Triangle(b, e, f); log.debug("Triangle BEF constructed: {}", triangle); // Semicircle: diameter F→E, opened towards D double cx = (f.getX() + e.getX()) / 2.0; double cy = (f.getY() + e.getY()) / 2.0; Point center = new Point(cx, cy); double radius = f.distanceTo(e) / 2.0; Arc arc = new Arc(center, radius, d); log.debug("Semicircle constructed: {}", arc); FigureDebf figure = new FigureDebf(triangle, arc); log.info("Figure 'debf' constructed. Analytical area = {}", String.format("%.10f", figure.analyticalArea())); return figure; } // ----------------------------------------------------------------------- // Experiment sizes // ----------------------------------------------------------------------- /** * Returns the list of N values for Monte Carlo experiments. * * <p>Reads the {@code sizes} key from the {@code [experiments]} section. * Falls back to {10³, 10⁴, 10⁵, 10⁶, 10⁷} if the section or key is absent.</p> * * @return array of sample counts, one per experiment */ public long[] readExperimentSizes() { long[] defaults = {1_000L, 10_000L, 100_000L, 1_000_000L, 10_000_000L}; Map<String, String> sec = sections.get("experiments"); if (sec == null) { log.warn("Section [experiments] not found; using defaults."); return defaults; } String sizesStr = sec.get("sizes"); if (sizesStr == null || sizesStr.isEmpty()) { log.warn("Key 'sizes' not found in [experiments]; using defaults."); return defaults; } String[] parts = sizesStr.split(","); long[] sizes = new long[parts.length]; for (int i = 0; i < parts.length; i++) { sizes[i] = Long.parseLong(parts[i].trim()); } return sizes; } // ----------------------------------------------------------------------- // Helpers // ----------------------------------------------------------------------- /** * Retrieves a double value from the given section and key. * * @param section INI section name * @param key key within the section * @return parsed double value * @throws RuntimeException if the section or key does not exist, or the value is not numeric */ private double getDouble(String section, String key) { Map<String, String> sec = sections.get(section); if (sec == null) { throw new RuntimeException("Missing INI section: [" + section + "]"); } String val = sec.get(key); if (val == null) { throw new RuntimeException( "Missing key '" + key + "' in section [" + section + "]"); } try { return Double.parseDouble(val); } catch (NumberFormatException ex) { throw new RuntimeException( "Cannot parse double for key '" + key + "' in [" + section + "]: '" + val + "'"); } } /** * Strips trailing inline comments ({@code #} or {@code ;}) from a line. * The first unquoted occurrence of {@code #} or {@code ;} marks the start * of the comment. * * @param line raw line from the file * @return line with the inline comment portion removed */ private String stripInlineComment(String line) { int hashIdx = line.indexOf('#'); int semiIdx = line.indexOf(';'); int idx = -1; if (hashIdx >= 0) { idx = hashIdx; } if (semiIdx >= 0 && (idx < 0 || semiIdx < idx)) { idx = semiIdx; } return idx >= 0 ? line.substring(0, idx) : line; } }