io.jsonwebtoken.impl.ParameterMap Maven / Gradle / Ivy
/*
* Copyright (C) 2014 jsonwebtoken.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.jsonwebtoken.impl;
import io.jsonwebtoken.impl.lang.Nameable;
import io.jsonwebtoken.impl.lang.Parameter;
import io.jsonwebtoken.impl.lang.ParameterReadable;
import io.jsonwebtoken.impl.lang.Parameters;
import io.jsonwebtoken.impl.lang.RedactedSupplier;
import io.jsonwebtoken.lang.Assert;
import io.jsonwebtoken.lang.Collections;
import io.jsonwebtoken.lang.Objects;
import io.jsonwebtoken.lang.Registry;
import io.jsonwebtoken.lang.Strings;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
public class ParameterMap implements Map, ParameterReadable, Nameable {
protected final Registry> PARAMS;
protected final Map values; // canonical values formatted per RFC requirements
protected final Map idiomaticValues; // the values map with any RFC values converted to Java type-safe values where possible
private final boolean initialized;
private final boolean mutable;
public ParameterMap(Set> params) {
this(Parameters.registry(params));
}
public ParameterMap(Registry> registry) { // mutable constructor
this(registry, null, true);
}
/**
* Copy constructor producing an immutable instance.
*
* @param registry registry of idiomatic parameters relevant for the map values
* @param values map values
*/
public ParameterMap(Registry> registry, Map values) {
this(registry, Assert.notNull(values, "Map argument cannot be null."), false);
}
public ParameterMap(Registry> registry, Map values, boolean mutable) {
Assert.notNull(registry, "Parameter registry cannot be null.");
Assert.notEmpty(registry.values(), "Parameter registry cannot be empty.");
this.PARAMS = registry;
this.values = new LinkedHashMap<>();
this.idiomaticValues = new LinkedHashMap<>();
if (!Collections.isEmpty(values)) {
putAll(values);
}
this.mutable = mutable;
this.initialized = true;
}
private void assertMutable() {
if (initialized && !mutable) {
String msg = getName() + " instance is immutable and may not be modified.";
throw new UnsupportedOperationException(msg);
}
}
protected ParameterMap replace(Parameter> param) {
Registry> registry = Parameters.replace(this.PARAMS, param);
return new ParameterMap(registry, this, this.mutable);
}
@Override
public String getName() {
return "Map";
}
@Override
public T get(Parameter param) {
Assert.notNull(param, "Parameter cannot be null.");
final String id = Assert.hasText(param.getId(), "Parameter id cannot be null or empty.");
Object value = idiomaticValues.get(id);
return param.cast(value);
}
@Override
public int size() {
return values.size();
}
@Override
public boolean isEmpty() {
return values.isEmpty();
}
@Override
public boolean containsKey(Object o) {
return values.containsKey(o);
}
@Override
public boolean containsValue(Object o) {
return values.containsValue(o);
}
@Override
public Object get(Object o) {
return values.get(o);
}
/**
* Convenience method to put a value for an idiomatic param.
*
* @param param the param representing the property name to set
* @param value the value to set
* @return the previous value for the param, or {@code null} if there was no previous value
* @since 0.12.0
*/
protected final Object put(Parameter param, Object value) {
assertMutable();
Assert.notNull(param, "Parameter cannot be null.");
Assert.hasText(param.getId(), "Parameter id cannot be null or empty.");
return apply(param, value);
}
@Override
public final Object put(String name, Object value) {
assertMutable();
name = Assert.notNull(Strings.clean(name), "Member name cannot be null or empty.");
Parameter> param = PARAMS.get(name);
if (param != null) {
// standard property, represent it idiomatically:
return put(param, value);
} else {
// non-standard or custom property, just apply directly:
return nullSafePut(name, value);
}
}
private Object nullSafePut(String name, Object value) {
if (value == null) {
return remove(name);
} else {
this.idiomaticValues.put(name, value);
return this.values.put(name, value);
}
}
private Object apply(Parameter param, Object rawValue) {
final String id = param.getId();
if (Objects.isEmpty(rawValue)) {
return remove(id);
}
T idiomaticValue; // preferred Java format
Object canonicalValue; // as required by the RFC
try {
idiomaticValue = param.applyFrom(rawValue);
Assert.notNull(idiomaticValue, "Parameter's resulting idiomaticValue cannot be null.");
canonicalValue = param.applyTo(idiomaticValue);
Assert.notNull(canonicalValue, "Parameter's resulting canonicalValue cannot be null.");
} catch (Exception e) {
StringBuilder sb = new StringBuilder(100);
sb.append("Invalid ").append(getName()).append(" ").append(param).append(" value");
if (param.isSecret()) {
sb.append(": ").append(RedactedSupplier.REDACTED_VALUE);
} else if (!(rawValue instanceof byte[])) {
// don't print raw byte array gibberish. We can't base64[url] encode it either because that could
// make the exception message confusing: the developer would see an encoded string and could think
// that was the rawValue specified when it wasn't.
sb.append(": ").append(Objects.nullSafeToString(rawValue));
}
sb.append(". ").append(e.getMessage());
String msg = sb.toString();
throw new IllegalArgumentException(msg, e);
}
this.idiomaticValues.put(id, idiomaticValue);
return this.values.put(id, canonicalValue);
}
@Override
public Object remove(Object key) {
assertMutable();
this.idiomaticValues.remove(key);
return this.values.remove(key);
}
@Override
public void putAll(Map extends String, ?> m) {
if (m == null) {
return;
}
for (Map.Entry extends String, ?> entry : m.entrySet()) {
String s = entry.getKey();
put(s, entry.getValue());
}
}
@Override
public void clear() {
assertMutable();
this.values.clear();
this.idiomaticValues.clear();
}
@Override
public Set keySet() {
return new KeySet();
}
@Override
public Collection
© 2015 - 2025 Weber Informatics LLC | Privacy Policy