/* * Copyright (c) 2012, 2013, Credit Suisse (Anatole Tresch), Werner Keil. * * 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.javamoney.calc; import java.io.Serializable; import java.util.*; import java.util.function.Predicate; /** * Defines a {@link ComplexType} containing several results. Hereby the * different results are identified by arbitrary keys. Additionally each * {@link ComplexType} has a <i>leading</i> item that identifies the type of * result.<br/> * A {@link ComplexType} instance is defined to be implemented as immutable * object and therefore is very useful for modeling multidimensional results * objects or input parameters as they are common in financial applications. * * @author Anatole Tresch * @author Werner Keil */ public final class ComplexType implements Serializable { /** * serialVersionUID. */ private static final long serialVersionUID = 4831291549617485148L; /** * The validation preciate to be used, for complex validations of the input. */ private final Predicate<Map<String, Object>> validationPredicate; /** * The defines input parameters, mapped to their required base type. */ @SuppressWarnings("rawtypes") private final Map<String, Class> typeDef = new HashMap<>(); /** * Set of the parameters that are mandatory. */ private final Set<String> typeRequired; /** * The name of the input type. */ private final String name; /** * Constructor used by builder. * * @param builder the Builder instance used. */ private ComplexType(Builder builder) { this.name = builder.name; this.typeDef.putAll(builder.typeDef); this.typeRequired = builder.typeRequired; this.validationPredicate = builder.validationPredicate; } /** * A {@link ComplexType}may have a type identifier that helps to identify, * what type of items object is returned. * * @return the {@link ComplexType}'s type, never null. */ public String getName() { return this.name; } /** * This method allows to check if a key within the {@code CompoundType} is a * required value, so a corresponding {@link ComplexValue} is valid. * * @param key the key * @return true, if the corresponding value is required, false otherwise. */ public boolean isRequired(String key) { return typeRequired.contains(key); } /** * Validates if the given {@link ComplexValue} defines all the attributes * as required by this {@link ComplexType} instance. * * @param compundValueMap the {@link Map} to be validated before a {@link ComplexValue} * is created. * @throws IllegalArgumentException if validation fails. */ @SuppressWarnings("unchecked") public void validate(Map<String, Object> compundValueMap) throws MonetaryConstraintException { // Check for required fields to be present for (String key : this.typeRequired) { Object value = compundValueMap.get(key); if (value == null) { throw new MonetaryConstraintException("Required value '" + key + "' of type " + typeDef.get(key) + " is missing."); } } // Check the fields type for all possible fields for (@SuppressWarnings("rawtypes") Map.Entry<String, Class> entry : this.typeDef.entrySet()) { Object value = compundValueMap.get(entry.getKey()); if (value != null && !entry.getValue().isAssignableFrom(value.getClass())) { throw new MonetaryConstraintException("Value for '" + entry.getKey() + "' has invalid type type " + value.getClass().getName() + ", required: " + entry.getValue() + "."); } } if (validationPredicate != null && !validationPredicate.test(compundValueMap)) { throw new MonetaryConstraintException("Validation predicate failed '" + validationPredicate + "."); } } /** * Builder for creating new instances of {@link ComplexType}. */ public static final class Builder { /** * The validation preciate to be used, for complex validations of the input. */ private Predicate<Map<String, Object>> validationPredicate; /** * The defines input parameters, mapped to their required base type. */ @SuppressWarnings("rawtypes") private Map<String, Class> typeDef = new HashMap<>(); /** * Set of the parameters that are mandatory. */ private Set<String> typeRequired = new HashSet<>(); /** * The name of the input type. */ private String name; /** * Creates a new Builder. * * @param name the compound type's name, not null. */ public Builder(String name) { Objects.requireNonNull(name); this.name = name; } public Builder setNameForInput(Class<?> type) { Objects.requireNonNull(type); this.name = type.getName() + "_in"; return this; } public Builder setNameForOutput(Class<?> type) { Objects.requireNonNull(type); this.name = type.getName() + "_out"; return this; } public Builder setName(String name) { Objects.requireNonNull(name); this.name = name; return this; } public Builder setValidationPredicate( Predicate<Map<String, Object>> predicate) { this.validationPredicate = predicate; return this; } public Builder addParameter(String key, Class<?> type) { this.typeDef.put(key, type); return this; } public Builder addRequiredParameter(String key, Class<?> type) { this.typeDef.put(key, type); this.typeRequired.add(key); return this; } public ComplexType build() { return new ComplexType(this); } } public void checkInput(ComplexValue input) { if (input == null) { throw new IllegalArgumentException("Input missing, required: " + this); } if (!this.equals(input.getComplexType())) { throw new IllegalArgumentException("Invalid input, was " + input + ", required: " + this); } } }