1 /*
2 * The contents of this file are subject to the terms of the Common Development and
3 * Distribution License (the License). You may not use this file except in compliance with the
4 * License.
5 *
6 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
7 * specific language governing permission and limitations under the License.
8 *
9 * When distributing Covered Software, include this CDDL Header Notice in each file and include
10 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
11 * Header, with the fields enclosed by brackets [] replaced by your own identifying
12 * information: "Portions copyright [year] [name of copyright owner]".
13 *
14 * Copyright 2016 ForgeRock AS.
15 */
16
17 package org.forgerock.api.models;
18
19 import static org.forgerock.api.util.ValidationUtil.*;
20
21 import java.util.HashMap;
22 import java.util.Map;
23 import java.util.Objects;
24 import java.util.Set;
25
26 import com.fasterxml.jackson.annotation.JsonAnySetter;
27 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
28 import org.forgerock.api.util.PathUtil;
29 import org.forgerock.http.routing.Version;
30 import org.forgerock.util.Reject;
31
32 import com.fasterxml.jackson.annotation.JsonIgnore;
33 import com.fasterxml.jackson.annotation.JsonValue;
34
35 /**
36 * Class that represents the Paths type in API descriptor.
37 */
38 @JsonDeserialize(builder = Paths.Builder.class)
39 public final class Paths {
40
41 private final Map<String, VersionedPath> paths;
42
43 private Paths(Builder builder) {
44 this.paths = builder.paths;
45 }
46
47 /**
48 * Gets a {@code Map} of path-names to Paths. This method is currently only used for JSON serialization.
49 *
50 * @return {@code Map} of path-names to Paths.
51 */
52 @JsonValue
53 protected Map<String, VersionedPath> getPaths() {
54 return paths;
55 }
56
57 /**
58 * Gets the Path for a given Path-name.
59 *
60 * @param name Path name
61 * @return Path or {@code null} if does-not-exist.
62 */
63 @JsonIgnore
64 public VersionedPath get(String name) {
65 return paths.get(name);
66 }
67
68 /**
69 * Returns all Path names.
70 *
71 * @return All Path names.
72 */
73 @JsonIgnore
74 public Set<String> getNames() {
75 return paths.keySet();
76 }
77
78 /**
79 * Create a new Builder for Paths.
80 *
81 * @return Builder
82 */
83 public static Builder paths() {
84 return new Builder();
85 }
86
87 @Override
88 public boolean equals(Object o) {
89 if (this == o) {
90 return true;
91 }
92 if (o == null || getClass() != o.getClass()) {
93 return false;
94 }
95 Paths paths1 = (Paths) o;
96 return Objects.equals(paths, paths1.paths);
97 }
98
99 @Override
100 public int hashCode() {
101 return Objects.hash(paths);
102 }
103
104 /**
105 * Builder to help construct the Paths.
106 */
107 public static final class Builder {
108
109 private final Map<String, VersionedPath> paths = new HashMap<>();
110
111 /**
112 * Private default constructor.
113 */
114 private Builder() {
115 }
116
117 /**
118 * Adds a Path.
119 *
120 * @param path Path string
121 * @param versionedPath Versioned path
122 * @return Builder
123 */
124 @JsonAnySetter
125 public Builder put(String path, VersionedPath versionedPath) {
126 if (path == null || containsWhitespace(path)) {
127 throw new IllegalArgumentException("path required and may not contain whitespace");
128 }
129 if (!path.isEmpty()) {
130 // paths must start with a slash (OpenAPI spec) and not end with one
131 path = PathUtil.buildPath(path);
132 }
133 if (paths.containsKey(path)) {
134 throw new IllegalStateException("path not unique");
135 }
136 paths.put(path, Reject.checkNotNull(versionedPath));
137 return this;
138 }
139
140 /**
141 * Merge the path definition into the existing path definitions. If there is already a {@code VersionedPath}
142 * at this path, then the versions will be added together.
143 *
144 * @param path Path string
145 * @param versionedPath Versioned path
146 * @return Builder.
147 */
148 public Builder merge(String path, VersionedPath versionedPath) {
149 if (path == null || containsWhitespace(path)) {
150 throw new IllegalArgumentException("path required and may not contain whitespace");
151 }
152 if (!path.isEmpty()) {
153 // paths must start with a slash (OpenAPI spec) and not end with one
154 path = PathUtil.buildPath(path);
155 }
156 if (!paths.containsKey(path)) {
157 put(path, Reject.checkNotNull(versionedPath));
158 } else {
159 VersionedPath existing = paths.get(path);
160 for (Version v : versionedPath.getVersions()) {
161 existing.addVersion(v, versionedPath.get(v));
162 }
163 }
164 return this;
165 }
166
167 /**
168 * Builds the Paths instance.
169 *
170 * @return Paths instance
171 */
172 public Paths build() {
173 return new Paths(this);
174 }
175 }
176
177 }