/*
* Copyright (c) 2016 wetransform GmbH
*
* All rights reserved. This program and the accompanying materials are made
* available under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution. If not, see <http://www.gnu.org/licenses/>.
*
* Contributors:
* wetransform GmbH <http://www.wetransform.to>
*/
package eu.esdihumboldt.hale.ui.common.definition.viewer;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
import eu.esdihumboldt.hale.common.align.model.AlignmentUtil;
import eu.esdihumboldt.hale.common.align.model.EntityDefinition;
import eu.esdihumboldt.hale.common.schema.model.ChildDefinition;
import eu.esdihumboldt.hale.common.schema.model.Definition;
import eu.esdihumboldt.hale.common.schema.model.GroupPropertyDefinition;
import eu.esdihumboldt.hale.common.schema.model.PropertyDefinition;
import eu.esdihumboldt.hale.common.schema.model.constraint.property.Cardinality;
import eu.esdihumboldt.hale.common.schema.model.constraint.property.NillableFlag;
import eu.esdihumboldt.hale.io.xsd.constraint.XmlAttributeFlag;
/**
* Filters that hides optional properties.(Only works for
* {@link EntityDefinition} elements).
*
* @author Arun
*/
public class OptionalPropertiesFilter extends ViewerFilter {
@Override
public boolean select(Viewer viewer, Object parentElement, Object element) {
if (element instanceof TreePath) {
element = ((TreePath) element).getLastSegment();
}
if (element instanceof EntityDefinition) {
EntityDefinition entityDef = (EntityDefinition) element;
if (!entityDef.getPropertyPath().isEmpty()) {
// property or group
// accept if not optional
return !isOptional(entityDef);
}
else {
// accept type entity allways
return true;
}
}
return true;
}
/**
* Determines if all children of the given entity are optional.
*
* @param entityDef the entity definition which children to check
* @return if the entity is optional
*/
private boolean isOptional(EntityDefinition entityDef) {
return isOptional(entityDef, new HashSet<Definition<?>>());
}
/**
* Determines if an entity definition is optional.
*
* @param entityDef the entity definition which children to check
* @param alreadyChecked the set of definitions that have already been
* checked (excluding the given entity)
* @return if the entity is optional
*/
private boolean isOptional(EntityDefinition entityDef, Set<Definition<?>> alreadyChecked) {
Definition<?> def = entityDef.getDefinition();
if (alreadyChecked.contains(def)) {
// could not decide yet if it is optional
// means there was no clearly mandatory property
// -> treat as optional
return true;
}
if (def instanceof GroupPropertyDefinition) {
Cardinality cardinality = ((GroupPropertyDefinition) def)
.getConstraint(Cardinality.class);
if (cardinality.getMinOccurs() != 0
&& !areChildrenOptional(entityDef, alreadyChecked)) {
// not optional if it must occur at least once and children
// are not optional
return false;
}
return true;
}
else if (def instanceof PropertyDefinition) {
Cardinality cardinality = ((PropertyDefinition) def).getConstraint(Cardinality.class);
if (cardinality.getMinOccurs() != 0) {
// if the property must occur it could still be nillable
if (((PropertyDefinition) def).asProperty().getConstraint(NillableFlag.class)
.isEnabled()) {
// if the property is nillable it is not optional if any
// of the children is not optional
if (!areChildrenOptional(entityDef, alreadyChecked)) {
return false;
}
}
else {
// the property must occur and is not nillable
return false;
}
}
return true;
}
return false;
}
/**
* Determines if the given (nillable) entity can be considered optional in
* respect to its children.
*
* @param entityDef the entity definition which children to check
* @param alreadyChecked the set of definitions that have already been
* checked (excluding the given entity)
* @return if all children are optional
*/
private boolean areChildrenOptional(EntityDefinition entityDef,
Set<Definition<?>> alreadyChecked) {
// get children without contexts
Collection<? extends EntityDefinition> children = AlignmentUtil
.getChildrenWithoutContexts(entityDef);
if (children == null || children.isEmpty()) {
return true;
}
for (EntityDefinition child : children) {
Set<Definition<?>> checked = new HashSet<>(alreadyChecked);
checked.add(entityDef.getDefinition());
ChildDefinition<?> childDef = (ChildDefinition<?>) child.getDefinition();
/*
* XML: We only need to check children that are attributes, if they
* are optional.
*/
if (childDef.asProperty() != null
&& childDef.asProperty().getConstraint(XmlAttributeFlag.class).isEnabled()) {
// child is an XML attribute
// need to check if it is optional
if (!isOptional(child, checked)) {
return false;
}
}
/*
* XXX does other special handling need to be done for other kinds
* of schemas?
*/
}
return true;
}
}