Use instanceof expression for yang-xpath-api equality
[yangtools.git] / xpath / yang-xpath-api / src / main / java / org / opendaylight / yangtools / yang / xpath / api / YangLocationPath.java
1 /*
2  * Copyright (c) 2018 Pantheon Technologies, s.r.o.  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.xpath.api;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.annotations.Beta;
13 import com.google.common.base.MoreObjects;
14 import com.google.common.base.MoreObjects.ToStringHelper;
15 import com.google.common.collect.ImmutableList;
16 import com.google.common.collect.ImmutableSet;
17 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
18 import java.io.Serializable;
19 import java.util.Arrays;
20 import java.util.Collection;
21 import java.util.Objects;
22 import java.util.Set;
23 import org.eclipse.jdt.annotation.NonNull;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.opendaylight.yangtools.yang.common.AbstractQName;
26 import org.opendaylight.yangtools.yang.common.QName;
27 import org.opendaylight.yangtools.yang.common.QNameModule;
28 import org.opendaylight.yangtools.yang.common.UnresolvedQName;
29
30 @Beta
31 public abstract sealed class YangLocationPath implements YangExpr {
32     public abstract static sealed class Step implements Serializable, YangPredicateAware {
33         private static final long serialVersionUID = 1L;
34
35         private final YangXPathAxis axis;
36
37         Step(final YangXPathAxis axis) {
38             this.axis = requireNonNull(axis);
39         }
40
41         public final YangXPathAxis getAxis() {
42             return axis;
43         }
44
45         @Override
46         public abstract int hashCode();
47
48         @Override
49         public abstract boolean equals(@Nullable Object obj);
50
51         @Override
52         public final String toString() {
53             return addToStringAttributes(MoreObjects.toStringHelper(Step.class)).toString();
54         }
55
56         protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
57             helper.add("axis", axis);
58             final Set<YangExpr> predicates = getPredicates();
59             if (!predicates.isEmpty()) {
60                 helper.add("predicates", predicates);
61             }
62             return helper;
63         }
64     }
65
66     public static sealed class AxisStep extends Step {
67         private static final long serialVersionUID = 1L;
68
69         AxisStep(final YangXPathAxis axis) {
70             super(axis);
71         }
72
73         @Override
74         public final int hashCode() {
75             return Objects.hash(getAxis(), getPredicates());
76         }
77
78         @Override
79         public final boolean equals(@Nullable final Object obj) {
80             return this == obj || obj instanceof AxisStep other && getAxis().equals(other.getAxis())
81                 && getPredicates().equals(other.getPredicates());
82         }
83
84         @SuppressFBWarnings(value = "SE_PRIVATE_READ_RESOLVE_NOT_INHERITED",
85                 justification = "We have only one subclass, and that does not want to inherit this")
86         private Object readResolve() {
87             return getAxis().asStep();
88         }
89     }
90
91     static final class AxisStepWithPredicates extends AxisStep {
92         private static final long serialVersionUID = 1L;
93
94         private final ImmutableSet<YangExpr> predicates;
95
96         AxisStepWithPredicates(final YangXPathAxis axis, final ImmutableSet<YangExpr> predicates) {
97             super(axis);
98             this.predicates = requireNonNull(predicates);
99         }
100
101         @Override
102         public ImmutableSet<YangExpr> getPredicates() {
103             return predicates;
104         }
105     }
106
107     // match a particular namespace
108     public static final class NamespaceStep extends Step {
109         private static final long serialVersionUID = 1L;
110
111         private final QNameModule namespace;
112
113         NamespaceStep(final YangXPathAxis axis, final QNameModule namespace) {
114             super(axis);
115             this.namespace = requireNonNull(namespace);
116         }
117
118         public QNameModule getNamespace() {
119             return namespace;
120         }
121
122         @Override
123         public int hashCode() {
124             return Objects.hash(getAxis(), namespace, getPredicates());
125         }
126
127         @Override
128         public boolean equals(@Nullable final Object obj) {
129             return this == obj || obj instanceof NamespaceStep other && getAxis().equals(other.getAxis())
130                 && namespace.equals(other.namespace) && getPredicates().equals(other.getPredicates());
131         }
132
133         @Override
134         protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
135             return super.addToStringAttributes(helper).add("namespace", namespace);
136         }
137     }
138
139     /**
140      * A step along an axis. This may be either a {@link ResolvedQNameStep} or a {@link UnresolvedQNameStep}.
141      *
142      * @author Robert Varga
143      */
144     public abstract static sealed class QNameStep extends Step implements QNameReferent {
145         private static final long serialVersionUID = 1L;
146
147         QNameStep(final YangXPathAxis axis) {
148             super(axis);
149         }
150     }
151
152     private abstract static sealed class AbstractQNameStep<T extends AbstractQName> extends QNameStep {
153         private static final long serialVersionUID = 1L;
154
155         private final T qname;
156
157         AbstractQNameStep(final YangXPathAxis axis, final T qname) {
158             super(axis);
159             this.qname = requireNonNull(qname);
160         }
161
162         @Override
163         public final @NonNull T getQName() {
164             return qname;
165         }
166
167         @Override
168         public final int hashCode() {
169             return Objects.hash(getAxis(), qname, getPredicates());
170         }
171
172         @Override
173         @SuppressFBWarnings(value = "EQ_UNUSUAL", justification = "Polymorphic via equalityClass()")
174         public final boolean equals(final @Nullable Object obj) {
175             if (this == obj) {
176                 return true;
177             }
178             final Class<? extends AbstractQNameStep<?>> eq = equalityClass();
179             if (!equalityClass().isInstance(obj)) {
180                 return false;
181             }
182
183             final AbstractQNameStep<?> other = eq.cast(obj);
184             return getAxis().equals(other.getAxis()) && qname.equals(other.qname)
185                     && getPredicates().equals(other.getPredicates());
186         }
187
188         @Override
189         protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
190             return super.addToStringAttributes(helper).add("qname", qname);
191         }
192
193         abstract Class<? extends AbstractQNameStep<?>> equalityClass();
194     }
195
196     public static sealed class ResolvedQNameStep extends AbstractQNameStep<QName> implements ResolvedQNameReferent {
197         private static final long serialVersionUID = 1L;
198
199         ResolvedQNameStep(final YangXPathAxis axis, final QName qname) {
200             super(axis, qname);
201         }
202
203         static ResolvedQNameStep of(final YangXPathAxis axis, final QName qname,
204                 final Collection<YangExpr> predicates) {
205             return predicates.isEmpty() ? new ResolvedQNameStep(axis, qname)
206                     : new ResolvedQNameStepWithPredicates(axis, qname, ImmutableSet.copyOf(predicates));
207         }
208
209         @Override
210         final Class<ResolvedQNameStep> equalityClass() {
211             return ResolvedQNameStep.class;
212         }
213     }
214
215     private static final class ResolvedQNameStepWithPredicates extends ResolvedQNameStep {
216         private static final long serialVersionUID = 1L;
217
218         private final ImmutableSet<YangExpr> predicates;
219
220         ResolvedQNameStepWithPredicates(final YangXPathAxis axis, final QName qname,
221                 final ImmutableSet<YangExpr> predicates) {
222             super(axis, qname);
223             this.predicates = requireNonNull(predicates);
224         }
225
226         @Override
227         public ImmutableSet<YangExpr> getPredicates() {
228             return predicates;
229         }
230     }
231
232     public static sealed class UnresolvedQNameStep extends AbstractQNameStep<UnresolvedQName>
233             implements UnresolvedQNameReferent {
234         private static final long serialVersionUID = 1L;
235
236         UnresolvedQNameStep(final YangXPathAxis axis, final UnresolvedQName qname) {
237             super(axis, qname);
238         }
239
240         static UnresolvedQNameStep of(final YangXPathAxis axis, final UnresolvedQName qname,
241                 final Collection<YangExpr> predicates) {
242             return predicates.isEmpty() ? new UnresolvedQNameStep(axis, qname)
243                     : new UnresolvedQNameStepWithPredicates(axis, qname, ImmutableSet.copyOf(predicates));
244         }
245
246         @Override
247         final Class<UnresolvedQNameStep> equalityClass() {
248             return UnresolvedQNameStep.class;
249         }
250     }
251
252     private static final class UnresolvedQNameStepWithPredicates extends UnresolvedQNameStep {
253         private static final long serialVersionUID = 1L;
254
255         private final ImmutableSet<YangExpr> predicates;
256
257         UnresolvedQNameStepWithPredicates(final YangXPathAxis axis, final UnresolvedQName qname,
258                 final ImmutableSet<YangExpr> predicates) {
259             super(axis, qname);
260             this.predicates = requireNonNull(predicates);
261         }
262
263         @Override
264         public ImmutableSet<YangExpr> getPredicates() {
265             return predicates;
266         }
267     }
268
269     public static sealed class NodeTypeStep extends Step {
270         private static final long serialVersionUID = 1L;
271
272         private final YangXPathNodeType nodeType;
273
274         NodeTypeStep(final YangXPathAxis axis, final YangXPathNodeType nodeType) {
275             super(axis);
276             this.nodeType = requireNonNull(nodeType);
277         }
278
279         public final YangXPathNodeType getNodeType() {
280             return nodeType;
281         }
282
283         @Override
284         public int hashCode() {
285             return Objects.hash(getAxis(), nodeType, getPredicates());
286         }
287
288         @Override
289         public boolean equals(@Nullable final Object obj) {
290             if (this == obj) {
291                 return true;
292             }
293             if (obj == null || !getClass().equals(obj.getClass())) {
294                 return false;
295             }
296             final NodeTypeStep other = (NodeTypeStep) obj;
297             return nodeType.equals(other.nodeType) && getAxis().equals(other.getAxis())
298                     && getPredicates().equals(other.getPredicates());
299         }
300
301         @Override
302         protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
303             return super.addToStringAttributes(helper).add("nodeType", nodeType);
304         }
305     }
306
307     @SuppressFBWarnings(value = "EQ_DOESNT_OVERRIDE_EQUALS",
308             justification = "https://github.com/spotbugs/spotbugs/issues/511")
309     static final class NodeTypeStepWithPredicates extends NodeTypeStep {
310         private static final long serialVersionUID = 1L;
311
312         private final ImmutableSet<YangExpr> predicates;
313
314         NodeTypeStepWithPredicates(final YangXPathAxis axis, final YangXPathNodeType type,
315                 final ImmutableSet<YangExpr> predicates) {
316             super(axis, type);
317             this.predicates = requireNonNull(predicates);
318         }
319
320         @Override
321         public ImmutableSet<YangExpr> getPredicates() {
322             return predicates;
323         }
324     }
325
326     public static sealed class ProcessingInstructionStep extends NodeTypeStep {
327         private static final long serialVersionUID = 1L;
328
329         private final String name;
330
331         ProcessingInstructionStep(final YangXPathAxis axis, final String name) {
332             super(axis, YangXPathNodeType.PROCESSING_INSTRUCTION);
333             this.name = requireNonNull(name);
334         }
335
336         public final String getName() {
337             return name;
338         }
339
340         @Override
341         public final int hashCode() {
342             return Objects.hash(getAxis(), getNodeType(), name, getPredicates());
343         }
344
345         @Override
346         public final boolean equals(final @Nullable Object obj) {
347             return obj == this || super.equals(obj) && name.equals(((ProcessingInstructionStep) obj).name);
348         }
349
350         @Override
351         protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
352             return super.addToStringAttributes(helper).add("name", name);
353         }
354     }
355
356     static final class ProcessingInstructionStepWithPredicates extends ProcessingInstructionStep {
357         private static final long serialVersionUID = 1L;
358
359         private final ImmutableSet<YangExpr> predicates;
360
361         ProcessingInstructionStepWithPredicates(final YangXPathAxis axis, final String name,
362                 final ImmutableSet<YangExpr> predicates) {
363             super(axis, name);
364             this.predicates = requireNonNull(predicates);
365         }
366
367         @Override
368         public ImmutableSet<YangExpr> getPredicates() {
369             return predicates;
370         }
371     }
372
373     public static final class Absolute extends YangLocationPath {
374         private static final long serialVersionUID = 1L;
375
376         Absolute(final ImmutableList<Step> steps) {
377             super(steps);
378         }
379
380         @Override
381         public boolean isAbsolute() {
382             return true;
383         }
384     }
385
386     public static final class Relative extends YangLocationPath {
387         private static final long serialVersionUID = 1L;
388
389         Relative(final ImmutableList<Step> steps) {
390             super(steps);
391         }
392
393         @Override
394         public boolean isAbsolute() {
395             return false;
396         }
397     }
398
399     private static final long serialVersionUID = 1L;
400     private static final Absolute ROOT = new Absolute(ImmutableList.of());
401     private static final Relative SELF = new Relative(ImmutableList.of());
402
403     private final ImmutableList<Step> steps;
404
405     private YangLocationPath(final ImmutableList<Step> steps) {
406         this.steps = requireNonNull(steps);
407     }
408
409     public static final Absolute absolute(final Step... steps) {
410         return absolute(Arrays.asList(steps));
411     }
412
413     public static final Absolute absolute(final Collection<Step> steps) {
414         return steps.isEmpty() ? ROOT : new Absolute(ImmutableList.copyOf(steps));
415     }
416
417     public static final Relative relative(final Step... steps) {
418         return relative(Arrays.asList(steps));
419     }
420
421     public static final Relative relative(final Collection<Step> steps) {
422         return steps.isEmpty() ? SELF : new Relative(ImmutableList.copyOf(steps));
423     }
424
425     /**
426      * The conceptual {@code root} {@link YangLocationPath}. This path is an absolute path and has no steps.
427      *
428      * @return Empty absolute {@link YangLocationPath}
429      */
430     public static final Absolute root() {
431         return ROOT;
432     }
433
434     /**
435      * The conceptual {@code same} {@link YangLocationPath}. This path is a relative path and has no steps and is
436      * equivalent to a step along {@link YangXPathAxis#SELF}.
437      *
438      * @return Empty relative {@link YangLocationPath}
439      */
440     public static final Relative self() {
441         return SELF;
442     }
443
444     public final ImmutableList<Step> getSteps() {
445         return steps;
446     }
447
448     public abstract boolean isAbsolute();
449
450     @Override
451     public final int hashCode() {
452         return Boolean.hashCode(isAbsolute()) * 31 + steps.hashCode();
453     }
454
455     @Override
456     public final boolean equals(final @Nullable Object obj) {
457         return this == obj || obj instanceof YangLocationPath other && isAbsolute() == other.isAbsolute()
458             && steps.equals(other.steps);
459     }
460
461     @Override
462     public final String toString() {
463         final ToStringHelper helper = MoreObjects.toStringHelper(YangLocationPath.class).add("absolute", isAbsolute());
464         if (!steps.isEmpty()) {
465             helper.add("steps", steps);
466         }
467         return helper.toString();
468     }
469
470     final Object readSolve() {
471         return steps.isEmpty() ? isAbsolute() ? ROOT : SELF : this;
472     }
473 }