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