Merge branch 'master' of ../controller
[yangtools.git] / yang / yang-parser-rfc7950 / src / main / java / org / opendaylight / yangtools / yang / parser / rfc7950 / stmt / extension / ExtensionEffectiveStatementImpl.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.rfc7950.stmt.extension;
9
10 import com.google.common.collect.ImmutableList;
11 import java.util.ArrayDeque;
12 import java.util.ArrayList;
13 import java.util.Collection;
14 import java.util.Deque;
15 import java.util.List;
16 import java.util.Objects;
17 import java.util.Optional;
18 import org.eclipse.jdt.annotation.NonNull;
19 import org.eclipse.jdt.annotation.Nullable;
20 import org.opendaylight.yangtools.util.RecursiveObjectLeaker;
21 import org.opendaylight.yangtools.yang.common.QName;
22 import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition;
23 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
24 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
25 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
26 import org.opendaylight.yangtools.yang.model.api.stmt.ArgumentEffectiveStatement;
27 import org.opendaylight.yangtools.yang.model.api.stmt.ExtensionEffectiveStatement;
28 import org.opendaylight.yangtools.yang.model.api.stmt.ExtensionStatement;
29 import org.opendaylight.yangtools.yang.model.api.stmt.YinElementEffectiveStatement;
30 import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.AbstractEffectiveDocumentedNode;
31 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
32
33 final class ExtensionEffectiveStatementImpl extends AbstractEffectiveDocumentedNode<QName, ExtensionStatement>
34         implements ExtensionDefinition, ExtensionEffectiveStatement {
35     private static final class RecursionDetector extends ThreadLocal<Deque<ExtensionEffectiveStatementImpl>> {
36         boolean check(final ExtensionEffectiveStatementImpl current) {
37             final Deque<ExtensionEffectiveStatementImpl> stack = get();
38             if (stack != null) {
39                 for (ExtensionEffectiveStatementImpl s : stack) {
40                     if (s == current) {
41                         return true;
42                     }
43                 }
44             }
45             return false;
46         }
47
48         void push(final ExtensionEffectiveStatementImpl current) {
49             Deque<ExtensionEffectiveStatementImpl> stack = get();
50             if (stack == null) {
51                 stack = new ArrayDeque<>(1);
52                 set(stack);
53             }
54
55             stack.push(current);
56         }
57
58         void pop() {
59             Deque<ExtensionEffectiveStatementImpl> stack = get();
60             stack.pop();
61             if (stack.isEmpty()) {
62                 remove();
63             }
64         }
65     }
66
67     private static final RecursionDetector TOSTRING_DETECTOR = new RecursionDetector();
68
69     private final @NonNull QName qname;
70     private final @Nullable String argument;
71     private final @NonNull SchemaPath schemaPath;
72
73     private final @NonNull ImmutableList<UnknownSchemaNode> unknownNodes;
74     private final boolean yin;
75
76     private ExtensionEffectiveStatementImpl(
77             final StmtContext<QName, ExtensionStatement, EffectiveStatement<QName, ExtensionStatement>> ctx) {
78         super(ctx);
79         this.qname = ctx.coerceStatementArgument();
80         this.schemaPath = ctx.getSchemaPath().get();
81
82         final List<UnknownSchemaNode> unknownNodesInit = new ArrayList<>();
83         for (EffectiveStatement<?, ?> unknownNode : effectiveSubstatements()) {
84             if (unknownNode instanceof UnknownSchemaNode) {
85                 unknownNodesInit.add((UnknownSchemaNode) unknownNode);
86             }
87         }
88         this.unknownNodes = ImmutableList.copyOf(unknownNodesInit);
89
90         // initFields
91         final Optional<ArgumentEffectiveStatement> optArgumentSubstatement = findFirstEffectiveSubstatement(
92             ArgumentEffectiveStatement.class);
93         if (optArgumentSubstatement.isPresent()) {
94             final ArgumentEffectiveStatement argumentStatement = optArgumentSubstatement.get();
95             this.argument = argumentStatement.argument().getLocalName();
96             this.yin = argumentStatement.findFirstEffectiveSubstatement(YinElementEffectiveStatement.class)
97                     .map(YinElementEffectiveStatement::argument).orElse(Boolean.FALSE).booleanValue();
98         } else {
99             this.argument = null;
100             this.yin = false;
101         }
102     }
103
104     /**
105      * Create a new ExtensionEffectiveStatement, dealing with potential recursion.
106      *
107      * @param ctx Statement context
108      * @return A potentially under-initialized instance
109      */
110     static EffectiveStatement<QName, ExtensionStatement> create(
111             final StmtContext<QName, ExtensionStatement, EffectiveStatement<QName, ExtensionStatement>> ctx) {
112         // Look at the thread-local leak in case we are invoked recursively
113         final ExtensionEffectiveStatementImpl existing = RecursiveObjectLeaker.lookup(ctx,
114             ExtensionEffectiveStatementImpl.class);
115         if (existing != null) {
116             // Careful! this object is not fully initialized!
117             return existing;
118         }
119
120         RecursiveObjectLeaker.beforeConstructor(ctx);
121         try {
122             // This result is fine, we know it has been completely initialized
123             return new ExtensionEffectiveStatementImpl(ctx);
124         } finally {
125             RecursiveObjectLeaker.afterConstructor(ctx);
126         }
127     }
128
129     @Override
130     protected Collection<? extends EffectiveStatement<?, ?>> initSubstatements(
131             final Collection<? extends StmtContext<?, ?, ?>> substatementsInit) {
132         // WARNING: this leaks an incompletely-initialized object
133         RecursiveObjectLeaker.inConstructor(this);
134
135         return super.initSubstatements(substatementsInit);
136     }
137
138     @Override
139     public QName getQName() {
140         return qname;
141     }
142
143     @Override
144     public SchemaPath getPath() {
145         return schemaPath;
146     }
147
148     @Override
149     public List<UnknownSchemaNode> getUnknownSchemaNodes() {
150         return unknownNodes;
151     }
152
153     @Override
154     public String getArgument() {
155         return argument;
156     }
157
158     @Override
159     public boolean isYinElement() {
160         return yin;
161     }
162
163     @Override
164     public int hashCode() {
165         final int prime = 31;
166         int result = 1;
167         result = prime * result + Objects.hashCode(qname);
168         result = prime * result + Objects.hashCode(schemaPath);
169         return result;
170     }
171
172     @Override
173     public boolean equals(final Object obj) {
174         if (this == obj) {
175             return true;
176         }
177         if (obj == null) {
178             return false;
179         }
180         if (getClass() != obj.getClass()) {
181             return false;
182         }
183         ExtensionEffectiveStatementImpl other = (ExtensionEffectiveStatementImpl) obj;
184         return Objects.equals(qname, other.qname) && Objects.equals(schemaPath, other.schemaPath);
185     }
186
187     @Override
188     public String toString() {
189         if (TOSTRING_DETECTOR.check(this)) {
190             return recursedToString();
191         }
192
193         TOSTRING_DETECTOR.push(this);
194         try {
195             return ExtensionEffectiveStatementImpl.class.getSimpleName() + "["
196                     + "argument=" + argument
197                     + ", qname=" + qname
198                     + ", schemaPath=" + schemaPath
199                     + ", extensionSchemaNodes=" + unknownNodes
200                     + ", yin=" + yin
201                     + "]";
202         } finally {
203             TOSTRING_DETECTOR.pop();
204         }
205     }
206
207     private String recursedToString() {
208         return ExtensionEffectiveStatementImpl.class.getSimpleName() + "["
209                 + "argument=" + argument
210                 + ", qname=" + qname
211                 + ", schemaPath=" + schemaPath
212                 + ", yin=" + yin
213                 + " <RECURSIVE> ]";
214     }
215 }