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