/* * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.yangtools.yang.parser.impl; import com.google.common.base.Verify; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; import java.util.List; import javax.annotation.concurrent.Immutable; import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser.ArgumentContext; import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser.KeywordContext; import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser.StatementContext; import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParserBaseListener; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.QNameModule; import org.opendaylight.yangtools.yang.common.YangConstants; import org.opendaylight.yangtools.yang.common.YangVersion; import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition; import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase; import org.opendaylight.yangtools.yang.parser.spi.source.DeclarationInTextSource; import org.opendaylight.yangtools.yang.parser.spi.source.PrefixToModule; import org.opendaylight.yangtools.yang.parser.spi.source.QNameToStatementDefinition; import org.opendaylight.yangtools.yang.parser.spi.source.SourceException; import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference; import org.opendaylight.yangtools.yang.parser.spi.source.StatementWriter; import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.Utils; @Immutable public class YangStatementParserListenerImpl extends YangStatementParserBaseListener { private static final class Counter { private int value = 0; int getAndIncrement() { return value++; } } private final List<String> toBeSkipped = new ArrayList<>(); private final Deque<Counter> counters = new ArrayDeque<>(); private final String sourceName; private QNameToStatementDefinition stmtDef; private PrefixToModule prefixes; private StatementWriter writer; private YangVersion yangVersion; public YangStatementParserListenerImpl(final String sourceName) { this.sourceName = sourceName; } public void setAttributes(final StatementWriter writer, final QNameToStatementDefinition stmtDef) { this.writer = writer; this.stmtDef = stmtDef; initCounters(); } public void setAttributes(final StatementWriter writer, final QNameToStatementDefinition stmtDef, final PrefixToModule prefixes) { this.writer = writer; this.stmtDef = stmtDef; this.prefixes = prefixes; initCounters(); } public void setAttributes(final StatementWriter writer, final QNameToStatementDefinition stmtDef, final PrefixToModule prefixes, final YangVersion yangVersion) { this.yangVersion = yangVersion; setAttributes(writer, stmtDef, prefixes); } private void initCounters() { counters.clear(); counters.push(new Counter()); } @Override public void enterStatement(final StatementContext ctx) { final StatementSourceReference ref = DeclarationInTextSource.atPosition(sourceName, ctx.getStart().getLine(), ctx.getStart().getCharPositionInLine()); final String keywordTxt = Verify.verifyNotNull(ctx.getChild(KeywordContext.class, 0)).getText(); final QName validStatementDefinition = getValidStatementDefinition(prefixes, stmtDef, keywordTxt); final int childId = counters.peek().getAndIncrement(); counters.push(new Counter()); if (stmtDef == null || validStatementDefinition == null || !toBeSkipped.isEmpty()) { SourceException.throwIf(writer.getPhase() == ModelProcessingPhase.FULL_DECLARATION, ref, "%s is not a YANG statement or use of extension.", keywordTxt); toBeSkipped.add(keywordTxt); return; } final ArgumentContext argumentCtx = ctx.getChild(ArgumentContext.class, 0); final String argument = argumentCtx != null ? Utils.stringFromStringContext(argumentCtx, yangVersion, ref) : null; writer.startStatement(childId, validStatementDefinition, argument, ref); } @Override public void exitStatement(final StatementContext ctx) { final StatementSourceReference ref = DeclarationInTextSource.atPosition( sourceName, ctx.getStart().getLine(), ctx.getStart().getCharPositionInLine()); final KeywordContext keyword = ctx.getChild(KeywordContext.class, 0); final String statementName = keyword.getText(); if (stmtDef != null && getValidStatementDefinition(prefixes, stmtDef, statementName) != null && toBeSkipped.isEmpty()) { writer.endStatement(ref); } // No-op if the statement is not on the list toBeSkipped.remove(statementName); counters.pop(); } /** * Based on identifier read from source and collections of relevant prefixes and statement definitions mappings * provided for actual phase, method resolves and returns valid QName for declared statement to be written. * This applies to any declared statement, including unknown statements. * * @param prefixes collection of all relevant prefix mappings supplied for actual parsing phase * @param stmtDef collection of all relevant statement definition mappings provided for actual parsing phase * @param keywordText statement keyword text to parse from source * @return valid QName for declared statement to be written, or null */ private static QName getValidStatementDefinition(final PrefixToModule prefixes, final QNameToStatementDefinition stmtDef, final String keywordText) { final int firstColon = keywordText.indexOf(':'); if (firstColon == -1) { final StatementDefinition statementDefinition = stmtDef.get( new QName(YangConstants.RFC6020_YIN_NAMESPACE, keywordText)); return statementDefinition != null ? statementDefinition.getStatementName() : null; } final int secondColon = keywordText.indexOf(':', firstColon + 1); if (secondColon != -1) { // Malformed string return null; } if (prefixes == null) { // No prefixes to look up from return null; } final String prefix = keywordText.substring(0, firstColon); final QNameModule qNameModule = prefixes.get(prefix); if (qNameModule == null) { // Failed to look the namespace return null; } final String localName = keywordText.substring(firstColon + 1); final StatementDefinition foundStmtDef; if (prefixes.isPreLinkageMap()) { foundStmtDef = stmtDef.getByNamespaceAndLocalName(qNameModule.getNamespace(), localName); } else { foundStmtDef = stmtDef.get(QName.create(qNameModule, localName)); } return foundStmtDef != null ? foundStmtDef.getStatementName() : null; } }