Merge branch 'master' of ../controller
[yangtools.git] / yang / yang-data-impl / src / main / java / org / opendaylight / yangtools / yang / data / impl / leafref / LeafRefPathParserListenerImpl.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang.data.impl.leafref;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11
12 import com.google.common.collect.Lists;
13 import java.util.ArrayList;
14 import java.util.List;
15 import java.util.Optional;
16 import org.antlr.v4.runtime.tree.TerminalNode;
17 import org.opendaylight.yangtools.yang.common.QName;
18 import org.opendaylight.yangtools.yang.common.QNameModule;
19 import org.opendaylight.yangtools.yang.common.Revision;
20 import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefPathParser.IdentifierContext;
21 import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefPathParser.Node_identifierContext;
22 import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefPathParser.Path_argContext;
23 import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefPathParser.Path_equality_exprContext;
24 import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefPathParser.Path_predicateContext;
25 import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefPathParser.PrefixContext;
26 import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefPathParser.Rel_path_keyexprContext;
27 import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefPathParser.Relative_pathContext;
28 import org.opendaylight.yangtools.yang.model.api.Module;
29 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
30 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
31 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
32
33 final class LeafRefPathParserListenerImpl extends LeafRefPathParserBaseListener {
34
35     private final List<QNameWithPredicateBuilder> leafRefPathQnameList = new ArrayList<>();
36     private final SchemaContext schemaContext;
37     private final Module module;
38     private final Module leafrefModule;
39     // FIXME: use for identifier path completion
40     private final SchemaNode node;
41
42     private ParsingState currentParsingState = ParsingState.LEAF_REF_PATH;
43     private List<QNameWithPredicateBuilder> predicatePathKeyQnameList;
44     private QNameWithPredicateBuilder currentLeafRefPathQName;
45     private QNamePredicateBuilder currentPredicate;
46     private QNameModule currentQnameModule;
47     private String currentQNameLocalName;
48     private LeafRefPath leafRefPath;
49     private boolean relativePath = false;
50
51     private enum ParsingState {
52         LEAF_REF_PATH, PATH_PREDICATE, PREDICATE_PATH_EQUALITY_EXPR, PATH_KEY_EXPR
53     }
54
55     LeafRefPathParserListenerImpl(final SchemaContext schemaContext, final Module leafrefModule,
56             final SchemaNode currentNode) {
57         this.schemaContext = schemaContext;
58         this.module = schemaContext.findModule(currentNode.getQName().getModule()).get();
59         this.leafrefModule = leafrefModule;
60         this.node = currentNode;
61     }
62
63     @Override
64     public void enterPath_predicate(final Path_predicateContext ctx) {
65         currentParsingState = ParsingState.PATH_PREDICATE;
66         currentPredicate = new QNamePredicateBuilder();
67     }
68
69     @Override
70     public void exitPath_predicate(final Path_predicateContext ctx) {
71         currentLeafRefPathQName.addQNamePredicate(currentPredicate.build());
72         currentPredicate = null;
73         currentParsingState = ParsingState.LEAF_REF_PATH;
74     }
75
76
77     @Override
78     public void enterRel_path_keyexpr(final Rel_path_keyexprContext ctx) {
79         currentParsingState = ParsingState.PATH_KEY_EXPR;
80
81         final List<TerminalNode> dots = ctx.DOTS();
82         predicatePathKeyQnameList = new ArrayList<>(dots.size());
83         for (int i = 0; i < dots.size(); ++i) {
84             predicatePathKeyQnameList.add(QNameWithPredicateBuilder.UP_PARENT_BUILDER);
85         }
86     }
87
88     @Override
89     public void exitRel_path_keyexpr(final Rel_path_keyexprContext ctx) {
90         final LeafRefPath pathKeyExpression = LeafRefPath.create(Lists.transform(predicatePathKeyQnameList,
91             QNameWithPredicateBuilder::build), false);
92         currentPredicate.setPathKeyExpression(pathKeyExpression);
93
94         currentParsingState = ParsingState.PREDICATE_PATH_EQUALITY_EXPR;
95     }
96
97     @Override
98     public void enterRelative_path(final Relative_pathContext ctx) {
99         relativePath = true;
100         final List<TerminalNode> dots = ctx.DOTS();
101         for (int i = 0; i < dots.size(); ++i) {
102             leafRefPathQnameList.add(QNameWithPredicateBuilder.UP_PARENT_BUILDER);
103         }
104     }
105
106     @Override
107     public void enterPath_equality_expr(final Path_equality_exprContext ctx) {
108         currentParsingState = ParsingState.PREDICATE_PATH_EQUALITY_EXPR;
109     }
110
111     @Override
112     public void exitPath_equality_expr(final Path_equality_exprContext ctx) {
113         currentParsingState = ParsingState.PATH_PREDICATE;
114     }
115
116     @Override
117     public void enterPrefix(final PrefixContext ctx) {
118         final String prefix = ctx.getText();
119         if (!leafrefModule.getPrefix().equals(prefix)) {
120             final Optional<QNameModule> qnameModuleOpt = getQNameModuleForImportPrefix(leafrefModule, prefix);
121             checkArgument(qnameModuleOpt.isPresent(), "No module import for prefix: %s in module: %s", prefix,
122                 leafrefModule.getName());
123             currentQnameModule = qnameModuleOpt.get();
124         } else {
125             currentQnameModule = leafrefModule.getQNameModule();
126         }
127     }
128
129     @Override
130     public void exitPath_arg(final Path_argContext ctx) {
131         leafRefPath = LeafRefPath.create(Lists.transform(leafRefPathQnameList, QNameWithPredicateBuilder::build),
132             !relativePath);
133     }
134
135     @Override
136     public void enterIdentifier(final IdentifierContext ctx) {
137         currentQNameLocalName = ctx.getText();
138     }
139
140     @Override
141     public void exitNode_identifier(final Node_identifierContext ctx) {
142         if (currentQnameModule == null) {
143             if (relativePath) {
144                 currentQnameModule = module.getQNameModule();
145             } else {
146                 currentQnameModule = leafrefModule.getQNameModule();
147             }
148         }
149
150         if (currentParsingState == ParsingState.PREDICATE_PATH_EQUALITY_EXPR) {
151             currentPredicate.setIdentifier(QName.create(currentQnameModule, currentQNameLocalName));
152         } else {
153             final QNameWithPredicateBuilder qnameBuilder = new QNameWithPredicateBuilder(currentQnameModule,
154                 currentQNameLocalName);
155
156             if (currentParsingState == ParsingState.PATH_KEY_EXPR) {
157                 predicatePathKeyQnameList.add(qnameBuilder);
158             } else if (currentParsingState == ParsingState.LEAF_REF_PATH) {
159                 currentLeafRefPathQName = qnameBuilder;
160                 leafRefPathQnameList.add(qnameBuilder);
161             }
162         }
163         currentQnameModule = null;
164         currentQNameLocalName = null;
165     }
166
167     public LeafRefPath getLeafRefPath() {
168         return leafRefPath;
169     }
170
171     private Optional<QNameModule> getQNameModuleForImportPrefix(final Module targetModule, final String prefix) {
172         final ModuleImport moduleImport = getModuleImport(targetModule, prefix);
173         if (moduleImport == null) {
174             return Optional.empty();
175         }
176
177         final String moduleName = moduleImport.getModuleName();
178         final Optional<Revision> revision = moduleImport.getRevision();
179         return schemaContext.findModule(moduleName, revision).map(Module::getQNameModule);
180     }
181
182     private static ModuleImport getModuleImport(final Module targetModule, final String prefix) {
183         return targetModule.getImports().stream()
184             .filter(imp -> prefix.equals(imp.getPrefix())).findFirst().orElse(null);
185     }
186 }