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