/******************************************************************************* * (c) Copyright 2016 Hewlett-Packard Development Company, L.P. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Apache License v2.0 which accompany this distribution. * * The Apache License is available at * http://www.apache.org/licenses/LICENSE-2.0 * *******************************************************************************/ package io.cloudslang.lang.compiler.parser; import io.cloudslang.lang.compiler.SlangSource; import io.cloudslang.lang.compiler.parser.model.ParsedDescriptionData; import io.cloudslang.lang.compiler.parser.utils.ParserExceptionHandler; import io.cloudslang.lang.compiler.utils.MetadataUtils; import io.cloudslang.lang.compiler.utils.SlangSourceUtils; import io.cloudslang.lang.compiler.validator.matcher.DescriptionPatternMatcher; import java.util.List; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.tuple.Pair; public class MetadataParser { private ParserExceptionHandler parserExceptionHandler; private DescriptionPatternMatcher descriptionPatternMatcher; public MetadataParser() { descriptionPatternMatcher = new DescriptionPatternMatcher(); } public ParsedDescriptionData parse(SlangSource source) { Validate.notNull(source.getContent(), "Source " + source.getName() + " cannot be null"); try { return extractDescriptionData(source); } catch (Throwable e) { throw new RuntimeException("There was a problem parsing the description: " + source.getName() + "." + System.lineSeparator() + parserExceptionHandler.getErrorMessage(e), e); } } private ParsedDescriptionData extractDescriptionData(SlangSource source) { List<String> lines = SlangSourceUtils.readLines(source); return processRawLines(lines); } private ParsedDescriptionData processRawLines(List<String> lines) { DescriptionBuilder descriptionBuilder = new DescriptionBuilder(); for (int lineNrZeroBased = 0; lineNrZeroBased < lines.size(); lineNrZeroBased++) { String currentLine = lines.get(lineNrZeroBased); // block start - #!! if (descriptionPatternMatcher.matchesDescriptionStart(currentLine)) { handleBlockStart(descriptionBuilder, lineNrZeroBased); } else // #!!# if (descriptionPatternMatcher.matchesDescriptionEnd(currentLine)) { handleDescriptionEnd(descriptionBuilder, lines, lineNrZeroBased); } else { // #! @tag var: content <=> @tag var if (descriptionPatternMatcher.matchesDescriptionVariableLine(currentLine)) { handleDescriptionLineVariableSyntax(descriptionBuilder, currentLine); } else // #! @tag: content if (descriptionPatternMatcher.matchesDescriptionGeneralLine(currentLine)) { handleDescriptionLineGeneralSyntax(descriptionBuilder, currentLine); } else { if (descriptionPatternMatcher.matchesVariableLineDeclarationOnlyLine(currentLine)) { handleDescriptionLineVariableDeclarationOnlySyntax(descriptionBuilder, currentLine); } else { // #! continued from previous line if (descriptionPatternMatcher.matchesDescriptionComplementaryLine(currentLine)) { handleDescriptionLineComplementarySyntax(descriptionBuilder, currentLine); } else { // check if line is allowed inside description if (descriptionBuilder.descriptionOpened()) { handleNonDescriptionLineInsideDescription( descriptionBuilder, currentLine, lineNrZeroBased ); } } } } } } return descriptionBuilder.build(); } private void handleNonDescriptionLineInsideDescription( DescriptionBuilder descriptionBuilder, String currentLine, int lineNumberZeroBased) { // check line fits into description if (!descriptionPatternMatcher.isLineAcceptedInsideDescription(currentLine)) { descriptionBuilder.addError( new RuntimeException( MetadataUtils.generateErrorMessage( lineNumberZeroBased, "Line is not acceptable inside description section" ) ) ); descriptionBuilder.resetCurrentDescription(); } } private void handleDescriptionEnd(DescriptionBuilder descriptionBuilder, List<String> lines, int currentLineNr) { // block end if (descriptionBuilder.descriptionOpened()) { String stepName = tryExtractStepName(lines, currentLineNr + 1); if (stepName == null) { // general description descriptionBuilder.endExecutableDescription(); } else { // step description descriptionBuilder.endStepDescription(stepName); } } // ignore } private String tryExtractStepName(List<String> lines, int lineNr) { String stepName = null; int nrOfLines = lines.size(); if (inRange(lineNr, nrOfLines)) { String currentLine = lines.get(lineNr); while (isIgnorableLine(currentLine) && inRange(lineNr, nrOfLines)) { currentLine = lines.get(lineNr++); } if (inRange(lineNr, nrOfLines)) { // investigate line if (descriptionPatternMatcher.matchesStepStartLine(currentLine)) { stepName = descriptionPatternMatcher.getStepName(currentLine); } } } return stepName; } private boolean isIgnorableLine(String line) { return StringUtils.isBlank(line) || descriptionPatternMatcher.matchesCommentLine(line); } private boolean inRange(int nr, int nrOfLines) { return nr < nrOfLines; } private void handleDescriptionLineComplementarySyntax(DescriptionBuilder descriptionBuilder, String currentLine) { // if description is opened if (descriptionBuilder.descriptionOpened()) { // add String data = descriptionPatternMatcher.getDescriptionComplementaryLineData(currentLine); data = data.trim(); descriptionBuilder.addToDescriptionToMostRecentlyUsedTag(data); } // otherwise ignore } private void handleDescriptionLineGeneralSyntax(DescriptionBuilder descriptionBuilder, String currentLine) { // if description is opened if (descriptionBuilder.descriptionOpened()) { // add Pair<String, String> data = descriptionPatternMatcher.getDescriptionGeneralLineData(currentLine); descriptionBuilder.addToDescription(data.getLeft(), data.getRight()); } // otherwise ignore } private void handleDescriptionLineVariableSyntax(DescriptionBuilder descriptionBuilder, String currentLine) { // if description is opened if (descriptionBuilder.descriptionOpened()) { // add Pair<String, String> data = descriptionPatternMatcher.getDescriptionVariableLineData(currentLine); descriptionBuilder.addToDescription(data.getLeft(), data.getRight()); } // otherwise ignore } private void handleDescriptionLineVariableDeclarationOnlySyntax( DescriptionBuilder descriptionBuilder, String currentLine) { // if description is opened if (descriptionBuilder.descriptionOpened()) { // add Pair<String, String> data = descriptionPatternMatcher.getDescriptionVariableLineDataDeclarationOnly(currentLine); descriptionBuilder.addToDescription(data.getLeft(), data.getRight()); } // otherwise ignore } private void handleBlockStart(DescriptionBuilder descriptionBuilder, int currentLineNumberZeroBased) { // not clear state if (descriptionBuilder.descriptionOpened()) { // raise exception, begin description descriptionBuilder.addError( new RuntimeException( MetadataUtils.generateErrorMessage( currentLineNumberZeroBased, "Description already in progress" ) ) ); } descriptionBuilder.beginDescription(currentLineNumberZeroBased + 1); } public void setParserExceptionHandler(ParserExceptionHandler parserExceptionHandler) { this.parserExceptionHandler = parserExceptionHandler; } }