/* * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://aws.amazon.com/apache2.0 * * or in the "license" file accompanying this file. This file 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.amazonaws.services.stepfunctions.builder.internal.validation; import com.amazonaws.services.stepfunctions.builder.internal.PropertyNames; import com.amazonaws.util.CollectionUtils; import com.amazonaws.util.StringUtils; import java.util.Collection; import java.util.Map; /** * Contains context about the current validation scope and factory methods * for new sub-contexts and for reporting various common problems. */ final class ValidationContext { private final ValidationContext parentContext; private final Location location; private final String identifier; private final ProblemReporter problemReporter; private ValidationContext(Builder builder) { this.parentContext = builder.parentContext; this.location = builder.location; this.identifier = builder.identifier; this.problemReporter = builder.problemReporter; } /** * @return Path to validation error given current context. */ public String getPath() { String parentPath = parentContext == null ? "" : parentContext.getPath() + "."; switch (location) { case Branch: case Retrier: case Catcher: case Choice: case State: return String.format("%s%s[%s]", parentPath, location.toString(), identifier); case StateMachine: return "StateMachine"; default: return "Unknown"; } } /** * Asserts the value is not null, reporting to {@link ProblemReporter} with this context if it is. * * @param propertyValue Value to assert on. * @param propertyName Name of property. */ public void assertNotNull(Object propertyValue, String propertyName) { if (propertyValue == null) { problemReporter.report(new Problem(this, String.format("%s is a required property.", propertyName))); } } /** * Asserts the string is not null and not empty, reporting to {@link ProblemReporter} with this context if it is. * * @param propertyValue Value to assert on. * @param propertyName Name of property. */ public void assertStringNotEmpty(String propertyValue, String propertyName) { if (StringUtils.isNullOrEmpty(propertyValue)) { problemReporter.report(new Problem(this, String.format("%s is a required property.", propertyName))); } } /** * Asserts the collection is not null and not empty, reporting to {@link ProblemReporter} with this context if it is. * * @param collection Collection to assert on. * @param propertyName Name of property. */ public void assertNotEmpty(Collection<?> collection, String propertyName) { if (CollectionUtils.isNullOrEmpty(collection)) { problemReporter.report(new Problem(this, String.format("%s requires one or more items", propertyName))); } } /** * Asserts the map is not null and not empty, reporting to {@link ProblemReporter} with this context if it is. * * @param map Map to assert on. * @param propertyName Name of property. */ public void assertNotEmpty(Map<?, ?> map, String propertyName) { if (map == null || map.size() == 0) { problemReporter.report(new Problem(this, String.format("%s requires one or more entries", propertyName))); } } /** * Asserts the integer is either null or positive, reporting to {@link ProblemReporter} with this context if it is. * * @param integer Value to assert on. * @param propertyName Name of property. */ public void assertIsPositiveIfPresent(Integer integer, String propertyName) { if (integer != null && integer <= 0) { problemReporter.report(new Problem(this, String.format("%s must be positive", propertyName))); } } /** * Asserts the integer is either null or non-negative, reporting to {@link ProblemReporter} with this context if it is. * * @param integer Value to assert on. * @param propertyName Name of property. */ public void assertIsNotNegativeIfPresent(Integer integer, String propertyName) { if (integer != null && integer < 0) { problemReporter.report(new Problem(this, String.format("%s must be non negative", propertyName))); } } /** * Asserts that the string represents a valid JsonPath expression. * * @param path Path expression to validate. */ public void assertIsValidInputPath(String path) { assertIsValidJsonPath(path, PropertyNames.INPUT_PATH); } /** * Asserts that the string represents a valid JsonPath expression. * * @param path Path expression to validate. */ public void assertIsValidOutputPath(String path) { assertIsValidJsonPath(path, PropertyNames.OUTPUT_PATH); } /** * Asserts that the string represents a valid JsonPath expression. * * @param path Path expression to validate. */ public void assertIsValidResultPath(String path) { assertIsValidReferencePath(path, PropertyNames.RESULT_PATH); } /** * Asserts that the string represents a valid JsonPath expression. * * @param path Path expression to validate. * @param propertyName Name of property. */ public void assertIsValidJsonPath(String path, String propertyName) { if (path == null) { return; } if (path.isEmpty()) { problemReporter.report(new Problem(this, String.format("%s cannot be empty", propertyName))); return; } } /** * Asserts that the string represents a valid reference path expression. * * @param path Path expression to validate. * @param propertyName Name of property. */ public void assertIsValidReferencePath(String path, String propertyName) { if (path == null) { return; } if (path.isEmpty()) { problemReporter.report(new Problem(this, String.format("%s cannot be empty", propertyName))); return; } } /** * @param stateName Name of state. * @return State sub-context. */ public ValidationContext state(String stateName) { return newChildContext() .identifier(stateName) .location(Location.State) .build(); } /** * @param index Index of retrier. * @return Retrier sub-context. */ public ValidationContext retrier(int index) { return newChildContext() .identifier(String.valueOf(index)) .location(Location.Retrier) .build(); } /** * @param index Index of branch. * @return Branch sub-context. */ public ValidationContext branch(int index) { return newChildContext() .identifier(String.valueOf(index)) .location(Location.Branch) .build(); } /** * @param index Index of choice. * @return Choice sub-context. */ public ValidationContext choice(int index) { return newChildContext() .identifier(String.valueOf(index)) .location(Location.Choice) .build(); } /** * @param index Index of catcher. * @return Catcher sub-context. */ public ValidationContext catcher(int index) { return newChildContext() .identifier(String.valueOf(index)) .location(Location.Catcher) .build(); } /** * @return Sub-context with this context as the parent and the * same problem reporter. */ private ValidationContext.Builder newChildContext() { return ValidationContext.builder() .parentContext(this) .problemReporter(problemReporter); } /** * @return Builder instance to construct a {@link ValidationContext}. */ public static Builder builder() { return new Builder(); } /** * Builder for a {@link ValidationContext}. */ public static final class Builder { private ValidationContext parentContext; private Location location; private String identifier; private ProblemReporter problemReporter; private Builder() { } /** * Sets the parent of the new context. May be null if at the root. * * @param parentContext Parent of context being built. * @return This object for method chaining. */ public Builder parentContext(ValidationContext parentContext) { this.parentContext = parentContext; return this; } /** * Sets the location of the context. * * @return This object for method chaining. */ public Builder location(Location location) { this.location = location; return this; } /** * Sets an additional identifier (i.e. state name, branch index, etc) for the context. May be null. * * @return This object for method chaining. */ public Builder identifier(String identifier) { this.identifier = identifier; return this; } /** * Sets the problem reporter to report problems in assertion methods. * * @return This object for method chaining. */ public Builder problemReporter(ProblemReporter problemReporter) { this.problemReporter = problemReporter; return this; } /** * @return An immutable {@link ValidationContext} object. */ public ValidationContext build() { return new ValidationContext(this); } } }