BUG-4456: add RecursiveExtensionResolver
[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.Deque;
14 import java.util.List;
15 import java.util.Objects;
16 import org.opendaylight.yangtools.yang.common.QName;
17 import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition;
18 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
19 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
20 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
21 import org.opendaylight.yangtools.yang.model.api.stmt.ExtensionStatement;
22 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
23
24 public class ExtensionEffectiveStatementImpl extends AbstractEffectiveDocumentedNode<QName, ExtensionStatement>
25         implements ExtensionDefinition {
26     private static final class RecursionDetector extends ThreadLocal<Deque<ExtensionEffectiveStatementImpl>> {
27         boolean check(final ExtensionEffectiveStatementImpl current) {
28             final Deque<ExtensionEffectiveStatementImpl> stack = get();
29             if (stack != null) {
30                 for (ExtensionEffectiveStatementImpl s : stack) {
31                     if (s == current) {
32                         return true;
33                     }
34                 }
35             }
36             return false;
37         }
38
39         void push(final ExtensionEffectiveStatementImpl current) {
40             Deque<ExtensionEffectiveStatementImpl> stack = get();
41             if (stack == null) {
42                 stack = new ArrayDeque<>(1);
43                 set(stack);
44             }
45
46             stack.push(current);
47         }
48
49         void pop() {
50             Deque<ExtensionEffectiveStatementImpl> stack = get();
51             stack.pop();
52             if (stack.isEmpty()) {
53                 remove();
54             }
55         }
56     }
57
58     private static final RecursionDetector TOSTRING_DETECTOR = new RecursionDetector();
59
60     private final QName qname;
61     private final String argument;
62     private final SchemaPath schemaPath;
63
64     private final List<UnknownSchemaNode> unknownNodes;
65     private final boolean yin;
66
67     public ExtensionEffectiveStatementImpl(
68             final StmtContext<QName, ExtensionStatement, EffectiveStatement<QName, ExtensionStatement>> ctx) {
69         super(ctx);
70         this.qname = ctx.getStatementArgument();
71         this.schemaPath = ctx.getSchemaPath().get();
72
73         final List<UnknownSchemaNode> unknownNodesInit = new ArrayList<>();
74         for (EffectiveStatement<?, ?> unknownNode : effectiveSubstatements()) {
75             if (unknownNode instanceof UnknownSchemaNode) {
76                 unknownNodesInit.add((UnknownSchemaNode) unknownNode);
77             }
78         }
79         this.unknownNodes = ImmutableList.copyOf(unknownNodesInit);
80
81         // initFields
82         ArgumentEffectiveStatementImpl argumentSubstatement = firstEffective(ArgumentEffectiveStatementImpl.class);
83         if (argumentSubstatement != null) {
84             this.argument = argumentSubstatement.argument().getLocalName();
85
86             YinElementEffectiveStatementImpl yinElement = argumentSubstatement
87                     .firstEffective(YinElementEffectiveStatementImpl.class);
88             if (yinElement != null) {
89                 this.yin = yinElement.argument();
90             } else {
91                 this.yin = false;
92             }
93         } else {
94             this.argument = null;
95             this.yin = false;
96         }
97     }
98
99     @Override
100     public QName getQName() {
101         return qname;
102     }
103
104     @Override
105     public SchemaPath getPath() {
106         return schemaPath;
107     }
108
109     @Override
110     public List<UnknownSchemaNode> getUnknownSchemaNodes() {
111         return unknownNodes;
112     }
113
114     @Override
115     public String getArgument() {
116         return argument;
117     }
118
119     @Override
120     public boolean isYinElement() {
121         return yin;
122     }
123
124     @Override
125     public int hashCode() {
126         final int prime = 31;
127         int result = 1;
128         result = prime * result + Objects.hashCode(qname);
129         result = prime * result + Objects.hashCode(schemaPath);
130         return result;
131     }
132
133     @Override
134     public boolean equals(final Object obj) {
135         if (this == obj) {
136             return true;
137         }
138         if (obj == null) {
139             return false;
140         }
141         if (getClass() != obj.getClass()) {
142             return false;
143         }
144         ExtensionEffectiveStatementImpl other = (ExtensionEffectiveStatementImpl) obj;
145         return Objects.equals(qname, other.qname) && Objects.equals(schemaPath, other.schemaPath);
146     }
147
148     @Override
149     public String toString() {
150         if (TOSTRING_DETECTOR.check(this)) {
151             return recursedToString();
152         }
153
154         TOSTRING_DETECTOR.push(this);
155         try {
156             StringBuilder sb = new StringBuilder(ExtensionEffectiveStatementImpl.class.getSimpleName());
157             sb.append("[");
158             sb.append("argument=").append(argument);
159             sb.append(", qname=").append(qname);
160             sb.append(", schemaPath=").append(schemaPath);
161             sb.append(", extensionSchemaNodes=").append(unknownNodes);
162             sb.append(", yin=").append(yin);
163             sb.append("]");
164             return sb.toString();
165         } finally {
166             TOSTRING_DETECTOR.pop();
167         }
168     }
169
170     private String recursedToString() {
171         return ExtensionEffectiveStatementImpl.class.getSimpleName() + "[" +
172                 "argument=" + argument +
173                 ", qname=" + qname +
174                 ", schemaPath=" + schemaPath +
175                 ", yin=" + yin +
176                 " <RECURSIVE> ]";
177     }
178 }