All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.robtimus.net.ip.jackson.databind.IPRangeDeserializer Maven / Gradle / Ivy

/*
 * IPRangeDeserializer.java
 * Copyright 2020 Rob Spoor
 *
 * 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 com.github.robtimus.net.ip.jackson.databind;

import static com.github.robtimus.net.ip.jackson.databind.IPRangeSerializer.FROM_FIELD_NAME;
import static com.github.robtimus.net.ip.jackson.databind.IPRangeSerializer.TO_FIELD_NAME;
import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
import com.fasterxml.jackson.databind.node.TextNode;
import com.github.robtimus.net.ip.IPAddress;
import com.github.robtimus.net.ip.IPRange;
import com.github.robtimus.net.ip.IPv4Address;
import com.github.robtimus.net.ip.IPv4Range;
import com.github.robtimus.net.ip.IPv4Subnet;
import com.github.robtimus.net.ip.IPv6Address;
import com.github.robtimus.net.ip.IPv6Range;
import com.github.robtimus.net.ip.IPv6Subnet;
import com.github.robtimus.net.ip.Subnet;

/**
 * Base class for all deserializers for {@link IPRange} and sub types.
 * It supports JSON in one of the following formats:
 * 
    *
  • A CIDR subnet notation.
  • *
  • An object with properties {@code from} and {@code to}.
  • *
* * @author Rob Spoor * @param The type of IP range to deserialize. */ public abstract class IPRangeDeserializer> extends JsonDeserializer { private IPRangeDeserializer() { } @Override public R deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { return p.hasToken(JsonToken.START_OBJECT) ? deserializeIPRange(p) : deserializeSubnet(p.getText()); } private R deserializeIPRange(JsonParser p) throws IOException { TreeNode node = p.readValueAsTree(); validateProperties(node, p); String from = getTextValue(node, FROM_FIELD_NAME); String to = getTextValue(node, TO_FIELD_NAME); return deserializeIPRange(from, to); } private String getTextValue(TreeNode node, String fieldName) { TreeNode childNode = node.get(fieldName); if (childNode instanceof TextNode) { TextNode textNode = (TextNode) childNode; return textNode.asText(); } if (childNode == null) { throw new IllegalStateException(Messages.IPRange.missingProperty.get(fieldName)); } throw new IllegalStateException(Messages.IPRange.invalidPropertyValue.get(fieldName, childNode)); } private void validateProperties(TreeNode node, JsonParser p) throws UnrecognizedPropertyException { Set fieldNames = new LinkedHashSet<>(); for (Iterator i = node.fieldNames(); i.hasNext(); ) { fieldNames.add(i.next()); } fieldNames.remove(FROM_FIELD_NAME); fieldNames.remove(TO_FIELD_NAME); if (!fieldNames.isEmpty()) { String fieldName = fieldNames.iterator().next(); throw UnrecognizedPropertyException.from(p, IPRange.class, fieldName, Arrays.asList(FROM_FIELD_NAME, TO_FIELD_NAME)); } } abstract R deserializeSubnet(String value); abstract R deserializeIPRange(String from, String to); @Override public abstract Class handledType(); /** * A deserializer for {@link IPv4Range}. * * @author Rob Spoor */ public static class IPv4 extends IPRangeDeserializer { static final IPv4 INSTANCE = new IPv4(); /** * Creates a new {@link IPv4Range} deserializer. */ public IPv4() { super(); } @Override IPv4Range deserializeSubnet(String value) { return IPv4Subnet.valueOf(value); } @Override IPv4Range deserializeIPRange(String from, String to) { IPv4Address fromAddress = IPv4Address.valueOf(from); IPv4Address toAddress = IPv4Address.valueOf(to); return fromAddress.equals(toAddress) ? fromAddress.asRange() : fromAddress.to(toAddress); } @Override public Class handledType() { return IPv4Range.class; } } /** * A deserializer for {@link IPv6Range}. * * @author Rob Spoor */ public static class IPv6 extends IPRangeDeserializer { static final IPv6 INSTANCE = new IPv6(); /** * Creates a new {@link IPv6Range} deserializer. */ public IPv6() { super(); } @Override IPv6Range deserializeSubnet(String value) { return IPv6Subnet.valueOf(value); } @Override IPv6Range deserializeIPRange(String from, String to) { IPv6Address fromAddress = IPv6Address.valueOf(from); IPv6Address toAddress = IPv6Address.valueOf(to); return fromAddress.equals(toAddress) ? fromAddress.asRange() : fromAddress.to(toAddress); } @Override public Class handledType() { return IPv6Range.class; } } /** * A deserializer for {@link IPRange}. It can handle both {@link IPv4Range} and {@link IPv6Range}. If a property is declared as either * {@code IPRange} or {@code IPRange}, it will limit the deserialization to only the specified type. * In other words, trying to deserialize an IPv6 range for a property of type {@code IPRange} or vice versa will fail. * * @author Rob Spoor */ public static class AnyVersion extends IPRangeDeserializer> implements ContextualDeserializer { static final AnyVersion INSTANCE = new AnyVersion(); /** * Creates a new {@link IPRange} deserializer. */ public AnyVersion() { super(); } @Override IPRange deserializeSubnet(String value) { return Subnet.valueOf(value); } @Override IPRange deserializeIPRange(String from, String to) { IPAddress fromAddress = IPAddress.valueOf(from); IPAddress toAddress = IPAddress.valueOf(to); return fromAddress.equals(toAddress) ? fromAddress.asRange() : createRange(fromAddress, toAddress); } @SuppressWarnings("unchecked") private static > IPRange createRange(IPAddress from, IPAddress to) { if (from.getClass() != to.getClass()) { throw new IllegalArgumentException(Messages.IPRange.incompatibleToAndFrom.get(from, to)); } // from and to are of the same class, so the cast is safe return ((I) from).to((I) to); } @Override public JsonDeserializer createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException { Class genericType = property != null ? getGenericType(property.getType()) : null; if (genericType == IPv4Address.class) { return IPv4.INSTANCE; } if (genericType == IPv6Address.class) { return IPv6.INSTANCE; } return this; } private Class getGenericType(JavaType type) { return type != null ? type.getBindings().getBoundType(0).getRawClass() : null; } @Override public Class handledType() { return IPRange.class; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy