Merge "Bug 3051: Fixed pattern checks in generated DTOs"
[yangtools.git] / yang / yang-data-impl / src / main / java / org / opendaylight / yangtools / yang / data / impl / leafref / LeafRefPath.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.data.impl.leafref;
9
10 import org.opendaylight.yangtools.concepts.Immutable;
11 import com.google.common.base.Preconditions;
12 import com.google.common.collect.ImmutableList;
13 import com.google.common.collect.Iterables;
14 import java.util.Arrays;
15 import java.util.Iterator;
16 import java.util.List;
17 import java.util.NoSuchElementException;
18 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
19
20 public abstract class LeafRefPath implements Immutable {
21
22     /**
23      * An absolute LeafRefPath.
24      */
25     private static final class AbsoluteLeafRefPath extends LeafRefPath {
26         private AbsoluteLeafRefPath(final LeafRefPath parent,
27                 final QNameWithPredicate qname) {
28             super(parent, qname);
29         }
30
31         @Override
32         public boolean isAbsolute() {
33             return true;
34         }
35
36         @Override
37         protected LeafRefPath createInstance(final LeafRefPath parent,
38                 final QNameWithPredicate qname) {
39             return new AbsoluteLeafRefPath(parent, qname);
40         }
41     }
42
43     /**
44      * A relative LeafRefPath.
45      */
46     private static final class RelativeLeafRefPath extends LeafRefPath {
47         private RelativeLeafRefPath(final LeafRefPath parent,
48                 final QNameWithPredicate qname) {
49             super(parent, qname);
50         }
51
52         @Override
53         public boolean isAbsolute() {
54             return false;
55         }
56
57         @Override
58         protected LeafRefPath createInstance(final LeafRefPath parent,
59                 final QNameWithPredicate qname) {
60             return new RelativeLeafRefPath(parent, qname);
61         }
62     }
63
64     @SuppressWarnings("rawtypes")
65     private static final AtomicReferenceFieldUpdater<LeafRefPath, ImmutableList> LEGACYPATH_UPDATER = AtomicReferenceFieldUpdater
66             .newUpdater(LeafRefPath.class, ImmutableList.class, "legacyPath");
67
68     /**
69      * Shared instance of the conceptual root schema node.
70      */
71     public static final LeafRefPath ROOT = new AbsoluteLeafRefPath(null, null);
72
73     /**
74      * Shared instance of the "same" relative schema node.
75      */
76     public static final LeafRefPath SAME = new RelativeLeafRefPath(null, null);
77
78     /**
79      * Parent path.
80      */
81     private final LeafRefPath parent;
82
83     /**
84      * This component.
85      */
86     private final QNameWithPredicate qname;
87
88     /**
89      * Cached hash code. We can use this since we are immutable.
90      */
91     private final int hash;
92
93     /**
94      * Cached legacy path, filled-in when {@link #getPath()} or
95      * {@link #getPathTowardsRoot()} is invoked.
96      */
97     private volatile ImmutableList<QNameWithPredicate> legacyPath;
98
99     private ImmutableList<QNameWithPredicate> getLegacyPath() {
100         ImmutableList<QNameWithPredicate> ret = legacyPath;
101         if (ret == null) {
102             ret = ImmutableList.copyOf(getPathTowardsRoot()).reverse();
103             LEGACYPATH_UPDATER.lazySet(this, ret);
104         }
105
106         return ret;
107     }
108
109     /**
110      * Returns the complete path to schema node.
111      *
112      * @return list of <code>QNameWithPredicate</code> instances which
113      *         represents complete path to schema node
114      *
115      * @deprecated Use {@link #getPathFromRoot()} instead.
116      */
117     @Deprecated
118     public List<QNameWithPredicate> getPath() {
119         return getLegacyPath();
120     }
121
122     protected LeafRefPath(final LeafRefPath parent,
123             final QNameWithPredicate qname) {
124         this.parent = parent;
125         this.qname = qname;
126
127         int h = parent == null ? 0 : parent.hashCode();
128         if (qname != null) {
129             h = h * 31 + qname.hashCode();
130         }
131
132         hash = h;
133     }
134
135     /**
136      * Constructs new instance of this class with the concrete path.
137      *
138      * @param path
139      *            list of QNameWithPredicate instances which specifies exact
140      *            path to the module node
141      * @param absolute
142      *            boolean value which specifies if the path is absolute or
143      *            relative
144      *
145      * @return A LeafRefPath instance.
146      */
147     public static LeafRefPath create(final Iterable<QNameWithPredicate> path,
148             final boolean absolute) {
149         final LeafRefPath parent = absolute ? ROOT : SAME;
150         return parent.createChild(path);
151     }
152
153     /**
154      * Constructs new instance of this class with the concrete path.
155      *
156      * @param absolute
157      *            boolean value which specifies if the path is absolute or
158      *            relative
159      * @param path
160      *            one or more QNameWithPredicate instances which specifies exact
161      *            path to the module node
162      *
163      * @return A LeafRefPath instance.
164      */
165     public static LeafRefPath create(final boolean absolute,
166             final QNameWithPredicate... path) {
167         return create(Arrays.asList(path), absolute);
168     }
169
170     /**
171      * Create a new instance.
172      *
173      * @param parent
174      *            Parent LeafRefPath
175      * @param qname
176      *            next path element
177      * @return A new LeafRefPath instance
178      */
179     protected abstract LeafRefPath createInstance(LeafRefPath parent,
180             QNameWithPredicate qname);
181
182     /**
183      * Create a child path based on concatenation of this path and a relative
184      * path.
185      *
186      * @param relative
187      *            Relative path
188      * @return A new child path
189      */
190     public LeafRefPath createChild(final Iterable<QNameWithPredicate> relative) {
191         if (Iterables.isEmpty(relative)) {
192             return this;
193         }
194
195         LeafRefPath parent = this;
196         for (QNameWithPredicate qname : relative) {
197             parent = parent.createInstance(parent, qname);
198         }
199
200         return parent;
201     }
202
203     /**
204      * Create a child path based on concatenation of this path and a relative
205      * path.
206      *
207      * @param relative
208      *            Relative LeafRefPath
209      * @return A new child path
210      */
211     public LeafRefPath createChild(final LeafRefPath relative) {
212         Preconditions.checkArgument(!relative.isAbsolute(),
213                 "Child creation requires relative path");
214
215         LeafRefPath parent = this;
216         for (QNameWithPredicate qname : relative.getPathFromRoot()) {
217             parent = parent.createInstance(parent, qname);
218         }
219
220         return parent;
221     }
222
223     /**
224      * Create a child path based on concatenation of this path and additional
225      * path elements.
226      *
227      * @param elements
228      *            Relative LeafRefPath elements
229      * @return A new child path
230      */
231     public LeafRefPath createChild(final QNameWithPredicate... elements) {
232         return createChild(Arrays.asList(elements));
233     }
234
235     /**
236      * Returns the list of nodes which need to be traversed to get from the
237      * starting point (root for absolute LeafRefPaths) to the node represented
238      * by this object.
239      *
240      * @return list of <code>qname</code> instances which represents path from
241      *         the root to the schema node.
242      */
243     public Iterable<QNameWithPredicate> getPathFromRoot() {
244         return getLegacyPath();
245     }
246
247     /**
248      * Returns the list of nodes which need to be traversed to get from this
249      * node to the starting point (root for absolute LeafRefPaths).
250      *
251      * @return list of <code>qname</code> instances which represents path from
252      *         the schema node towards the root.
253      */
254     public Iterable<QNameWithPredicate> getPathTowardsRoot() {
255         return new Iterable<QNameWithPredicate>() {
256             @Override
257             public Iterator<QNameWithPredicate> iterator() {
258                 return new Iterator<QNameWithPredicate>() {
259                     private LeafRefPath current = LeafRefPath.this;
260
261                     @Override
262                     public boolean hasNext() {
263                         return current.parent != null;
264                     }
265
266                     @Override
267                     public QNameWithPredicate next() {
268                         if (current.parent != null) {
269                             final QNameWithPredicate ret = current.qname;
270                             current = current.parent;
271                             return ret;
272                         } else {
273                             throw new NoSuchElementException(
274                                     "No more elements available");
275                         }
276                     }
277
278                     @Override
279                     public void remove() {
280                         throw new UnsupportedOperationException(
281                                 "Component removal not supported");
282                     }
283                 };
284             }
285         };
286     }
287
288     /**
289      * Returns the immediate parent LeafRefPath.
290      *
291      * @return Parent path, null if this LeafRefPath is already toplevel.
292      */
293     public LeafRefPath getParent() {
294         return parent;
295     }
296
297     /**
298      * Get the last component of this path.
299      *
300      * @return The last component of this path.
301      */
302     public final QNameWithPredicate getLastComponent() {
303         return qname;
304     }
305
306     /**
307      * Describes whether schema path is|isn't absolute.
308      *
309      * @return boolean value which is <code>true</code> if schema path is
310      *         absolute.
311      */
312     public abstract boolean isAbsolute();
313
314     @Override
315     public final int hashCode() {
316         return hash;
317     }
318
319     @Override
320     public boolean equals(final Object obj) {
321         if (this == obj) {
322             return true;
323         }
324         if (obj == null) {
325             return false;
326         }
327         if (getClass() != obj.getClass()) {
328             return false;
329         }
330         final LeafRefPath other = (LeafRefPath) obj;
331
332         if (qname != null) {
333             if (!qname.equals(other.qname)) {
334                 return false;
335             }
336         } else {
337             if (other.qname != null) {
338                 return false;
339             }
340         }
341
342         if (parent == null) {
343             return other.parent == null;
344         }
345         return parent.equals(other.parent);
346     }
347
348     @Override
349     public String toString() {
350         StringBuilder sb = new StringBuilder();
351
352         Iterable<QNameWithPredicate> pathFromRoot = this.getPathFromRoot();
353
354         sb.append(isAbsolute() ? "Absolute path:" : "Relative path:");
355
356         for (QNameWithPredicate qName : pathFromRoot) {
357             sb.append("/" + qName);
358         }
359
360         return sb.toString();
361
362     }
363
364     // @Override
365     // public final String toString() {
366     // return addToStringAttributes(Objects.toStringHelper(this)).toString();
367     // }
368     //
369     // protected ToStringHelper addToStringAttributes(final ToStringHelper
370     // toStringHelper) {
371     // return toStringHelper.add("path", getPathFromRoot());
372     // }
373
374 }