Java tutorial
/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.gst.portfolio.tax.serialization; import java.lang.reflect.Type; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import org.apache.commons.lang.StringUtils; import com.gst.accounting.glaccount.domain.GLAccountType; import com.gst.infrastructure.core.api.JsonCommand; import com.gst.infrastructure.core.data.ApiParameterError; import com.gst.infrastructure.core.data.DataValidatorBuilder; import com.gst.infrastructure.core.exception.InvalidJsonException; import com.gst.infrastructure.core.exception.PlatformApiDataValidationException; import com.gst.infrastructure.core.serialization.FromJsonHelper; import com.gst.infrastructure.core.service.DateUtils; import com.gst.portfolio.tax.api.TaxApiConstants; import com.gst.portfolio.tax.domain.TaxComponent; import com.gst.portfolio.tax.domain.TaxGroup; import com.gst.portfolio.tax.domain.TaxGroupMappings; import org.joda.time.LocalDate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.reflect.TypeToken; @Component public class TaxValidator { final Set<String> supportedTaxComponentCreateParameters = new HashSet<>(Arrays.asList("dateFormat", "locale", TaxApiConstants.nameParamName, TaxApiConstants.percentageParamName, TaxApiConstants.startDateParamName, TaxApiConstants.debitAccountTypeParamName, TaxApiConstants.debitAcountIdParamName, TaxApiConstants.creditAccountTypeParamName, TaxApiConstants.creditAcountIdParamName)); final Set<String> supportedTaxComponentUpdateParameters = new HashSet<>( Arrays.asList("dateFormat", "locale", TaxApiConstants.nameParamName, TaxApiConstants.percentageParamName, TaxApiConstants.startDateParamName)); final Set<String> supportedTaxGroupParameters = new HashSet<>(Arrays.asList("dateFormat", "locale", TaxApiConstants.nameParamName, TaxApiConstants.taxComponentsParamName)); final Set<String> supportedTaxGroupTaxComponentsCreateParameters = new HashSet<>( Arrays.asList(TaxApiConstants.taxComponentIdParamName, TaxApiConstants.startDateParamName)); final Set<String> supportedTaxGroupTaxComponentsUpdateParameters = new HashSet<>( Arrays.asList(TaxApiConstants.idParamName, TaxApiConstants.taxComponentIdParamName, TaxApiConstants.startDateParamName, TaxApiConstants.endDateParamName)); private final FromJsonHelper fromApiJsonHelper; @Autowired public TaxValidator(final FromJsonHelper fromApiJsonHelper) { this.fromApiJsonHelper = fromApiJsonHelper; } public void validateForTaxComponentCreate(final String json) { if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } final Type typeOfMap = new TypeToken<Map<String, Object>>() { }.getType(); this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, this.supportedTaxComponentCreateParameters); final List<ApiParameterError> dataValidationErrors = new ArrayList<>(); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) .resource("tax.component"); final JsonElement element = this.fromApiJsonHelper.parse(json); final String name = this.fromApiJsonHelper.extractStringNamed(TaxApiConstants.nameParamName, element); baseDataValidator.reset().parameter(TaxApiConstants.nameParamName).value(name).notBlank(); final BigDecimal percentage = this.fromApiJsonHelper .extractBigDecimalWithLocaleNamed(TaxApiConstants.percentageParamName, element); baseDataValidator.reset().parameter(TaxApiConstants.percentageParamName).value(percentage).notBlank() .positiveAmount().notGreaterThanMax(BigDecimal.valueOf(100)); final Integer debitAccountType = this.fromApiJsonHelper .extractIntegerSansLocaleNamed(TaxApiConstants.debitAccountTypeParamName, element); baseDataValidator.reset().parameter(TaxApiConstants.debitAccountTypeParamName).value(debitAccountType) .ignoreIfNull().isOneOfTheseValues(GLAccountType.ASSET.getValue(), GLAccountType.LIABILITY.getValue(), GLAccountType.EQUITY.getValue(), GLAccountType.INCOME.getValue(), GLAccountType.EXPENSE.getValue()); final Long debitAccountId = this.fromApiJsonHelper.extractLongNamed(TaxApiConstants.debitAcountIdParamName, element); baseDataValidator.reset().parameter(TaxApiConstants.debitAcountIdParamName).value(debitAccountId) .longGreaterThanZero(); if (debitAccountType != null || debitAccountId != null) { baseDataValidator.reset().parameter(TaxApiConstants.debitAccountTypeParamName).value(debitAccountType) .notBlank(); baseDataValidator.reset().parameter(TaxApiConstants.debitAcountIdParamName).value(debitAccountId) .notBlank(); } final Integer creditAccountType = this.fromApiJsonHelper .extractIntegerSansLocaleNamed(TaxApiConstants.creditAccountTypeParamName, element); baseDataValidator.reset().parameter(TaxApiConstants.creditAccountTypeParamName).value(creditAccountType) .ignoreIfNull().isOneOfTheseValues(GLAccountType.ASSET.getValue(), GLAccountType.LIABILITY.getValue(), GLAccountType.EQUITY.getValue(), GLAccountType.INCOME.getValue(), GLAccountType.EXPENSE.getValue()); final Long creditAccountId = this.fromApiJsonHelper .extractLongNamed(TaxApiConstants.creditAcountIdParamName, element); baseDataValidator.reset().parameter(TaxApiConstants.creditAcountIdParamName).value(creditAccountId) .longGreaterThanZero(); if (creditAccountType != null || creditAccountId != null) { baseDataValidator.reset().parameter(TaxApiConstants.creditAcountIdParamName).value(creditAccountId) .notBlank(); baseDataValidator.reset().parameter(TaxApiConstants.creditAccountTypeParamName).value(creditAccountType) .notBlank(); } throwExceptionIfValidationWarningsExist(dataValidationErrors); } public void validateForTaxComponentUpdate(final String json) { if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } final Type typeOfMap = new TypeToken<Map<String, Object>>() { }.getType(); this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, this.supportedTaxComponentUpdateParameters); final List<ApiParameterError> dataValidationErrors = new ArrayList<>(); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) .resource("tax.component"); final JsonElement element = this.fromApiJsonHelper.parse(json); if (this.fromApiJsonHelper.parameterExists(TaxApiConstants.nameParamName, element)) { final String name = this.fromApiJsonHelper.extractStringNamed(TaxApiConstants.nameParamName, element); baseDataValidator.reset().parameter(TaxApiConstants.nameParamName).value(name).notBlank(); } if (this.fromApiJsonHelper.parameterExists(TaxApiConstants.percentageParamName, element)) { final BigDecimal percentage = this.fromApiJsonHelper .extractBigDecimalWithLocaleNamed(TaxApiConstants.percentageParamName, element); baseDataValidator.reset().parameter(TaxApiConstants.percentageParamName).value(percentage).notBlank() .positiveAmount().notGreaterThanMax(BigDecimal.valueOf(100)); } if (this.fromApiJsonHelper.parameterExists(TaxApiConstants.startDateParamName, element)) { final LocalDate startDate = this.fromApiJsonHelper .extractLocalDateNamed(TaxApiConstants.startDateParamName, element); baseDataValidator.reset().parameter(TaxApiConstants.startDateParamName).value(startDate) .validateDateAfter(DateUtils.getLocalDateOfTenant()); } throwExceptionIfValidationWarningsExist(dataValidationErrors); } public void validateForTaxGroupCreate(final String json) { if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } final Type typeOfMap = new TypeToken<Map<String, Object>>() { }.getType(); this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, this.supportedTaxGroupParameters); final List<ApiParameterError> dataValidationErrors = new ArrayList<>(); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) .resource("tax.group"); final JsonElement element = this.fromApiJsonHelper.parse(json); final String name = this.fromApiJsonHelper.extractStringNamed(TaxApiConstants.nameParamName, element); baseDataValidator.reset().parameter(TaxApiConstants.nameParamName).value(name).notBlank(); final JsonArray taxComponents = this.fromApiJsonHelper .extractJsonArrayNamed(TaxApiConstants.taxComponentsParamName, element); baseDataValidator.reset().parameter(TaxApiConstants.taxComponentsParamName).value(taxComponents).notBlank(); final JsonObject topLevelJsonElement = element.getAsJsonObject(); if (topLevelJsonElement.get(TaxApiConstants.taxComponentsParamName).isJsonArray()) { final JsonArray array = topLevelJsonElement.get(TaxApiConstants.taxComponentsParamName) .getAsJsonArray(); baseDataValidator.reset().parameter(TaxApiConstants.taxComponentsParamName).value(array.size()) .integerGreaterThanZero(); for (int i = 1; i <= array.size(); i++) { final JsonObject taxComponent = array.get(i - 1).getAsJsonObject(); final String arrayObjectJson = this.fromApiJsonHelper.toJson(taxComponent); this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, arrayObjectJson, supportedTaxGroupTaxComponentsCreateParameters); final Long taxComponentId = this.fromApiJsonHelper .extractLongNamed(TaxApiConstants.taxComponentIdParamName, taxComponent); baseDataValidator.reset().parameter(TaxApiConstants.taxComponentsParamName) .parameterAtIndexArray(TaxApiConstants.taxComponentIdParamName, i).value(taxComponentId) .notNull().longGreaterThanZero(); } } throwExceptionIfValidationWarningsExist(dataValidationErrors); } public void validateForTaxGroupUpdate(final String json) { if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } final Type typeOfMap = new TypeToken<Map<String, Object>>() { }.getType(); this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, this.supportedTaxGroupParameters); final List<ApiParameterError> dataValidationErrors = new ArrayList<>(); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) .resource("tax.group"); final JsonElement element = this.fromApiJsonHelper.parse(json); if (this.fromApiJsonHelper.parameterExists(TaxApiConstants.nameParamName, element)) { final String name = this.fromApiJsonHelper.extractStringNamed(TaxApiConstants.nameParamName, element); baseDataValidator.reset().parameter(TaxApiConstants.nameParamName).value(name).notBlank(); } if (this.fromApiJsonHelper.parameterExists(TaxApiConstants.taxComponentsParamName, element)) { final JsonArray taxComponents = this.fromApiJsonHelper .extractJsonArrayNamed(TaxApiConstants.taxComponentsParamName, element); baseDataValidator.reset().parameter(TaxApiConstants.taxComponentsParamName).value(taxComponents) .notBlank(); final JsonObject topLevelJsonElement = element.getAsJsonObject(); final String dateFormat = this.fromApiJsonHelper.extractDateFormatParameter(topLevelJsonElement); final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement); if (topLevelJsonElement.get(TaxApiConstants.taxComponentsParamName).isJsonArray()) { final JsonArray array = topLevelJsonElement.get(TaxApiConstants.taxComponentsParamName) .getAsJsonArray(); for (int i = 1; i <= array.size(); i++) { final JsonObject taxComponent = array.get(i - 1).getAsJsonObject(); final String arrayObjectJson = this.fromApiJsonHelper.toJson(taxComponent); this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, arrayObjectJson, supportedTaxGroupTaxComponentsUpdateParameters); final Long taxComponentId = this.fromApiJsonHelper .extractLongNamed(TaxApiConstants.taxComponentIdParamName, taxComponent); final Long taxMappingId = this.fromApiJsonHelper .extractLongNamed(TaxApiConstants.taxComponentIdParamName, taxComponent); if (taxMappingId == null) { baseDataValidator.reset() .parameter(TaxApiConstants.taxComponentsParamName + "." + TaxApiConstants.taxComponentIdParamName + ".at.index." + i) .value(taxComponentId).notNull().longGreaterThanZero(); } else { baseDataValidator.reset() .parameter(TaxApiConstants.taxComponentsParamName + "." + TaxApiConstants.taxComponentIdParamName + ".at.index." + i) .value(taxComponentId).longGreaterThanZero(); baseDataValidator.reset() .parameter(TaxApiConstants.taxComponentsParamName + "." + TaxApiConstants.idParamName + ".at.index." + i) .value(taxMappingId).longGreaterThanZero(); } final LocalDate endDate = this.fromApiJsonHelper.extractLocalDateNamed( TaxApiConstants.endDateParamName, taxComponent, dateFormat, locale); baseDataValidator.reset() .parameter(TaxApiConstants.taxComponentsParamName + "." + TaxApiConstants.endDateParamName + ".at.index." + i) .value(endDate).ignoreIfNull().validateDateAfter(DateUtils.getLocalDateOfTenant()); final LocalDate startDate = this.fromApiJsonHelper.extractLocalDateNamed( TaxApiConstants.startDateParamName, taxComponent, dateFormat, locale); if (endDate != null && startDate != null) { baseDataValidator.reset() .parameter(TaxApiConstants.taxComponentsParamName + ".at.index." + i) .failWithCode("start.date.end.date.both.should.not.be.present", startDate, endDate); } } } } throwExceptionIfValidationWarningsExist(dataValidationErrors); } public void validateTaxGroupEndDateAndTaxComponent(final TaxGroup taxGroup, final Set<TaxGroupMappings> groupMappings) { final List<ApiParameterError> dataValidationErrors = new ArrayList<>(); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) .resource("tax.group"); for (TaxGroupMappings mapping : groupMappings) { if (mapping.getId() != null) { TaxGroupMappings existing = taxGroup.findOneBy(mapping); if (existing.endDate() != null && mapping.endDate() != null && !existing.endDate().isEqual(mapping.endDate())) { baseDataValidator.reset().parameter(TaxApiConstants.endDateParamName) .failWithCode("can.not.modify.end.date.once.updated"); } else { baseDataValidator.reset().parameter(TaxApiConstants.endDateParamName).value(mapping.endDate()) .ignoreIfNull().validateDateAfter(existing.startDate()); } if (mapping.getTaxComponent() != null && !existing.getTaxComponent().getId().equals(mapping.getTaxComponent().getId())) { baseDataValidator.reset().parameter(TaxApiConstants.taxComponentIdParamName) .failWithCode("update.not.supported"); ; } } else if (mapping.endDate() != null) { baseDataValidator.reset().parameter(TaxApiConstants.endDateParamName) .failWithCode("not.supported.for.new.association"); } } throwExceptionIfValidationWarningsExist(dataValidationErrors); } public void validateTaxGroup(final TaxGroup taxGroup) { final List<ApiParameterError> dataValidationErrors = new ArrayList<>(); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) .resource("tax.group"); Set<TaxGroupMappings> groupMappings = taxGroup.getTaxGroupMappings(); validateGroupTotal(groupMappings, baseDataValidator, "total.percentage"); validateOverlappingComponents(groupMappings, baseDataValidator); throwExceptionIfValidationWarningsExist(dataValidationErrors); } public void validateTaxComponentForUpdate(final TaxComponent taxComponent) { final List<ApiParameterError> dataValidationErrors = new ArrayList<>(); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) .resource("tax.component"); validateGroupTotal(taxComponent.getTaxGroupMappings(), baseDataValidator, "group.total." + TaxApiConstants.percentageParamName); throwExceptionIfValidationWarningsExist(dataValidationErrors); } public void validateStartDate(final LocalDate existingStartDate, final JsonCommand command) { final List<ApiParameterError> dataValidationErrors = new ArrayList<>(); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) .resource("tax.component"); validateStartDate(existingStartDate, command.localDateValueOfParameterNamed(TaxApiConstants.startDateParamName), baseDataValidator); throwExceptionIfValidationWarningsExist(dataValidationErrors); } private void validateStartDate(final LocalDate existingStartDate, final LocalDate startDate, final DataValidatorBuilder baseDataValidator) { baseDataValidator.reset().parameter(TaxApiConstants.startDateParamName).value(startDate) .validateDateAfter(existingStartDate); } private void validateOverlappingComponents(final Set<TaxGroupMappings> taxMappings, final DataValidatorBuilder baseDataValidator) { for (TaxGroupMappings groupMappingsOne : taxMappings) { final List<TaxGroupMappings> mappings = new ArrayList<>(taxMappings); mappings.remove(groupMappingsOne); for (TaxGroupMappings groupMappings : mappings) { if (groupMappingsOne.getTaxComponent().equals(groupMappings.getTaxComponent())) { if (groupMappingsOne.endDate() == null && groupMappings.endDate() == null) { baseDataValidator.reset().parameter("component").failWithCode("dates.are.overlapping"); } else if (groupMappingsOne.startDate().isAfter(groupMappings.startDate())) { baseDataValidator.reset().parameter("component.start.date") .value(groupMappingsOne.startDate()).validateDateAfter(groupMappings.endDate()); } else { baseDataValidator.reset().parameter("component.start.date").value(groupMappings.startDate()) .validateDateAfter(groupMappingsOne.endDate()); } } } } } private void validateGroupTotal(final Set<TaxGroupMappings> taxMappings, final DataValidatorBuilder baseDataValidator, final String paramenter) { for (TaxGroupMappings groupMappingsOne : taxMappings) { Collection<LocalDate> dates = groupMappingsOne.getTaxComponent().allStartDates(); for (LocalDate date : dates) { LocalDate applicableDate = date.plusDays(1); BigDecimal total = BigDecimal.ZERO; for (TaxGroupMappings groupMappings : taxMappings) { if (groupMappings.occursOnDayFromAndUpToAndIncluding(applicableDate)) { BigDecimal applicablePercentage = groupMappings.getTaxComponent() .getApplicablePercentage(applicableDate); if (applicablePercentage != null) { total = total.add(applicablePercentage); } } } baseDataValidator.reset().parameter(paramenter).value(total) .notGreaterThanMax(BigDecimal.valueOf(100)); } } } private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) { if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } }