View Javadoc
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 }