Use switch expressions in QueryParams
[netconf.git] / protocol / restconf-api / src / main / java / org / opendaylight / restconf / api / query / FieldsParam.java
1 /*
2  * Copyright (c) 2021 PANTHEON.tech, s.r.o. 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.restconf.api.query;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.annotations.Beta;
14 import com.google.common.base.MoreObjects;
15 import com.google.common.collect.ImmutableList;
16 import java.net.URI;
17 import java.text.ParseException;
18 import org.eclipse.jdt.annotation.NonNullByDefault;
19 import org.opendaylight.restconf.api.ApiPath.ApiIdentifier;
20 import org.opendaylight.yangtools.concepts.Immutable;
21
22 /**
23  * This class represents a {@code fields} parameter as defined in
24  * <a href="https://datatracker.ietf.org/doc/html/rfc8040#section-4.8.3">RFC8040 section 4.8.3</a>.
25  */
26 @Beta
27 @NonNullByDefault
28 public final class FieldsParam implements RestconfQueryParam<FieldsParam> {
29     /**
30      * A selector for a single node as identified by {@link #path()}. Individual child nodes are subject to further
31      * filtering based on {@link #subSelectors()}.
32      */
33     public static final class NodeSelector implements Immutable {
34         private final ImmutableList<ApiIdentifier> path;
35         private final ImmutableList<NodeSelector> subSelectors;
36
37         NodeSelector(final ImmutableList<ApiIdentifier> path, final ImmutableList<NodeSelector> subSelectors) {
38             this.path = requireNonNull(path);
39             this.subSelectors = requireNonNull(subSelectors);
40             checkArgument(!path.isEmpty(), "At least path segment is required");
41         }
42
43         /**
44          * Return the path to the selected node. Guaranteed to have at least one element.
45          *
46          * @return path to the selected node
47          */
48         public ImmutableList<ApiIdentifier> path() {
49             return path;
50         }
51
52         /**
53          * Selectors for single nodes which should be selected from the node found by interpreting {@link #path}. If
54          * there are no selectors, i.e. {@code subSelectors().isEmpty())}, all child nodes are meant to be selected.
55          *
56          * @return Selectors for nested nodes.
57          */
58         public ImmutableList<NodeSelector> subSelectors() {
59             return subSelectors;
60         }
61
62         @Override
63         public String toString() {
64             final var helper = MoreObjects.toStringHelper(this).add("path", path);
65             if (!subSelectors.isEmpty()) {
66                 helper.add("subSelectors", subSelectors);
67             }
68             return helper.toString();
69         }
70
71         void appendTo(final StringBuilder sb) {
72             final var it = path.iterator();
73             appendStep(sb, it.next());
74             while (it.hasNext()) {
75                 appendStep(sb.append('/'), it.next());
76             }
77
78             if (!subSelectors.isEmpty()) {
79                 appendSelectors(sb.append('('), subSelectors).append(')');
80             }
81         }
82
83         private static void appendStep(final StringBuilder sb, final ApiIdentifier step) {
84             final var mod = step.module();
85             if (mod != null) {
86                 sb.append(mod).append(':');
87             }
88             sb.append(step.identifier().getLocalName());
89         }
90     }
91
92     // API consistency: must not be confused with enum constants
93     @SuppressWarnings("checkstyle:ConstantName")
94     public static final String uriName = "fields";
95     private static final URI CAPABILITY = URI.create("urn:ietf:params:restconf:capability:fields:1.0");
96
97     private final ImmutableList<NodeSelector> nodeSelectors;
98
99     FieldsParam(final ImmutableList<NodeSelector> nodeSelectors) {
100         this.nodeSelectors = requireNonNull(nodeSelectors);
101         checkArgument(!nodeSelectors.isEmpty(), "At least one selector is required");
102     }
103
104     /**
105      * Parse a {@code fields} parameter.
106      *
107      * @param str Unescaped URL string
108      * @return The contents of parameter
109      * @throws ParseException if {@code str} does not represent a valid {@code fields} parameter.
110      */
111     public static FieldsParam parse(final String str) throws ParseException {
112         return new FieldsParameterParser().parse(str);
113     }
114
115     public static FieldsParam forUriValue(final String uriValue) {
116         try {
117             return parse(uriValue);
118         } catch (ParseException e) {
119             throw new IllegalArgumentException(e.getMessage() + " [at offset " + e.getErrorOffset() + "]", e);
120         }
121     }
122
123     @Override
124     public Class<FieldsParam> javaClass() {
125         return FieldsParam.class;
126     }
127
128     @Override
129     public String paramName() {
130         return uriName;
131     }
132
133     public static URI capabilityUri() {
134         return CAPABILITY;
135     }
136
137     /**
138      * Selectors for nodes which should be reported. Guaranteed to have at least one element.
139      *
140      * @return selectors for nodes to be reported
141      */
142     public ImmutableList<NodeSelector> nodeSelectors() {
143         return nodeSelectors;
144     }
145
146     @Override
147     public String paramValue() {
148         return appendSelectors(new StringBuilder(), nodeSelectors).toString();
149     }
150
151     @Override
152     public String toString() {
153         return MoreObjects.toStringHelper(this).add("nodeSelectors", nodeSelectors).toString();
154     }
155
156     private static StringBuilder appendSelectors(final StringBuilder sb, final ImmutableList<NodeSelector> selectors) {
157         final var it = selectors.iterator();
158         it.next().appendTo(sb);
159         while (it.hasNext()) {
160             it.next().appendTo(sb.append(';'));
161         }
162         return sb;
163     }
164 }