ValidationEngine.java
/*
* GovWay - A customizable API Gateway
* https://govway.org
*
* Copyright (c) 2005-2024 Link.it srl (https://link.it).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3, as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openspcoop2.web.lib.mvc.properties.utils;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.openspcoop2.core.mvc.properties.Collection;
import org.openspcoop2.core.mvc.properties.Condition;
import org.openspcoop2.core.mvc.properties.Conditions;
import org.openspcoop2.core.mvc.properties.Config;
import org.openspcoop2.core.mvc.properties.Defined;
import org.openspcoop2.core.mvc.properties.Equals;
import org.openspcoop2.core.mvc.properties.Item;
import org.openspcoop2.core.mvc.properties.Property;
import org.openspcoop2.core.mvc.properties.Section;
import org.openspcoop2.core.mvc.properties.Selected;
import org.openspcoop2.core.mvc.properties.Subsection;
import org.openspcoop2.web.lib.mvc.properties.beans.BaseItemBean;
import org.openspcoop2.web.lib.mvc.properties.beans.ConfigBean;
import org.openspcoop2.web.lib.mvc.properties.beans.ItemBean;
import org.openspcoop2.web.lib.mvc.properties.beans.SectionBean;
import org.openspcoop2.web.lib.mvc.properties.beans.SubsectionBean;
import org.openspcoop2.web.lib.mvc.properties.exception.ValidationException;
import org.openspcoop2.core.mvc.properties.constants.ItemType;
import org.openspcoop2.core.mvc.properties.provider.ExternalResources;
import org.openspcoop2.core.mvc.properties.provider.IProvider;
import org.openspcoop2.core.mvc.properties.provider.ProviderException;
import org.openspcoop2.utils.resources.ClassLoaderUtilities;
/****
*
* ValidationEngine validatore delle configurazioni
*
* @author Pintori Giuliano (pintori@link.it)
* @author $Author$
* @version $Rev$, $Date$
*
*/
public class ValidationEngine {
public static boolean validateConfig(Config config, ExternalResources externalResources) throws ValidationException{
// controllo l'univocita' delle chiavi e le conservo per riutilizzarle per verificare
//che gli elementi riferiti nelle conditions esistano.
ConfigBean metadata = getMetadata(config);
List<Section> sectionList = config.getSectionList();
try {
for (int i= 0; i < sectionList.size() ; i++) {
Section section = sectionList.get(i);
validateSection(section,metadata, externalResources);
}
// controllo che tutti gli item che vanno a finire dentro una unica property abbiano lo stesso separatore
for (String nomeProperty : metadata.getMapPropertyItem().keySet()) {
String separatore = null;
List<BaseItemBean<?>> list = metadata.getMapPropertyItem().get(nomeProperty);
if(list.size() > 1) {
for (BaseItemBean<?> itemBean : list) {
if(itemBean.getSaveProperty()!= null && itemBean.getSaveProperty().isAppend()) {
if(separatore == null) {
separatore = itemBean.getSaveProperty().getAppendSeparator();
} else {
if(!separatore.equals(itemBean.getSaveProperty().getAppendSeparator()))
throw new ValidationException("I separatori di append per la property ["+nomeProperty+"] devono essere tutti uguali.");
}
}
}
}
}
}catch(ValidationException e) {
throw new ValidationException("Errore durante la validazione della configurazione ["+config.getId()+"]: "+ e.getMessage(),e);
}
return true;
}
private static void validateSection(Section section,ConfigBean metadata, ExternalResources externalResources) throws ValidationException{
try {
validaConditions(section.getConditions(),metadata);
if(section.getItemList() != null) {
for (Item item : section.getItemList()) {
validaItem(item,metadata, externalResources);
}
}
if(section.getSubsectionList() != null) {
for (int i= 0; i < section.getSubsectionList().size() ; i++) {
Subsection subSection = section.getSubsectionList().get(i);
validaSubsection(subSection,metadata, externalResources);
}
}
}catch(ValidationException e) {
throw new ValidationException("Section ["+section.getLabel()+"] -> "+ e.getMessage(), e);
}
}
private static void validaSubsection(Subsection subSection,ConfigBean metadata, ExternalResources externalResources) throws ValidationException {
try {
validaConditions(subSection.getConditions(),metadata);
if(subSection.getItemList() != null) {
for (Item item : subSection.getItemList()) {
validaItem(item,metadata, externalResources);
}
}
}catch(ValidationException e) {
throw new ValidationException("Subsection ["+subSection.getLabel()+"] -> "+ e.getMessage(), e);
}
}
private static void validaItem(Item item,ConfigBean metadata, ExternalResources externalResources) throws ValidationException {
try {
validaConditions(item.getConditions(),metadata);
if(item.getProperty().getProperties() != null) {
if(!metadata.getListaNomiProperties().contains(item.getProperty().getProperties()))
throw new ValidationException("Il nome delle properties ["+item.getProperty().getProperties()+"] indicato nella collection non e' dichiarato nella sezione collection della configurazione");
}
// il force puo' essere utilizzato solo da elementi hidden
if(item.getProperty().isForce() && !item.getType().equals(ItemType.HIDDEN)) {
throw new ValidationException("L'attributo Force puo' essere utilizzato solo per gli items di tipo Hidden");
}
switch(item.getType()){
case CHECKBOX:
validaCheckBox(item);
break;
case HIDDEN:
case LOCK_HIDDEN:
validaHidden(item);
break;
case NUMBER:
validaNumber(item,metadata, externalResources);
break;
case SELECT:
validaSelect(item,metadata, externalResources);
break;
case TEXT:
case TEXTAREA:
case LOCK:
validaText(item,metadata, externalResources);
break;
}
}catch(Exception e) {
throw new ValidationException("Item ["+item.getName()+"] non valido: "+ e.getMessage(), e);
}
}
private static void validaCheckBox(Item item) throws ValidationException{
Property property = item.getProperty();
// se e' una property di tipo append valido il separatore
if(property.isAppend()) {
if(property.getSelectedValue().contains(property.getAppendSeparator()))
throw new ValidationException("Il valore indicato per l'attributo SelectedValue ["+property.getSelectedValue()+"] contiene il separatore previsto per il salvataggio ["+property.getAppendSeparator()+"]");
if(property.getUnselectedValue().contains(property.getAppendSeparator()))
throw new ValidationException("Il valore indicato per l'attributo UnselectedValue ["+property.getUnselectedValue()+"] contiene il separatore previsto per il salvataggio ["+property.getAppendSeparator()+"]");
}
}
private static void validaHidden(Item item) throws ValidationException{
Property property = item.getProperty();
if(item.getValue() == null) {
throw new ValidationException("L'attributo Value e' obbligatorio per gli elementi di tipo Hidden");
}
// se e' una property di tipo append valido il separatore
if(property.isAppend()) {
if(item.getValue().contains(property.getAppendSeparator()))
throw new ValidationException("Il valore indicato per l'attributo Value ["+item.getValue()+"] contiene il separatore previsto per il salvataggio ["+property.getAppendSeparator()+"]");
}
}
private static String getDefault(Item item,ConfigBean metadata, ExternalResources externalResources) throws ProviderException {
if(StringUtils.isNotEmpty(item.getDefault())) {
return item.getDefault();
}
else if(metadata.getProvider()!=null) {
return metadata.getProvider().getDefault(item.getName(), externalResources);
}
return null;
}
private static void validaNumber(Item item,ConfigBean metadata, ExternalResources externalResources) throws ValidationException, ProviderException{
Property property = item.getProperty();
// se e' una property di tipo append valido il separatore
if(property.isAppend()) {
String defaultValue = getDefault(item, metadata, externalResources);
if(defaultValue != null && defaultValue.contains(property.getAppendSeparator()))
throw new ValidationException("Il valore indicato per l'attributo Default ["+item.getValue()+"] contiene il separatore previsto per il salvataggio ["+property.getAppendSeparator()+"]");
}
}
private static void validaSelect(Item item,ConfigBean metadata, ExternalResources externalResources) throws ValidationException, ProviderException{
Property property = item.getProperty();
// se e' una property di tipo append valido il separatore
if(property.isAppend()) {
String defaultValue = getDefault(item, metadata, externalResources);
if(defaultValue != null && defaultValue.contains(property.getAppendSeparator()))
throw new ValidationException("Il valore indicato per l'attributo Default ["+item.getValue()+"] contiene il separatore previsto per il salvataggio ["+property.getAppendSeparator()+"]");
}
if(item.getValues() == null || item.getValues().sizeValueList() == 0) {
if(metadata.getProvider()==null || metadata.getProvider().getValues(item.getName(), externalResources)==null || metadata.getProvider().getValues(item.getName(), externalResources).size()<=0) {
throw new ValidationException("E' necessario indicare una lista di Values, o definirli in un plugins, per gli Item di tipo Select");
}
}
}
private static void validaText(Item item,ConfigBean metadata, ExternalResources externalResources) throws ValidationException, ProviderException{
Property property = item.getProperty();
// se e' una property di tipo append valido il separatore
if(property.isAppend()) {
String defaultValue = getDefault(item, metadata, externalResources);
if(defaultValue != null && defaultValue.contains(property.getAppendSeparator()))
throw new ValidationException("Il valore indicato per l'attributo Default ["+item.getValue()+"] contiene il separatore previsto per il salvataggio ["+property.getAppendSeparator()+"]");
}
}
private static void validaConditions(Conditions conditions,ConfigBean metadata) throws ValidationException {
if(conditions == null) return;
for (int i = 0; i < conditions.sizeConditionList(); i ++) {
Condition condition = conditions.getCondition(i);
validaCondition(condition, (i+1),metadata);
}
}
private static void validaCondition(Condition condition, int indice,ConfigBean metadata) throws ValidationException{
if((condition.getDefinedList() == null || condition.getDefinedList().size() == 0)
&& (condition.getEqualsList() == null || condition.getEqualsList().size() == 0)
&& (condition.getLessEqualsList() == null || condition.getLessEqualsList().size() == 0)
&& (condition.getLessThenList() == null || condition.getLessThenList().size() == 0)
&& (condition.getGreaterEqualsList() == null || condition.getGreaterEqualsList().size() == 0)
&& (condition.getGreaterThenList() == null || condition.getGreaterThenList().size() == 0)
&& (condition.getStartsWithList() == null || condition.getStartsWithList().size() == 0)
&& (condition.getEndsWithList() == null || condition.getEndsWithList().size() == 0)
&& (condition.getSelectedList() == null || condition.getSelectedList().size() == 0))
throw new ValidationException("La condition numero ["+indice+"] non e' valida: indicare almeno un elemento tra Defined, Equals o Selected.");
// controlli sulle condizioni le condizioni devono riferire a elementi presenti
for (int i = 0; i < condition.getDefinedList().size(); i++) {
Defined defined = condition.getDefined(i);
if(!itemDefined(defined.getName(),metadata))
throw new ValidationException("L'elemento Defined numero ["+(i+1)+"] della Condition numero ["
+indice+"] non e' valido: si riferisce ad un elemento ["+defined.getName()+"] non presente nella configurazione.");
BaseItemBean<?> itemToCheck = metadata.getItem(defined.getName());
ItemType itemToCheckType = itemToCheck.getItemType();
// la condizione defined non si puo' attivare su un elemento checkbox
if(itemToCheckType.equals(ItemType.CHECKBOX)) {
throw new ValidationException("L'elemento Defined numero ["+(i+1)+"] della Condition numero ["
+indice+"] non e' valido: si riferisce ad un elemento ["+defined.getName()+"] che ha un tipo non compatibile con la regola, non si puo' controllare se una CheckBox e' Defined");
}
}
for (int i = 0; i < condition.getEqualsList().size(); i++) {
checkEqualsTypeCondition(condition.getEquals(i), condition, indice, metadata, i, "equals");
}
for (int i = 0; i < condition.getLessEqualsList().size(); i++) {
checkEqualsTypeCondition(condition.getLessEquals(i), condition, indice, metadata, i, "lessEquals");
}
for (int i = 0; i < condition.getLessThenList().size(); i++) {
checkEqualsTypeCondition(condition.getLessThen(i), condition, indice, metadata, i, "lessThen");
}
for (int i = 0; i < condition.getGreaterEqualsList().size(); i++) {
checkEqualsTypeCondition(condition.getGreaterEquals(i), condition, indice, metadata, i, "greaterEquals");
}
for (int i = 0; i < condition.getGreaterThenList().size(); i++) {
checkEqualsTypeCondition(condition.getGreaterThen(i), condition, indice, metadata, i, "greaterThen");
}
for (int i = 0; i < condition.getStartsWithList().size(); i++) {
checkEqualsTypeCondition(condition.getStartsWith(i), condition, indice, metadata, i, "startsWith");
}
for (int i = 0; i < condition.getEndsWithList().size(); i++) {
checkEqualsTypeCondition(condition.getEndsWith(i), condition, indice, metadata, i, "endsWith");
}
for (int i = 0; i < condition.getSelectedList().size(); i++) {
Selected selected = condition.getSelected(i);
if(!itemDefined(selected.getName(),metadata))
throw new ValidationException("L'elemento Selected numero ["+(i+1)+"] della Condition numero ["
+indice+"] non e' valido: si riferisce ad un elemento ["+selected.getName()+"] non presente nella configurazione.");
BaseItemBean<?> itemToCheck = metadata.getItem(selected.getName());
ItemType itemToCheckType = itemToCheck.getItemType();
// la condizione selected si puo' attivare su un elemento checkbox
if(!itemToCheckType.equals(ItemType.CHECKBOX)
&& !itemToCheckType.equals(ItemType.HIDDEN) // aggiunto hidden perche' puo' essere utile quando si disattiva un checbox ma non si vuole eliminare il codice
) {
throw new ValidationException("L'elemento Selected numero ["+(i+1)+"] della Condition numero ["
+indice+"] non e' valido: si riferisce ad un elemento ["+selected.getName()+"] che non e' di tipo CheckBox");
}
}
}
private static void checkEqualsTypeCondition(Equals equals, Condition condition, int indice,ConfigBean metadata, int i, String tipo) throws ValidationException {
if(!itemDefined(equals.getName(),metadata))
throw new ValidationException("L'elemento '"+tipo+"' numero ["+(i+1)+"] della Condition numero ["
+indice+"] non e' valido: si riferisce ad un elemento ["+equals.getName()+"] non presente nella configurazione.");
BaseItemBean<?> itemToCheck = metadata.getItem(equals.getName());
ItemType itemToCheckType = itemToCheck.getItemType();
// la condizione equals non si puo' attivare su un elemento checkbox
if(itemToCheckType.equals(ItemType.CHECKBOX)) {
throw new ValidationException("L'elemento '"+tipo+"' numero ["+(i+1)+"] della Condition numero ["
+indice+"] non e' valido: si riferisce ad un elemento ["+equals.getName()+"] che ha un tipo non compatibile con la regola, non si puo' controllare se una CheckBox e' Equals");
}
}
private static boolean itemDefined(String itemName, ConfigBean metadata) {
return metadata.getListakeys().contains(itemName);
}
public static ConfigBean getMetadata(Config config) throws ValidationException{
IProvider provider = null;
if(StringUtils.isNotEmpty(config.getProvider())) {
try {
provider = (IProvider) ClassLoaderUtilities.newInstance(config.getProvider());
}catch(Exception e) {
throw new ValidationException("Errore durante l'istanziazione del provider ["+config.getProvider()+"]: "+e.getMessage(),e);
}
}
ConfigBean cbTmp = new ConfigBean(provider);
cbTmp.setId(config.getId());
org.openspcoop2.core.mvc.properties.Properties properties = config.getProperties();
if(properties != null) {
List<Collection> collectionList = properties.getCollectionList();
for (Collection collection : collectionList) {
cbTmp.getListaNomiProperties().add(collection.getName());
}
}
List<Section> sectionList = config.getSectionList();
for (int i= 0; i < sectionList.size() ; i++) {
Section section = sectionList.get(i);
getMetadataSection(section,"s"+i,cbTmp);
}
return cbTmp;
}
private static void getMetadataSection(Section section, String sectionIdx,ConfigBean cbTmp) throws ValidationException{
SectionBean sectionBean = new SectionBean(section,sectionIdx, cbTmp.getProvider());
cbTmp.addItem(sectionBean);
if(section.getItemList() != null) {
for (Item item : section.getItemList()) {
ItemBean itemBean = new ItemBean(item, item.getName(), cbTmp.getProvider());
cbTmp.addItem(itemBean);
}
}
if(section.getSubsectionList() != null) {
for (int i= 0; i < section.getSubsectionList().size() ; i++) {
Subsection subSection = section.getSubsectionList().get(i);
getMetadataSubsection(subSection,sectionIdx+ "_ss"+i,cbTmp);
}
}
}
private static void getMetadataSubsection(Subsection subSection, String subsectionIdx, ConfigBean cbTmp) throws ValidationException {
SubsectionBean subsectionBean = new SubsectionBean(subSection,subsectionIdx,cbTmp.getProvider());
cbTmp.addItem(subsectionBean);
if(subSection.getItemList() != null) {
for (Item item : subSection.getItemList()) {
ItemBean itemBean = new ItemBean(item, item.getName(),cbTmp.getProvider());
cbTmp.addItem(itemBean);
}
}
}
}