package com.baselet.element.elementnew.plot.parser; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.ListIterator; import java.util.Map.Entry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Parser { private static final Logger log = LoggerFactory.getLogger(Parser.class); // The parserResult contains every information which is relevant after input parsing is finished private final ParserResult parserResult; // The following fields are only used during parsing but never referenced after parsing is finished // The values HashsMap contains the actual state of key->value assignments which is copied to every plot at its time of creation private final HashMap<String, KeyValue> tempPlotValuesCache; // The datasetNr is used for sequential naming of not explicitly named datasets private int datasetNr = 1; // The datasetlist is filled during parsing. After parsing every plot gets its dataset injected private final ArrayList<DataSet> datasetList; public Parser() { parserResult = new ParserResult(); datasetList = new ArrayList<DataSet>(); tempPlotValuesCache = new HashMap<String, KeyValue>(); } public ParserResult parse(String source) { List<String> inputList = Arrays.asList(source.split("\n", -1)); ListIterator<String> inputIterator = inputList.listIterator(); while (inputIterator.hasNext()) { String line = inputIterator.next(); if (line.isEmpty() || line.matches(PlotConstants.REGEX_COMMENT)) {/* ignore empty lines and comments */} else if (line.matches(PlotConstants.REGEX_PLOT)) { parserResult.addPlotState(createPlotStateObject(line.split(" "), inputIterator)); } else if (line.matches(PlotConstants.REGEX_PLOT_ADD)) { List<PlotState> plotStates = parserResult.getPlotStateList(); if (plotStates.isEmpty()) { // if no plotStates, create a new one parserResult.addPlotState(createPlotStateObject(line.split(" "), inputIterator)); } else { // if plots exist, add new plotState to last plotState PlotState last = plotStates.get(plotStates.size() - 1); last.addSubPlot(createPlotStateObject(line.split(" "), inputIterator)); } } else if (line.matches(PlotConstants.REGEX_DATA)) { createDatasetObject(line.split(" "), inputIterator); } else if (line.matches(PlotConstants.REGEX_DATA_GUESS)) { inputIterator.previous(); // Must go 1 step back to avoid skipping the first line in createDatasetObject createDatasetObject(new String[] { PlotConstants.DATA }, inputIterator); } else if (line.matches(PlotConstants.REGEX_VALUE_ASSIGNMENT)) { createKeyValueAssignment(line, inputIterator.nextIndex()); } else { throw new ParserException("Invalid line: " + line + "(line: " + inputIterator.nextIndex() + ")"); } } analyseDatasets(); addDatasetsToPlotStates(); return parserResult; } /** * Is called after parsing everything to analyse the dataset content */ private void analyseDatasets() { for (DataSet dataset : datasetList) { dataset.analyseMatrix(); } } /** * Is called after parsing everything to fill datasets in each plotState Object */ private void addDatasetsToPlotStates() { if (datasetList.isEmpty()) { throw new ParserException("You must specify at least one dataset."); } int actualAutoDatasetNr = 0; for (PlotState plotState : parserResult.getPlotStateList()) { actualAutoDatasetNr = addDataset(plotState, actualAutoDatasetNr); // also add datasets to subplots for (PlotState subPlotState : plotState.getSubplots()) { log.info("Add dataset for subplot"); actualAutoDatasetNr = addDataset(subPlotState, actualAutoDatasetNr); } } } private int addDataset(PlotState plotState, int actualAutoDatasetNr) { String datasetId = plotState.getValue(PlotConstants.DATA, null); if (datasetId == null) { if (actualAutoDatasetNr >= datasetList.size()) { actualAutoDatasetNr = 0; } plotState.setDataSet(datasetList.get(actualAutoDatasetNr++)); } else { DataSet dataset = null; if (datasetId.startsWith("#")) { String datasetNr = datasetId.substring(1); for (DataSet tempDataset : datasetList) { if (datasetNr.equals(String.valueOf(tempDataset.getNr()))) { dataset = tempDataset; } } } else { for (DataSet tempDataset : datasetList) { if (datasetId.equals(tempDataset.getId())) { dataset = tempDataset; } } } if (dataset != null) { plotState.setDataSet(dataset); } else { throw new ParserException(PlotConstants.DATA, datasetId, plotState.getLine(PlotConstants.DATA)); } } return actualAutoDatasetNr; } /** * Creates a dataset with the second argument as its id (if it has no such parameter it gets a generated id) * This method is called if the input string starts with "data" or if the input string contains a tab (then a dataset is assumed) * All lines until the next empty line are part of the dataset * * @param args any parameters to the data command including the command itself as first parameter */ private void createDatasetObject(String[] args, ListIterator<String> inputIterator) { int lineNr = inputIterator.nextIndex(); String datasetId = null; if (args != null) { if (args.length > 1) { datasetId = args[1]; /* handle further parameters here */ } } DataSet newDataset = new DataSet(datasetId, datasetNr++, lineNr); while (inputIterator.hasNext()) { String nextLine = inputIterator.next(); if (nextLine.matches(PlotConstants.REGEX_COMMENT)) { continue; } else if (nextLine.trim().isEmpty()) { break; } else { newDataset.addLine(nextLine.split(PlotConstants.REGEX_DATA_SEPARATOR)); } } if (datasetId != null) { for (DataSet ds : datasetList) { if (datasetId.equals(ds.getId())) { throw new ParserException("The dataset name \"" + datasetId + "\" (line: " + lineNr + ") already exists"); } } } datasetList.add(newDataset); } /** * Creates a plotValues Object which contains the dataset as its second argument (if it has no such parameter it cycles through all datasets) * This method is called if the input string starts with "plot". All values which are stored in the parser are copied to the plot * * @param args any parameters to the data command including the command itself as first parameter */ private PlotState createPlotStateObject(String[] args, ListIterator<String> inputIterator) { int lineNr = inputIterator.nextIndex(); HashMap<String, KeyValue> localCopyOfValuesCache = copyHashMap(tempPlotValuesCache); if (args != null) { // Arguments are handled as any other key->value assignment but are only valid for this plot for (int i = 1; i < args.length; i++) { String[] split = args[i].split("="); if (split.length == 1) { split = new String[] { split[0], "" }; } localCopyOfValuesCache.put(split[0], new KeyValue(split[0], split[1], lineNr)); } } // If no dataset is specified the data-value is set to auto if (localCopyOfValuesCache.get(PlotConstants.DATA) == null) { localCopyOfValuesCache.put(PlotConstants.DATA, new KeyValue(PlotConstants.DATA, PlotConstants.DEFAULT_VALUE, lineNr)); } PlotState newPlotState = new PlotState(lineNr, localCopyOfValuesCache); return newPlotState; } /** * Adds a key->value assignment to the values HashMap */ private void createKeyValueAssignment(String line, int lineNr) { String[] split = line.split("="); if (split.length == 1) { split = new String[] { split[0], "" }; } if (split[0].matches(PlotConstants.KEY_INT_GRID_WIDTH)) { parserResult.addPlotGridValue(split[0], new KeyValue(split[0], split[1], lineNr)); } else { tempPlotValuesCache.put(split[0], new KeyValue(split[0], split[1], lineNr)); } } private HashMap<String, KeyValue> copyHashMap(HashMap<String, KeyValue> inputHashMap) { HashMap<String, KeyValue> returnHashMap = new HashMap<String, KeyValue>(); for (Entry<String, KeyValue> entry : inputHashMap.entrySet()) { returnHashMap.put(entry.getKey(), entry.getValue()); } return returnHashMap; } }