Rework body formatting wiring
[netconf.git] / protocol / restconf-api / src / main / java / org / opendaylight / restconf / api / QueryParameters.java
1 /*
2  * Copyright (c) 2024 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;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.collect.ImmutableMap;
13 import com.google.common.collect.Maps;
14 import java.util.Arrays;
15 import java.util.Collection;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Map.Entry;
19 import java.util.function.Function;
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.opendaylight.restconf.api.query.RestconfQueryParam;
23 import org.opendaylight.yangtools.concepts.Immutable;
24
25 /**
26  * Query parameters of a RESTCONF request URI. Individual parameters can be looked up by
27  * {@link RestconfQueryParam#paramName()} via {@link #lookup(String)}. All parameters are accessible via
28  * {@link #asCollection()}, where each {@link RestconfQueryParam#paramName()} is guaranteed to be encountered at most
29  * once.
30  */
31 @NonNullByDefault
32 public final class QueryParameters implements Immutable {
33     static final QueryParameters EMPTY = new QueryParameters(ImmutableMap.of());
34
35     private final ImmutableMap<String, String> params;
36
37     private QueryParameters(final ImmutableMap<String, String> params) {
38         this.params = requireNonNull(params);
39     }
40
41     private QueryParameters(final Collection<RestconfQueryParam<?>> params) {
42         // TODO: consider caching common request parameter combinations
43         this(params.stream()
44             .collect(ImmutableMap.toImmutableMap(RestconfQueryParam::paramName, RestconfQueryParam::paramValue)));
45     }
46
47     public static QueryParameters of() {
48         return EMPTY;
49     }
50
51     public static QueryParameters of(final String paramName, final String paramValue) {
52         return new QueryParameters(ImmutableMap.of(paramName, paramValue));
53     }
54
55     public static QueryParameters of(final Entry<String, String> entry) {
56         return of(entry.getKey(), entry.getValue());
57     }
58
59     public static QueryParameters of(final RestconfQueryParam<?> param) {
60         return of(param.paramName(), param.paramValue());
61     }
62
63     public static QueryParameters of(final RestconfQueryParam<?>... params) {
64         return switch (params.length) {
65             case 0 -> of();
66             case 1 -> of(params[0]);
67             default -> new QueryParameters(Arrays.asList(params));
68         };
69     }
70
71     public static QueryParameters of(final Collection<RestconfQueryParam<?>> params) {
72         return params instanceof List ? of((List<RestconfQueryParam<?>>) params) : switch (params.size()) {
73             case 0 -> of();
74             case 1 -> of(params.iterator().next());
75             default -> new QueryParameters(params);
76         };
77     }
78
79     public static QueryParameters of(final List<RestconfQueryParam<?>> params) {
80         return switch (params.size()) {
81             case 0 -> of();
82             case 1 -> of(params.get(0));
83             default -> new QueryParameters(params);
84         };
85     }
86
87     public static QueryParameters of(final Map<String, String> params) {
88         return params.isEmpty() ? of() : new QueryParameters(ImmutableMap.copyOf(params));
89     }
90
91     /**
92      * Normalize query parameters from an map containing zero or more values for each parameter value, such as coming
93      * from JAX-RS's {@code UriInfo}.
94      *
95      * @param multiParams Input map
96      * @return A {@link QueryParameters} instance
97      * @throws NullPointerException if {@code uriInfo} is {@code null}
98      * @throws IllegalArgumentException if there are multiple values for a parameter
99      */
100     public static QueryParameters ofMultiValue(final Map<String, List<String>> multiParams) {
101         if (multiParams.isEmpty()) {
102             return of();
103         }
104
105         final var builder = ImmutableMap.<String, String>builder();
106         for (var entry : multiParams.entrySet()) {
107             final var values = entry.getValue();
108             switch (values.size()) {
109                 case 0 -> {
110                     // No-op
111                 }
112                 case 1 -> builder.put(entry.getKey(), values.get(0));
113                 default -> throw new IllegalArgumentException(
114                     "Parameter " + entry.getKey() + " can appear at most once in request URI");
115             }
116         }
117
118         final var params = builder.build();
119         return params.isEmpty() ? of() : new QueryParameters(params);
120     }
121
122     public boolean isEmpty() {
123         return params.isEmpty();
124     }
125
126     public Collection<Entry<String, String>> asCollection() {
127         return params.entrySet();
128     }
129
130     public @Nullable String lookup(final String paramName) {
131         return params.get(requireNonNull(paramName));
132     }
133
134     public <T> @Nullable T lookup(final String paramName, final Function<String, T> parseFunction) {
135         final var str = lookup(paramName);
136         if (str != null) {
137             return parseFunction.apply(str);
138         }
139         return null;
140     }
141
142     public QueryParameters withoutParam(final String paramName) {
143         return params.containsKey(paramName) ? of(Maps.filterKeys(params, key -> !key.equals(paramName))) : this;
144     }
145
146     @Override
147     public String toString() {
148         return QueryParameters.class.getSimpleName() + "(" + params + ")";
149     }
150
151 }