package com.opensr5.ini; import com.devexperts.logging.Logging; import com.opensr5.ini.field.*; import com.rusefi.core.FindFileHelper; import java.io.*; import java.util.*; /** * Andrey Belomutskiy, (c) 2013-2020 * 12/23/2015. */ public class IniFileModel { private static final Logging log = Logging.getLogging(IniFileModel.class); public static final String RUSEFI_INI_PREFIX = "rusefi"; public static final String RUSEFI_INI_SUFFIX = ".ini"; public static final String INI_FILE_PATH = System.getProperty("ini_file_path", ".."); private static final String SECTION_PAGE = "page"; private static final String FIELD_TYPE_SCALAR = "scalar"; public static final String FIELD_TYPE_STRING = "string"; private static final String FIELD_TYPE_ARRAY = "array"; private static final String FIELD_TYPE_BITS = "bits"; public Map> defines = new TreeMap<>(); private static IniFileModel INSTANCE; private String dialogId; private String dialogUiName; private final Map dialogs = new TreeMap<>(); // this is only used while reading model - TODO extract reader private final List fieldsOfCurrentDialog = new ArrayList<>(); public Map allIniFields = new LinkedHashMap<>(); public final Map fieldsInUiOrder = new LinkedHashMap<>(); public Map tooltips = new TreeMap<>(); public Map protocolMeta = new TreeMap<>(); private boolean isConstantsSection; private String currentYBins; private String currentXBins; private final Map xBinsByZBins = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); private final Map yBinsByZBins = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); public static void main(String[] args) { IniFileModel iniFile = new IniFileModel(); iniFile.findAndReadIniFile(INI_FILE_PATH); log.info("Dialogs: " + iniFile.dialogs); } private boolean isInSettingContextHelp = false; private boolean isInsidePageDefinition; public IniFileModel findAndReadIniFile(String iniFilePath) { String fileName = findMetaInfoFile(iniFilePath); return readIniFile(fileName); } public IniFileModel readIniFile(String fileName) { File input = null; if (fileName != null) input = new File(fileName); if (fileName == null || !input.exists()) { log.error("No such file: " + fileName); return null; } log.info("Reading " + fileName); RawIniFile content = IniFileReader.read(input); readIniFile(content); return this; } public IniFileModel readIniFile(RawIniFile content) { for (RawIniFile.Line line : content.getLines()) { handleLine(line); } finishDialog(); return this; } private static String findMetaInfoFile(String iniFilePath) { return FindFileHelper.findFile(iniFilePath, RUSEFI_INI_PREFIX, RUSEFI_INI_SUFFIX); } private void finishDialog() { if (fieldsOfCurrentDialog.isEmpty()) return; if (dialogUiName == null) dialogUiName = dialogId; dialogs.put(dialogUiName, new DialogModel(dialogId, dialogUiName, fieldsOfCurrentDialog)); dialogId = null; fieldsOfCurrentDialog.clear(); } private void handleLine(RawIniFile.Line line) { String rawText = line.getRawText(); try { LinkedList list = new LinkedList<>(Arrays.asList(line.getTokens())); if (!list.isEmpty() && list.get(0).equals("#define")) { defines.put(list.get(1), list.subList(2, list.size())); return; } if (!list.isEmpty() && list.get(0).equals(SECTION_PAGE)) { isInsidePageDefinition = true; return; } // todo: use TSProjectConsumer constant if (isInSettingContextHelp) { // todo: use TSProjectConsumer constant if (rawText.contains("SettingContextHelpEnd")) { isInSettingContextHelp = false; } if (list.size() == 2) tooltips.put(list.get(0), list.get(1)); return; } else if (rawText.contains("SettingContextHelp")) { isInsidePageDefinition = false; isInSettingContextHelp = true; return; } if (RawIniFile.Line.isCommentLine(rawText)) return; if (RawIniFile.Line.isPreprocessorDirective(rawText)) return; trim(list); if (list.isEmpty()) return; String first = list.getFirst(); if (first.startsWith("[") && first.endsWith("]")) { log.info("Section " + first); isConstantsSection = first.equals("[Constants]"); } if (isConstantsSection) { if (isInsidePageDefinition) { if (list.size() > 1) handleFieldDefinition(list, line); return; } else { if (list.size() > 1) { protocolMeta.put(list.get(0), list.get(1)); } } } switch (first) { case "field": handleField(list); break; case "slider": handleSlider(list); break; case "dialog": handleDialog(list); break; case "table": handleTable(list); break; case "xBins": handleXBins(list); break; case "yBins": handleYBins(list); break; case "zBins": handleZBins(list); break; } } catch (RuntimeException e) { throw new IllegalStateException("Failed to handle [" + rawText + "]: " + e, e); } } private void handleZBins(LinkedList list) { list.removeFirst(); String zBins = list.removeFirst(); addField(zBins); if (currentXBins == null || currentYBins == null) throw new IllegalStateException("X or Y missing for " + zBins); xBinsByZBins.put(zBins, currentXBins); yBinsByZBins.put(zBins, currentYBins); } public String getXBin(String tableName) { return xBinsByZBins.get(tableName); } public Set getTables() { return xBinsByZBins.keySet(); } public String getYBin(String tableName) { return yBinsByZBins.get(tableName); } private void handleYBins(LinkedList list) { list.removeFirst(); currentYBins = list.removeFirst(); addField(currentYBins); } private void handleXBins(LinkedList list) { list.removeFirst(); currentXBins = list.removeFirst(); addField(currentXBins); } private void addField(String key) { DialogModel.Field field = new DialogModel.Field(key, key); fieldsInUiOrder.put(key, field); } private void handleTable(LinkedList list) { list.removeFirst(); String tableName = list.removeFirst(); } private void handleFieldDefinition(LinkedList list, RawIniFile.Line line) { switch (list.get(1)) { case FIELD_TYPE_SCALAR: registerField(ScalarIniField.parse(list)); break; case FIELD_TYPE_STRING: registerField(StringIniField.parse(list)); break; case FIELD_TYPE_ARRAY: registerField(ArrayIniField.parse(list)); break; case FIELD_TYPE_BITS: registerField(EnumIniField.parse(list, line, this)); break; default: throw new IllegalStateException("Unexpected " + list); } } private void registerField(IniField field) { if (allIniFields.containsKey(field.getName())) return; allIniFields.put(field.getName(), field); } private void handleSlider(LinkedList list) { list.removeFirst(); // "slider" String uiFieldName = list.isEmpty() ? "" : list.removeFirst(); String key = list.isEmpty() ? null : list.removeFirst(); registerUiField(key, uiFieldName); log.debug("IniFileModel: Slider label=[" + uiFieldName + "] : key=[" + key + "]"); } private void handleField(LinkedList list) { list.removeFirst(); // "field" String uiFieldName = list.isEmpty() ? "" : list.removeFirst(); String key = list.isEmpty() ? null : list.removeFirst(); registerUiField(key, uiFieldName); log.debug("IniFileModel: Field label=[" + uiFieldName + "] : key=[" + key + "]"); } private void registerUiField(String key, String uiFieldName) { DialogModel.Field field = new DialogModel.Field(key, uiFieldName); if (key != null) { fieldsOfCurrentDialog.add(field); fieldsInUiOrder.put(key, field); } } private void handleDialog(LinkedList list) { finishDialog(); list.removeFirst(); // "dialog" // trim(list); String keyword = list.removeFirst(); // trim(list); String name = list.isEmpty() ? null : list.removeFirst(); dialogId = keyword; dialogUiName = name; log.debug("IniFileModel: Dialog key=" + keyword + ": name=[" + name + "]"); } private void trim(LinkedList list) { while (!list.isEmpty() && list.getFirst().isEmpty()) list.removeFirst(); } public IniField findByOffset(int i) { for (IniField field : allIniFields.values()) { if (i >= field.getOffset() && i < field.getOffset() + field.getSize()) return field; } return null; } public static synchronized IniFileModel getInstance() { if (INSTANCE == null) { INSTANCE = new IniFileModel(); INSTANCE.findAndReadIniFile(INI_FILE_PATH); } return INSTANCE; } public Map getDialogs() { return dialogs; } }