CommandJsonDeserializer.java
/*
* Copyright (c) 2017 Bosch Software Innovations GmbH.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/epl-2.0/index.php
*
* Contributors:
* Bosch Software Innovations GmbH - initial contribution
*/
package org.eclipse.ditto.signals.commands.base;
import static org.eclipse.ditto.model.base.common.ConditionChecker.argumentNotEmpty;
import static org.eclipse.ditto.model.base.common.ConditionChecker.checkNotNull;
import java.text.MessageFormat;
import java.util.Optional;
import java.util.function.Supplier;
import javax.annotation.concurrent.Immutable;
import org.eclipse.ditto.json.JsonFactory;
import org.eclipse.ditto.json.JsonFieldDefinition;
import org.eclipse.ditto.json.JsonMissingFieldException;
import org.eclipse.ditto.json.JsonObject;
import org.eclipse.ditto.json.JsonParseException;
import org.eclipse.ditto.model.base.exceptions.DittoJsonException;
/**
* This class helps to deserialize JSON to a sub-class of {@link Command}. Hereby this class extracts the values
* which are common for all commands. All remaining required values have to be extracted in a user provided Supplier.
* There the actual command object is created, too.
*/
// TODO Replace with simple type check.
@Immutable
public final class CommandJsonDeserializer<T extends Command> {
private final JsonObject jsonObject;
private final String expectedCommandType;
/**
* Constructs a new {@code CommandJsonDeserializer} object.
*
* @param type the type of the command.
* @param jsonObject the JSON object to deserialize.
* @throws NullPointerException if any argument is {@code null}.
* @throws IllegalArgumentException if {@code type} is empty or does not contain a type prefix.
*/
public CommandJsonDeserializer(final String type, final JsonObject jsonObject) {
validateType(argumentNotEmpty(type, "command type"));
checkNotNull(jsonObject, "JSON object to be deserialized");
this.jsonObject = jsonObject;
expectedCommandType = type;
}
private static void validateType(final String type) {
// for backward compatibility, extract the prefix for later use:
if (!type.contains(":")) {
final String msgPattern = "The type <{0}> does not contain a prefix separated by a colon (':')!";
throw new IllegalArgumentException(MessageFormat.format(msgPattern, type));
}
}
/**
* Constructs a new {@code CommandJsonDeserializer} object.
*
* @param type the type of the target command of deserialization.
* @param jsonString the JSON string to be deserialized.
* @throws NullPointerException if any argument is {@code null}.
* @throws IllegalArgumentException if {@code jsonString} is empty.
* @throws org.eclipse.ditto.json.JsonParseException if {@code jsonString} does not contain a valid JSON object.
*/
public CommandJsonDeserializer(final String type, final String jsonString) {
this(type, JsonFactory.newObject(jsonString));
}
/**
* Validates the command type and invokes the specified Supplier which provides the actual {@link Command}.
*
* @param commandSupplier creates the actual {@code Command} object.
* @return the command.
* @throws NullPointerException if {@code commandSupplier} is {@code null}.
* @throws org.eclipse.ditto.json.JsonMissingFieldException if the JSON object did not contain a field for
* {@link Command.JsonFields#TYPE}.
* @throws DittoJsonException if the JSON object did not contain the expected value for
* {@link Command.JsonFields#TYPE}.
*/
public T deserialize(final Supplier<T> commandSupplier) {
checkNotNull(commandSupplier, "supplier for a command object");
validateCommandType();
return commandSupplier.get();
}
private void validateCommandType() {
final JsonFieldDefinition<String> typeFieldDefinition = Command.JsonFields.TYPE;
final Optional<String> actualCommandTypeOptional = jsonObject.getValue(typeFieldDefinition);
final String actualCommandType = actualCommandTypeOptional.orElseGet(() -> {
// "type" was introduced in V2, if not present, take "command" instead.
final String commandTypePrefix = expectedCommandType.split(":")[0];
return jsonObject.getValue(Command.JsonFields.ID)
.map(id -> commandTypePrefix + ':' + id)
.orElseThrow(() -> new JsonMissingFieldException(typeFieldDefinition));
});
if (!expectedCommandType.equals(actualCommandType)) {
final String msgPattern = "Command JSON was not a <{0}> command but a <{1}>!";
final String msg = MessageFormat.format(msgPattern, expectedCommandType, actualCommandType);
throw new DittoJsonException(new JsonParseException(msg));
}
}
}