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