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 2013-2017 ForgeRock AS.
015 */
016
017package org.forgerock.json.jose.jwt;
018
019import java.util.LinkedHashMap;
020import java.util.List;
021import java.util.Map;
022import java.util.Set;
023
024import org.forgerock.json.JsonValue;
025import org.forgerock.json.jose.exceptions.JwtRuntimeException;
026import org.forgerock.json.jose.utils.Utils;
027
028/**
029 * A base implementation class for a JSON Web object.
030 * <p>
031 * Provides a set of methods which are common across JWT, JWS, JWE and JWK implementations.
032 *
033 * @since 2.0.0
034 */
035public abstract class JWObject {
036
037    private final JsonValue jsonValue;
038
039    /**
040     * Constructs a new, empty JWObject.
041     */
042    public JWObject() {
043        this.jsonValue = new JsonValue(new LinkedHashMap<>());
044    }
045
046    /**
047     * Checks that the given value is of an assignable type from the required class.
048     * <p>
049     * Will throw a JwtRuntimeException if the value is not of the required type
050     *
051     * @param value The value to check is of the required type.
052     * @param requiredClazz The class of the required type.
053     * @see #isValueOfType(Object, Class)
054     */
055    protected void checkValueIsOfType(Object value, Class<?> requiredClazz) {
056        if (!requiredClazz.isAssignableFrom(value.getClass())) {
057            throw new JwtRuntimeException("Value is not of the required type. Required, " + requiredClazz.getName()
058                    + ", actual, " + value.getClass().getName());
059        }
060    }
061
062    /**
063     * Checks that the given List's type is of an assignable type from the required class.
064     * <p>
065     * Will throw a JwtRuntimeException if the value is not of the required type
066     *
067     * @param value The List to check the type is of the required type.
068     * @param requiredClazz The class of the required type.
069     * @see #checkValueIsOfType(Object, Class)
070     */
071    protected void checkListValuesAreOfType(List<?> value, Class<?> requiredClazz) {
072        if (value.size() > 0) {
073            checkValueIsOfType(value.get(0), requiredClazz);
074        }
075    }
076
077    /**
078     * Checks to see if the given value is of an assignable type from the required class.
079     *
080     * @param value The value to check is of the required type.
081     * @param requiredClazz The class of the required type.
082     * @return <code>true</code> if the value if of the required type.
083     * @see #checkValueIsOfType(Object, Class)
084     */
085    protected boolean isValueOfType(Object value, Class<?> requiredClazz) {
086        return requiredClazz.isAssignableFrom(value.getClass());
087    }
088
089    /**
090     * Sets or removes the value of the specified member.
091     * <p>
092     * If the value is not null, then the value is set as the value of the given key.
093     * <p>
094     * Otherwise, if the value is null and the key already exist with a value assigned to it, then the key and its value
095     * will be removed. If the specified key is not defined, calling this method has no effect.
096     *
097     * @param key the {@code Map} key identifying the value to set or to remove.
098     * @param value the object value to assign to the member.
099     */
100    public void put(String key, Object value) {
101        if (value != null) {
102            jsonValue.put(key, value);
103        } else if (jsonValue.isDefined(key)) {
104            jsonValue.remove(key);
105        }
106    }
107
108    /**
109     * Returns the specified item value. If no such member value exists, then a JSON value containing {@code null} is
110     * returned.
111     *
112     * @param key the {@code Map} key identifying the item to return.
113     * @return a JSON value containing the value or {@code null}.
114     */
115    public JsonValue get(String key) {
116        return jsonValue.get(key);
117    }
118
119    /**
120     * Returns {@code true} if this JWObject contains the specified item.
121     *
122     * @param key the {@code Map} key of the item to seek.
123     * @return {@code true} if this JSON value contains the specified member.
124     */
125    public boolean isDefined(String key) {
126        return jsonValue.isDefined(key);
127    }
128
129    /**
130     * Returns the set of keys for this JWObject's values.
131     * <p>
132     * The order of the resulting keys is undefined. If there are no values set, this method returns an empty set.
133     *
134     * @return A Set of keys.
135     */
136    public Set<String> keys() {
137        return jsonValue.keys();
138    }
139
140    /**
141     * Returns the {@code Map} of keys and values stored by {@link #put}.
142     *
143     * @return {@code Map} of this JWObject's keys and values.
144     */
145    Map<String, Object> getAll() {
146        return jsonValue.asMap();
147    }
148
149    /**
150     * Returns a string representation of the JWObject. The result is guaranteed to be valid JSON object syntax.
151     *
152     * @return A JSON String representation.
153     */
154    @Override
155    public String toString() {
156        return Utils.writeJsonObject(jsonValue.asMap());
157    }
158
159    /**
160     * Returns a json representation of the JWObject.
161     *
162     * @return A JSON representation.
163     */
164    public JsonValue toJsonValue() {
165        return jsonValue.copy();
166    }
167}