BUG-4556: Introduce StmtContext.getSchemaPath()
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / reactor / SubstatementContext.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.parser.stmt.reactor;
9
10 import com.google.common.base.Optional;
11 import com.google.common.base.Preconditions;
12 import com.google.common.base.Verify;
13 import java.util.Collection;
14 import org.opendaylight.yangtools.yang.common.QName;
15 import org.opendaylight.yangtools.yang.common.QNameModule;
16 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
17 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
18 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
19 import org.opendaylight.yangtools.yang.model.api.stmt.AugmentStatement;
20 import org.opendaylight.yangtools.yang.model.api.stmt.ChoiceStatement;
21 import org.opendaylight.yangtools.yang.model.api.stmt.KeyStatement;
22 import org.opendaylight.yangtools.yang.model.api.stmt.RefineStatement;
23 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
24 import org.opendaylight.yangtools.yang.model.api.stmt.UsesStatement;
25 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.NamespaceStorageNode;
26 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.Registry;
27 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
28 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
29 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
30 import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace;
31 import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace.ValidationBundleType;
32 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.GroupingUtils;
33 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.Utils;
34
35 final class SubstatementContext<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
36         extends StatementContextBase<A, D, E> {
37
38     private final StatementContextBase<?, ?, ?> parent;
39     private final A argument;
40     private volatile SchemaPath schemaPath;
41
42     SubstatementContext(final StatementContextBase<?, ?, ?> parent,
43             final ContextBuilder<A, D, E> builder) throws SourceException {
44         super(builder);
45         this.parent = Preconditions.checkNotNull(parent, "Parent must not be null");
46         this.argument = builder.getDefinition().parseArgumentValue(this, builder.getRawArgument());
47     }
48
49     @SuppressWarnings("unchecked")
50     SubstatementContext(final SubstatementContext<A, D, E> original,
51             final QNameModule newQNameModule,
52             final StatementContextBase<?, ?, ?> newParent, final TypeOfCopy typeOfCopy) {
53         super(original);
54         this.parent = newParent;
55
56         if (newQNameModule != null) {
57             if (original.argument instanceof QName) {
58                 QName originalQName = (QName) original.argument;
59                 this.argument = (A) QName.create(newQNameModule, originalQName.getLocalName());
60             } else if (StmtContextUtils.producesDeclared(original, KeyStatement.class)) {
61                 this.argument = (A) StmtContextUtils.replaceModuleQNameForKey(
62                                 (StmtContext<Collection<SchemaNodeIdentifier>, KeyStatement, ?>) original,
63                                 newQNameModule);
64             } else {
65                 this.argument = original.argument;
66             }
67         } else {
68             this.argument = original.argument;
69         }
70     }
71
72
73     private void copyDeclaredStmts(final SubstatementContext<A, D, E> original,
74             final QNameModule newQNameModule, final TypeOfCopy typeOfCopy)
75             throws SourceException {
76         Collection<? extends StatementContextBase<?, ?, ?>> originalDeclaredSubstatements = original
77                 .declaredSubstatements();
78         for (StatementContextBase<?, ?, ?> stmtContext : originalDeclaredSubstatements) {
79             if (GroupingUtils.needToCopyByUses(stmtContext)) {
80                 StatementContextBase<?, ?, ?> copy = stmtContext.createCopy(
81                         newQNameModule, this, typeOfCopy);
82                 this.addEffectiveSubstatement(copy);
83             } else if (GroupingUtils.isReusedByUses(stmtContext)) {
84                 this.addEffectiveSubstatement(stmtContext);
85             }
86         }
87     }
88
89     private void copyEffectiveStmts(final SubstatementContext<A, D, E> original,
90             final QNameModule newQNameModule, final TypeOfCopy typeOfCopy)
91             throws SourceException {
92         Collection<? extends StatementContextBase<?, ?, ?>> originalEffectiveSubstatements = original
93                 .effectiveSubstatements();
94         for (StatementContextBase<?, ?, ?> stmtContext : originalEffectiveSubstatements) {
95             if (GroupingUtils.needToCopyByUses(stmtContext)) {
96                 StatementContextBase<?, ?, ?> copy = stmtContext.createCopy(
97                         newQNameModule, this, typeOfCopy);
98                 this.addEffectiveSubstatement(copy);
99             } else if (GroupingUtils.isReusedByUses(stmtContext)) {
100                 this.addEffectiveSubstatement(stmtContext);
101             }
102         }
103     }
104
105     @Override
106     public StatementContextBase<?, ?, ?> getParentContext() {
107         return parent;
108     }
109
110     @Override
111     public NamespaceStorageNode getParentNamespaceStorage() {
112         return parent;
113     }
114
115     @Override
116     public Registry getBehaviourRegistry() {
117         return parent.getBehaviourRegistry();
118     }
119
120     @Override
121     public RootStatementContext<?, ?, ?> getRoot() {
122         return parent.getRoot();
123     }
124
125     @Override
126     public A getStatementArgument() {
127         return argument;
128     }
129
130     @Override
131     public StatementContextBase<?, ?, ?> createCopy(
132             final StatementContextBase<?, ?, ?> newParent, final TypeOfCopy typeOfCopy)
133             throws SourceException {
134         return createCopy(null, newParent, typeOfCopy);
135     }
136
137     @Override
138     public StatementContextBase<A, D, E> createCopy(final QNameModule newQNameModule,
139             final StatementContextBase<?, ?, ?> newParent, final TypeOfCopy typeOfCopy)
140             throws SourceException {
141         SubstatementContext<A, D, E> copy = new SubstatementContext<>(this, newQNameModule, newParent, typeOfCopy);
142
143         copy.addAllToCopyHistory(this.getCopyHistory());
144         copy.addToCopyHistory(typeOfCopy);
145
146         if(this.getOriginalCtx() != null) {
147             copy.setOriginalCtx(this.getOriginalCtx());
148         } else {
149             copy.setOriginalCtx(this);
150         }
151
152         definition().onStatementAdded(copy);
153
154         copy.copyDeclaredStmts(this, newQNameModule, typeOfCopy);
155         copy.copyEffectiveStmts(this, newQNameModule, typeOfCopy);
156         return copy;
157     }
158
159     private boolean isSupportedAsShorthandCase() {
160         final Collection<?> supportedCaseShorthands = getFromNamespace(ValidationBundlesNamespace.class,
161                 ValidationBundleType.SUPPORTED_CASE_SHORTHANDS);
162         return supportedCaseShorthands == null || supportedCaseShorthands.contains(getPublicDefinition());
163     }
164
165     private SchemaPath createSchemaPath() {
166         final Optional<SchemaPath> maybeParentPath = parent.getSchemaPath();
167         Verify.verify(maybeParentPath.isPresent(), "Parent %s does not have a SchemaPath", parent);
168         final SchemaPath parentPath = maybeParentPath.get();
169
170         if (argument instanceof QName) {
171             QName qname = (QName) argument;
172             if (StmtContextUtils.producesDeclared(this, UsesStatement.class)) {
173                 return maybeParentPath.orNull();
174             }
175
176             final SchemaPath path;
177             if (StmtContextUtils.producesDeclared(getParentContext(), ChoiceStatement.class)
178                     && isSupportedAsShorthandCase()) {
179                 path = parentPath.createChild(qname);
180             } else {
181                 path = parentPath;
182             }
183             return path.createChild(qname);
184         }
185         if (argument instanceof String) {
186             // FIXME: This may yield illegal argument exceptions
187             final StatementContextBase<?, ?, ?> originalCtx = getOriginalCtx();
188             final QName qname = (originalCtx != null) ? Utils.qNameFromArgument(originalCtx, (String) argument)
189                     : Utils.qNameFromArgument(this, (String) argument);
190             return parentPath.createChild(qname);
191         }
192         if (argument instanceof SchemaNodeIdentifier &&
193                 (StmtContextUtils.producesDeclared(this, AugmentStatement.class)
194                    || StmtContextUtils.producesDeclared(this, RefineStatement.class))) {
195
196             return parentPath.createChild(((SchemaNodeIdentifier) argument).getPathFromRoot());
197         }
198         if (Utils.isUnknownNode(this)) {
199             return parentPath.createChild(getPublicDefinition().getStatementName());
200         }
201
202         // FIXME: this does not look right
203         return maybeParentPath.orNull();
204     }
205
206     @Override
207     public Optional<SchemaPath> getSchemaPath() {
208         SchemaPath local = schemaPath;
209         if (local == null) {
210             synchronized (this) {
211                 local = schemaPath;
212                 if (local == null) {
213                     local = createSchemaPath();
214                     schemaPath = local;
215                 }
216             }
217
218         }
219
220         return Optional.fromNullable(local);
221     }
222
223     @Override
224     public boolean isRootContext() {
225         return false;
226     }
227 }