001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2016 ForgeRock AS.
015 * Portions Copyright 2018 Wren Security
016 */
017
018package org.forgerock.api.models;
019
020import static org.forgerock.api.util.ValidationUtil.*;
021import static org.forgerock.util.Reject.*;
022
023import java.util.HashMap;
024import java.util.Map;
025import java.util.Objects;
026import java.util.Set;
027
028import com.fasterxml.jackson.annotation.JsonAnySetter;
029import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
030
031import com.fasterxml.jackson.annotation.JsonIgnore;
032import com.fasterxml.jackson.annotation.JsonValue;
033
034/**
035 * Class that represents API descriptor {@link Schema} definitions.
036 */
037@JsonDeserialize(builder = Definitions.Builder.class)
038public final class Definitions {
039
040    private final Map<String, Schema> definitions;
041
042    private Definitions(Builder builder) {
043        this.definitions = builder.definitions;
044    }
045
046    /**
047     * Gets a {@code Map} of schema-names to {@link Schema}s. This method is currently only used for JSON serialization.
048     *
049     * @return {@code Map} of schema-names to {@link Schema}s.
050     */
051    @JsonValue
052    protected Map<String, Schema> getDefinitions() {
053        return definitions;
054    }
055
056    /**
057     * Gets the {@link Schema} for a given Schema-name.
058     *
059     * @param name Schema name
060     * @return {@link Schema} or {@code null} if does-not-exist.
061     */
062    @JsonIgnore
063    public Schema get(String name) {
064        return definitions.get(name);
065    }
066
067    /**
068     * Returns all {@link Schema} names.
069     *
070     * @return All {@link Schema} names.
071     */
072    @JsonIgnore
073    public Set<String> getNames() {
074        return definitions.keySet();
075    }
076
077    /**
078     * Create a new Builder for Definitions.
079     *
080     * @return Builder
081     */
082    public static Builder definitions() {
083        return new Builder();
084    }
085
086    @Override
087    public boolean equals(Object o) {
088        if (this == o) {
089            return true;
090        }
091        if (o == null || getClass() != o.getClass()) {
092            return false;
093        }
094        Definitions that = (Definitions) o;
095        return Objects.equals(definitions, that.definitions);
096    }
097
098    @Override
099    public int hashCode() {
100        return Objects.hash(definitions);
101    }
102
103    /**
104     * Builder to help construct the Definitions.
105     */
106    public static final class Builder {
107
108        private final Map<String, Schema> definitions = new HashMap<>();
109
110        /**
111         * Private default constructor.
112         */
113        private Builder() {
114        }
115
116        /**
117         * Adds a {@link Schema}.
118         *
119         * @param name Schema name
120         * @param schema {@link Schema}
121         * @return Builder
122         */
123        @JsonAnySetter
124        public Builder put(String name, Schema schema) {
125            if (isEmpty(name) || containsWhitespace(name)) {
126                throw new IllegalArgumentException(
127                    "Schema name is required, must not be blank, and must not contain " +
128                    "whitespace; given: '" + name + "'");
129            }
130            if (definitions.containsKey(name) && !definitions.get(name).equals(schema)) {
131                throw new IllegalStateException(
132                    "Schema name already exists but Schema objects are not equal; " +
133                    "given: '" + name + "'");
134            }
135
136            definitions.put(name, checkNotNull(schema));
137            return this;
138        }
139
140        /**
141         * Builds the Definitions instance.
142         *
143         * @return Definitions instance
144         */
145        public Definitions build() {
146            return new Definitions(this);
147        }
148    }
149
150}