UnknownAware.java
/*
* Copyright 2018-2020 the original author or authors.
*
* 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 org.bremersee.common.model;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.StringTokenizer;
import java.util.function.Function;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
/**
* This base class allows to keep unknown json properties.
*
* @author Christian Bremer
*/
@ToString
@EqualsAndHashCode
@NoArgsConstructor
@AllArgsConstructor
public abstract class UnknownAware {
@Schema(description = "Unknown properties.", hidden = true)
@JsonIgnore
private Map<String, Object> unknown;
/**
* Gets the unknown json properties (can be {@code null}).
*
* @return the unknown
*/
@JsonAnyGetter
public Map<String, Object> unknown() {
return unknown;
}
/**
* Sets the unknown json properties.
*
* @param unknown the unknown json properties
*/
public void unknown(Map<String, Object> unknown) {
if (unknown != null && !unknown.isEmpty()) {
this.unknown = unknown;
}
}
/**
* Any json setter.
*
* @param name the name
* @param value the value
*/
@JsonAnySetter
public void unknown(String name, Object value) {
if (name == null || name.trim().length() == 0) {
return;
}
if (unknown == null) {
unknown = new LinkedHashMap<>();
}
unknown.put(name, value);
}
/**
* Returns {@code true} if there are unknown properties, otherwise {@code false}.
*
* @return {@code true} if there are unknown properties, otherwise {@code false}
*/
public boolean hasUnknown() {
return unknown != null && !unknown.isEmpty();
}
/**
* Find a value from the unknown map.
*
* @param <T> the class type
* @param jsonPath the json path, e. g. {@code $.firstKey.secondKey.thirdKey}
* @param clazz the expected result class
* @return an empty optional if the value was not found or can not be casted, otherwise the value
*/
public <T> Optional<T> findUnknown(final String jsonPath, final Class<T> clazz) {
if (!hasUnknown() || !isJsonPath(jsonPath) || clazz == null) {
return Optional.empty();
}
Object value = null;
Map<String, Object> tmpUnknown = unknown;
final StringTokenizer tokenizer = new StringTokenizer(jsonPath.substring(2), ".");
while (tokenizer.hasMoreTokens()) {
final String token = tokenizer.nextToken();
value = tmpUnknown.get(token);
if (value == null) {
break;
}
if ((value instanceof Map) && tokenizer.hasMoreTokens()) {
try {
//noinspection unchecked,rawtypes
tmpUnknown = (Map) value;
} catch (Exception e) {
return Optional.empty();
}
}
}
if (value == null) {
return Optional.empty();
}
try {
return Optional.of(clazz.cast(value));
} catch (Exception e) {
return Optional.empty();
}
}
/**
* Find a list from the unknown map.
*
* @param <E> the list element type
* @param jsonPath the json path, e. g. {@code $.firstKey.secondKey.thirdKey}
* @param clazz he list element type
* @return an empty optional if the list was not found or can not be casted, otherwise the list
*/
public <E> Optional<List<E>> findUnknownList(final String jsonPath, final Class<E> clazz) {
if (clazz == null) {
return Optional.empty();
}
try {
//noinspection Convert2MethodRef,unchecked,rawtypes
return findUnknown(jsonPath, List.class).map(
(Function<List, List<E>>) list -> Collections.unmodifiableList(list));
} catch (RuntimeException ignored) {
return Optional.empty();
}
}
/**
* Find a map / json object from the unknown map.
*
* @param jsonPath the json path, e. g. {@code $.firstKey.secondKey.thirdKey}
* @return an empty optional if the map / json object was not found or can not be casted,
* otherwise the map / json object
*/
public Optional<Map<String, Object>> findUnknownMap(final String jsonPath) {
try {
//noinspection Convert2MethodRef,unchecked,rawtypes
return findUnknown(jsonPath, Map.class)
.map((Function<Map, Map<String, Object>>) map -> Collections.unmodifiableMap(map));
} catch (RuntimeException ignored) {
return Optional.empty();
}
}
private boolean isJsonPath(final String jsonPath) {
return jsonPath != null && jsonPath.startsWith("$.") && jsonPath.length() > 2;
}
}