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 java.util.HashMap;
20 import java.util.Map;
21 import java.util.Objects;
22 import java.util.Set;
23
24 import com.fasterxml.jackson.annotation.JsonAnySetter;
25 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
26 import org.forgerock.api.ApiValidationException;
27 import org.forgerock.http.routing.Version;
28 import org.forgerock.util.Reject;
29
30 import com.fasterxml.jackson.annotation.JsonIgnore;
31 import com.fasterxml.jackson.annotation.JsonValue;
32
33 /**
34 * Class that represents versioned {@link Resource}s on an API descriptor path.
35 */
36 @JsonDeserialize(builder = VersionedPath.Builder.class)
37 public final class VersionedPath {
38
39 /**
40 * Version {@code 0.0} represents null/empty, for when resource versions are not required by an API (e.g., OpenIDM).
41 */
42 public static final Version UNVERSIONED = Version.version(0);
43
44 private final Map<Version, Resource> paths;
45
46 private VersionedPath(Builder builder) {
47 this.paths = builder.paths;
48
49 if (paths.isEmpty()) {
50 throw new ApiValidationException("Must have at least one versioned resource");
51 }
52 if (paths.size() > 1) {
53 for (final Version version : paths.keySet()) {
54 if (UNVERSIONED.equals(version)) {
55 throw new ApiValidationException("Version 0.0 (unversioned) must be the only version when used");
56 }
57 }
58 }
59 }
60
61 /**
62 * Gets a {@code Map} of versions to {@link Resource}s. This method is currently only used for JSON serialization.
63 *
64 * @return {@code Map} of versions to {@link Resource}s.
65 */
66 @JsonValue
67 protected Map<Version, Resource> getPaths() {
68 return paths;
69 }
70
71 /**
72 * Gets the {@link Resource} for a given version.
73 *
74 * @param version Resource version
75 * @return {@link Resource} or {@code null} if does-not-exist.
76 */
77 @JsonIgnore
78 public Resource get(Version version) {
79 return paths.get(version);
80 }
81
82 /**
83 * Returns all resource-versions on this path.
84 *
85 * @return All resource-versions.
86 */
87 @JsonIgnore
88 public Set<Version> getVersions() {
89 return paths.keySet();
90 }
91
92 /**
93 * Create a new Builder for VersionedPath.
94 *
95 * @return Builder
96 */
97 public static Builder versionedPath() {
98 return new Builder();
99 }
100
101 /**
102 * Allows for mutation of paths when merging {@code Paths} instances.
103 * @param v The version.
104 * @param resource The resource.
105 */
106 void addVersion(Version v, Resource resource) {
107 if (paths.containsKey(v)) {
108 throw new IllegalArgumentException("Trying to redefine version: " + v);
109 }
110 paths.put(v, Reject.checkNotNull(resource));
111 }
112
113 @Override
114 public boolean equals(Object o) {
115 if (this == o) {
116 return true;
117 }
118 if (o == null || getClass() != o.getClass()) {
119 return false;
120 }
121 VersionedPath that = (VersionedPath) o;
122 return Objects.equals(paths, that.paths);
123 }
124
125 @Override
126 public int hashCode() {
127 return Objects.hash(paths);
128 }
129
130 /**
131 * Builder to help construct the VersionedPath.
132 */
133 public static final class Builder {
134
135 private final Map<Version, Resource> paths = new HashMap<>();
136
137 /**
138 * Private default constructor.
139 */
140 private Builder() {
141 }
142
143 /**
144 * Adds a resource-version.
145 *
146 * @param version Resource-version
147 * @param resource {@link Resource}
148 * @return Builder
149 */
150 public Builder put(Version version, Resource resource) {
151 if (paths.containsKey(version)) {
152 throw new IllegalStateException("version not unique");
153 }
154 paths.put(Reject.checkNotNull(version), Reject.checkNotNull(resource));
155 return this;
156 }
157
158 /**
159 * Adds a resource-version.
160 *
161 * @param version Resource-version as string
162 * @param resource {@link Resource}
163 * @return Builder
164 */
165 @JsonAnySetter
166 public Builder put(String version, Resource resource) {
167 return put(Version.version(version), resource);
168 }
169
170 /**
171 * Builds the VersionedPath instance.
172 *
173 * @return VersionedPath instance
174 */
175 public VersionedPath build() {
176 return new VersionedPath(this);
177 }
178 }
179
180 }