package eu.eudat.service.visibility; import eu.eudat.commons.types.description.PropertyDefinitionEntity; import eu.eudat.commons.types.descriptiontemplate.*; import eu.eudat.model.persist.descriptionproperties.PropertyDefinitionPersist; import javax.ws.rs.NotSupportedException; import java.util.*; public class VisibilityServiceImpl implements VisibilityService { private final DefinitionEntity definition; private final PropertyDefinition propertyDefinition; private Map> rulesBySources; private Map visibility; public VisibilityServiceImpl(DefinitionEntity definition, PropertyDefinitionPersist propertyDefinition) { this.definition = definition; this.propertyDefinition = new PropertyDefinition(propertyDefinition); } public VisibilityServiceImpl(DefinitionEntity definition, PropertyDefinitionEntity propertyDefinition) { this.definition = definition; this.propertyDefinition = new PropertyDefinition(propertyDefinition); } private void initRules(){ if (this.rulesBySources == null) return; rulesBySources = new HashMap<>(); for (FieldEntity fieldEntity : this.definition.getAllField()){ if (fieldEntity.getVisibilityRules() != null && !fieldEntity.getVisibilityRules().isEmpty()) { for (RuleEntity rule : fieldEntity.getVisibilityRules()){ if (!rulesBySources.containsKey(fieldEntity.getId())) rulesBySources.put(fieldEntity.getId(), new ArrayList<>()); RuleWithTarget ruleWithTarget = new RuleWithTarget(fieldEntity.getId(), rule, fieldEntity); rulesBySources.get(fieldEntity.getId()).add(ruleWithTarget); } } } } @Override public boolean isVisible(String id, Integer ordinal) { this.calculateVisibility(); FieldKey fieldKey = new FieldKey(id, ordinal); return this.visibility.getOrDefault(fieldKey, false); } @Override public Map getVisibilityStates() { this.calculateVisibility(); return visibility; } private void calculateVisibility(){ if (visibility != null) return; this.initRules(); this.buildTargetVisibility(); this.expandVisibilityToChildren(); this.setDefaultVisibilityForNotCaclucted(); this.hideParentIfAllChildrenAreHidden(); } private void buildTargetVisibility(){ visibility = new HashMap<>(); for (Map.Entry> ruleForSource : rulesBySources.entrySet()){ for (RuleWithTarget rule : ruleForSource.getValue()){ if (propertyDefinition.getFieldSets() != null && !propertyDefinition.getFieldSets().isEmpty()){ for (PropertyDefinitionFieldSet propertyDefinitionFieldSet: propertyDefinition.getFieldSets().values()) { if (propertyDefinitionFieldSet.getItems() != null && !propertyDefinitionFieldSet.getItems().isEmpty()) { for (PropertyDefinitionFieldSetItem definitionFieldSetItem : propertyDefinitionFieldSet.getItems()) { if (definitionFieldSetItem.getFields() != null && !definitionFieldSetItem.getFields().isEmpty()) { for (String key : definitionFieldSetItem.getFields().keySet()) { if (rule.getSource().equals(key)){ Field field = definitionFieldSetItem.getFields().get(key); if (definitionFieldSetItem.getFields().containsKey(rule.getTarget())){ //Rule applies only for current multiple item FieldKey fieldKey = new FieldKey(rule.getTarget(), definitionFieldSetItem.getOrdinal()); boolean currentState = this.visibility.getOrDefault(fieldKey, false); this.visibility.put(fieldKey, currentState || ruleIsTrue(rule, field)); } else if (!this.definition.getFieldById(rule.getTarget()).isEmpty() || !this.definition.getFieldSetById(rule.getTarget()).isEmpty()) { //Rule applies to different fieldset, so we apply for all multiple items List ordinals = this.getKeyOrdinals(rule.getTarget()); for (Integer ordinal : ordinals){ FieldKey fieldKey = new FieldKey(rule.getTarget(), ordinal); boolean currentState = this.visibility.getOrDefault(fieldKey, false); this.visibility.put(fieldKey, currentState || ruleIsTrue(rule, field)); } } else { FieldKey fieldKey = new FieldKey(rule.getTarget(), null); //Ordinal is null if target not on field boolean currentState = this.visibility.getOrDefault(fieldKey, false); this.visibility.put(fieldKey, currentState || ruleIsTrue(rule, field)); } } } } } } } } } } } private List getKeyOrdinals(String key){ if (propertyDefinition.getFieldSets() != null && !propertyDefinition.getFieldSets().isEmpty()){ for (Map.Entry propertyDefinitionFieldSet: propertyDefinition.getFieldSets().entrySet()) { if (propertyDefinitionFieldSet.getKey().equals(key)) return propertyDefinitionFieldSet.getValue().getItems().stream().map(PropertyDefinitionFieldSetItem::getOrdinal).toList(); if (propertyDefinitionFieldSet.getValue() != null && propertyDefinitionFieldSet.getValue().getItems() != null && !propertyDefinitionFieldSet.getValue().getItems().isEmpty()) { for (PropertyDefinitionFieldSetItem definitionFieldSetItem : propertyDefinitionFieldSet.getValue().getItems()) { if (definitionFieldSetItem.getFields() != null && !definitionFieldSetItem.getFields().isEmpty()) { for (String fieldKey : definitionFieldSetItem.getFields().keySet()) { if (fieldKey.equals(key)) return propertyDefinitionFieldSet.getValue().getItems().stream().map(PropertyDefinitionFieldSetItem::getOrdinal).toList(); } } } } } } return new ArrayList<>(); } private void expandVisibilityToChildren(){ if (this.definition.getPages() == null) return; for (PageEntity pageEntity : this.definition.getPages()){ FieldKey fieldKey = new FieldKey(pageEntity.getId(), null); Boolean currentValue = this.visibility.getOrDefault(fieldKey, null); this.expandPageVisibility(pageEntity, currentValue); } } private void expandPageVisibility(PageEntity pageEntity, Boolean parentVisibility){ if (pageEntity.getSections() == null) return; for (SectionEntity sectionEntity : pageEntity.getSections()){ FieldKey fieldKey = new FieldKey(sectionEntity.getId(), null); Boolean currentValue = this.visibility.getOrDefault(fieldKey, null); if (currentValue != null){ if (parentVisibility != null && !parentVisibility) { //Parent is hidden so all childs should be hidden this.visibility.put(fieldKey, false); this.expandSectionVisibility(sectionEntity, currentValue); } else { this.expandSectionVisibility(sectionEntity, currentValue); } } else { if (parentVisibility != null) this.visibility.put(fieldKey, parentVisibility); this.expandSectionVisibility(sectionEntity, parentVisibility); } } } private void expandSectionVisibility(SectionEntity sectionEntity, Boolean parentVisibility){ if (sectionEntity.getSections() != null) { for (SectionEntity subSectionEntity : sectionEntity.getSections()) { FieldKey fieldKey = new FieldKey(subSectionEntity.getId(), null); Boolean currentValue = this.visibility.getOrDefault(fieldKey, null); if (currentValue != null){ if (parentVisibility != null && !parentVisibility) { //Parent is hidden so all childs should be hidden this.visibility.put(fieldKey, false); this.expandSectionVisibility(subSectionEntity, currentValue); } else { this.expandSectionVisibility(subSectionEntity, currentValue); } } else { if (parentVisibility != null) this.visibility.put(fieldKey, parentVisibility); this.expandSectionVisibility(subSectionEntity, parentVisibility); } } } if (sectionEntity.getFieldSets() != null) { for (FieldSetEntity fieldSetEntity : sectionEntity.getFieldSets()) { if (propertyDefinition.getFieldSets() != null && !propertyDefinition.getFieldSets().isEmpty()){ PropertyDefinitionFieldSet propertyDefinitionFieldSet = propertyDefinition.getFieldSets().getOrDefault(fieldSetEntity.getId(), null); if (propertyDefinitionFieldSet != null && propertyDefinitionFieldSet.getItems() != null && !propertyDefinitionFieldSet.getItems().isEmpty()) { for (PropertyDefinitionFieldSetItem definitionFieldSetItem : propertyDefinitionFieldSet.getItems()) { FieldKey fieldKey = new FieldKey(fieldSetEntity.getId(), definitionFieldSetItem.getOrdinal()); Boolean currentValue = this.visibility.getOrDefault(fieldKey, null); if (currentValue != null){ if (parentVisibility != null && !parentVisibility) { //Parent is hidden so all childs should be hidden this.visibility.put(fieldKey, false); this.expandFieldSetVisibility(fieldSetEntity, currentValue, definitionFieldSetItem.getOrdinal()); } else { this.expandFieldSetVisibility(fieldSetEntity, currentValue, definitionFieldSetItem.getOrdinal()); } } else { if (parentVisibility != null) this.visibility.put(fieldKey, parentVisibility); this.expandFieldSetVisibility(fieldSetEntity, parentVisibility, definitionFieldSetItem.getOrdinal()); } } } } } } } private void expandFieldSetVisibility(FieldSetEntity fieldSetEntity, Boolean parentVisibility, Integer ordinal){ if (fieldSetEntity.getFields() != null) { for (FieldEntity fieldEntity : fieldSetEntity.getFields()) { FieldKey fieldKey = new FieldKey(fieldEntity.getId(), ordinal); Boolean currentValue = this.visibility.getOrDefault(fieldKey, null); if (currentValue != null){ if (parentVisibility != null && !parentVisibility) { //Parent is hidden so all childs should be hidden this.visibility.put(fieldKey, false); } } else if (parentVisibility != null){ this.visibility.put(fieldKey, parentVisibility); } } } } private void setDefaultVisibilityForNotCaclucted(){ if (this.definition.getPages() == null) return; for (PageEntity pageEntity : this.definition.getPages()){ FieldKey fieldKey = new FieldKey(pageEntity.getId(), null); Boolean currentValue = this.visibility.getOrDefault(fieldKey, null); if (currentValue == null) this.visibility.put(fieldKey, true); this.setDefaultPageVisibility(pageEntity); } } private void setDefaultPageVisibility(PageEntity pageEntity){ if (pageEntity.getSections() == null) return; for (SectionEntity sectionEntity : pageEntity.getSections()){ FieldKey fieldKey = new FieldKey(sectionEntity.getId(), null); Boolean currentValue = this.visibility.getOrDefault(fieldKey, null); if (currentValue == null){ this.visibility.put(fieldKey, true); this.setDefaultSectionVisibility(sectionEntity); } } } private void setDefaultSectionVisibility(SectionEntity sectionEntity){ if (sectionEntity.getSections() != null) { for (SectionEntity subSectionEntity : sectionEntity.getSections()) { FieldKey fieldKey = new FieldKey(subSectionEntity.getId(), null); Boolean currentValue = this.visibility.getOrDefault(fieldKey, null); if (currentValue == null){ this.visibility.put(fieldKey, true); this.setDefaultSectionVisibility(sectionEntity); } } } if (sectionEntity.getFieldSets() != null) { for (FieldSetEntity fieldSetEntity : sectionEntity.getFieldSets()) { if (propertyDefinition.getFieldSets() != null && !propertyDefinition.getFieldSets().isEmpty()){ PropertyDefinitionFieldSet propertyDefinitionFieldSet = propertyDefinition.getFieldSets().getOrDefault(fieldSetEntity.getId(), null); if (propertyDefinitionFieldSet != null && propertyDefinitionFieldSet.getItems() != null && !propertyDefinitionFieldSet.getItems().isEmpty()) { for (PropertyDefinitionFieldSetItem definitionFieldSetItem : propertyDefinitionFieldSet.getItems()) { FieldKey fieldKey = new FieldKey(fieldSetEntity.getId(), definitionFieldSetItem.getOrdinal()); Boolean currentValue = this.visibility.getOrDefault(fieldKey, null); if (currentValue == null){ this.visibility.put(fieldKey, true); this.setDefaultFieldSetVisibility(fieldSetEntity, definitionFieldSetItem.getOrdinal()); } } } } } } } private void setDefaultFieldSetVisibility(FieldSetEntity fieldSetEntity, Integer ordinal){ if (fieldSetEntity.getFields() != null) { for (FieldEntity fieldEntity : fieldSetEntity.getFields()) { FieldKey fieldKey = new FieldKey(fieldEntity.getId(), ordinal); Boolean currentValue = this.visibility.getOrDefault(fieldKey, null); if (currentValue == null){ this.visibility.put(fieldKey, true); } } } } private void hideParentIfAllChildrenAreHidden(){ if (this.definition.getPages() == null) return; for (PageEntity pageEntity : this.definition.getPages()){ FieldKey fieldKey = new FieldKey(pageEntity.getId(), null); boolean isCurrentHidden = this.isHiddenPageVisibilityIfAllChildrenIsHidden(pageEntity); if (this.visibility.getOrDefault(fieldKey, true) && isCurrentHidden){ this.visibility.put(fieldKey, false); } } } private boolean isHiddenPageVisibilityIfAllChildrenIsHidden(PageEntity pageEntity){ boolean isHidden = true; if (pageEntity.getSections() == null) return isHidden; for (SectionEntity sectionEntity : pageEntity.getSections()){ FieldKey fieldKey = new FieldKey(sectionEntity.getId(), null); boolean isCurrentHidden = this.isHiddenSectionIfAllChildrenIsHidden(sectionEntity); if (this.visibility.getOrDefault(fieldKey, true) && isCurrentHidden){ this.visibility.put(fieldKey, false); } isHidden = isHidden && isCurrentHidden; } return isHidden; } private boolean isHiddenSectionIfAllChildrenIsHidden(SectionEntity sectionEntity){ boolean isHidden = true; if (sectionEntity.getSections() != null) { for (SectionEntity subSectionEntity : sectionEntity.getSections()) { FieldKey fieldKey = new FieldKey(subSectionEntity.getId(), null); boolean isCurrentHidden = this.isHiddenSectionIfAllChildrenIsHidden(subSectionEntity); if (this.visibility.getOrDefault(fieldKey, true) && isCurrentHidden){ this.visibility.put(fieldKey, false); } isHidden = isHidden && isCurrentHidden; } } if (sectionEntity.getFieldSets() != null) { for (FieldSetEntity fieldSetEntity : sectionEntity.getFieldSets()) { if (propertyDefinition.getFieldSets() != null && !propertyDefinition.getFieldSets().isEmpty()){ PropertyDefinitionFieldSet propertyDefinitionFieldSet = propertyDefinition.getFieldSets().getOrDefault(fieldSetEntity.getId(), null); if (propertyDefinitionFieldSet != null && propertyDefinitionFieldSet.getItems() != null && !propertyDefinitionFieldSet.getItems().isEmpty()) { for (PropertyDefinitionFieldSetItem definitionFieldSetItem : propertyDefinitionFieldSet.getItems()) { FieldKey fieldKey = new FieldKey(fieldSetEntity.getId(), definitionFieldSetItem.getOrdinal()); boolean isCurrentHidden = this.isHiddenFieldSetIfAllChildrenIsHidden(fieldSetEntity, definitionFieldSetItem.getOrdinal()); if (this.visibility.getOrDefault(fieldKey, true) && isCurrentHidden){ this.visibility.put(fieldKey, false); } isHidden = isHidden && isCurrentHidden; } } } } } return isHidden; } private boolean isHiddenFieldSetIfAllChildrenIsHidden(FieldSetEntity fieldSetEntity, Integer ordinal){ boolean isHidden = true; if (fieldSetEntity.getFields() != null) { for (FieldEntity fieldEntity : fieldSetEntity.getFields()) { FieldKey fieldKey = new FieldKey(fieldEntity.getId(), ordinal); Boolean currentValue = this.visibility.getOrDefault(fieldKey, true); isHidden = isHidden && !currentValue; } return isHidden; } return true; } private boolean ruleIsTrue(RuleWithTarget rule, Field field){ if (field != null){ eu.eudat.commons.enums.FieldType fieldType = rule.getFieldEntity() != null && rule.getFieldEntity().getData() != null ? rule.getFieldEntity().getData().getFieldType() : eu.eudat.commons.enums.FieldType.FREE_TEXT; if (eu.eudat.commons.enums.FieldType.isTextType(fieldType) && field.getTextValue() != null && !field.getTextValue().isBlank()) { if (eu.eudat.commons.enums.FieldType.UPLOAD.equals(fieldType)){ throw new NotSupportedException("Upload file rule not supported"); } else { return field.getTextValue().equals(rule.getTextValue()); } } else if (eu.eudat.commons.enums.FieldType.isTextListType(fieldType) && field.getTextListValue() != null && !field.getTextListValue().isEmpty()) { return rule.getTextListValue() != null && new HashSet<>(field.getTextListValue()).containsAll(rule.getTextListValue()) && new HashSet<>(rule.getTextListValue()).containsAll(field.getTextListValue()); } else if (eu.eudat.commons.enums.FieldType.isReferenceType(fieldType)) { return rule.getTextListValue() != null && new HashSet<>(field.getTextListValue()).containsAll(rule.getTextListValue()) && new HashSet<>(rule.getTextListValue()).containsAll(field.getTextListValue()); } else if (eu.eudat.commons.enums.FieldType.isDateType(fieldType) && field.getDateValue() != null) return field.getDateValue().equals(rule.getDateValue()); else if (eu.eudat.commons.enums.FieldType.isExternalIdentifierType(fieldType) && field.getExternalIdentifier() != null) { throw new NotSupportedException("External identifier rule not supported"); } } return false; } }