/*
* Copyright (c) 2012 Data Harmonisation Panel
*
* 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:
* HUMBOLDT EU Integrated Project #030962
* Data Harmonisation Panel <http://www.dhpanel.eu>
*/
package eu.esdihumboldt.hale.common.schema.model.impl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import javax.xml.namespace.QName;
import eu.esdihumboldt.hale.common.schema.model.ChildDefinition;
import eu.esdihumboldt.hale.common.schema.model.DefinitionGroup;
import eu.esdihumboldt.hale.common.schema.model.DefinitionUtil;
import eu.esdihumboldt.hale.common.schema.model.constraint.property.Cardinality;
import eu.esdihumboldt.hale.common.schema.model.impl.internal.ConstraintOverrideGroupProperty;
import eu.esdihumboldt.hale.common.schema.model.impl.internal.ConstraintOverrideProperty;
/**
* Default {@link DefinitionGroup} implementation used internally in
* {@link DefaultTypeDefinition} and {@link DefaultGroupPropertyDefinition}. It
* has a random UUID as identifier.
*
* @author Simon Templer
*/
public class DefaultGroup implements DefinitionGroup {
private final String identifier;
/**
* The list of declared children (qualified name mapped to child definition,
* LinkedHashMap because order must be maintained for writing)
*/
private final LinkedHashMap<QName, ChildDefinition<?>> declaredChildren = new LinkedHashMap<QName, ChildDefinition<?>>();
private LinkedHashMap<QName, ChildDefinition<?>> flattenedChildren;
private final boolean flatten;
/**
* Create a group
*
* @param identifier the reproducable unique identifier of the group
* @param flatten if contained group properties may be replaced by their
* children if possible
*/
public DefaultGroup(String identifier, boolean flatten) {
super();
this.flatten = flatten;
this.identifier = identifier;
}
/**
* @see DefinitionGroup#getDeclaredChildren()
*/
@Override
public Collection<? extends ChildDefinition<?>> getDeclaredChildren() {
return Collections.unmodifiableCollection(flattenChildren().values());
}
/**
* @see DefinitionGroup#getChild(QName)
*/
@Override
public ChildDefinition<?> getChild(QName name) {
return flattenChildren().get(name);
}
/**
* @see DefinitionGroup#addChild(ChildDefinition)
*/
@Override
public void addChild(ChildDefinition<?> child) {
if (flatten) {
synchronized (this) {
flattenedChildren = null;
}
}
declaredChildren.put(child.getName(), child);
}
/**
* @see DefinitionGroup#getIdentifier()
*/
@Override
public String getIdentifier() {
return identifier;
}
private LinkedHashMap<QName, ChildDefinition<?>> flattenChildren() {
if (flatten) {
synchronized (this) {
if (flattenedChildren == null) {
Collection<? extends ChildDefinition<?>> flat = flattenIfPossible(declaredChildren
.values());
flattenedChildren = new LinkedHashMap<QName, ChildDefinition<?>>();
for (ChildDefinition<?> child : flat) {
flattenedChildren.put(child.getName(), child);
}
}
return flattenedChildren;
}
}
else {
return declaredChildren;
}
}
/**
* Replace groups with their children where possible
*
* @param children the children
* @return the flattened children
*/
private Collection<? extends ChildDefinition<?>> flattenIfPossible(
Collection<? extends ChildDefinition<?>> children) {
Collection<ChildDefinition<?>> result = new ArrayList<ChildDefinition<?>>();
for (ChildDefinition<?> child : children) {
boolean skipAdd = false;
if (child.asGroup() != null) {
if (child.asGroup().allowFlatten()) {
// prevent the group from being added
skipAdd = true;
// replace group with children
for (ChildDefinition<?> groupChild : child.asGroup().getDeclaredChildren()) {
result.add(DefinitionUtil.redeclareChild(groupChild, child.asGroup()
.getDeclaringGroup()));
}
}
else if (child.asGroup().getDeclaredChildren().size() == 1) { // special
// case:
// group
// has
// exactly
// one
// child
// check the cardinality of the group child
ChildDefinition<?> groupChild = child.asGroup().getDeclaredChildren()
.iterator().next();
Cardinality gcc = null;
if (groupChild.asProperty() != null) {
gcc = groupChild.asProperty().getConstraint(Cardinality.class);
}
else if (groupChild.asGroup() != null) {
gcc = groupChild.asGroup().getConstraint(Cardinality.class);
}
if (gcc != null && gcc.getMinOccurs() == 1 && gcc.getMaxOccurs() == 1) {
// the cardinality of the group child is exactly one
// it can take on the group cardinality and replace the
// group
// get group cardinality
Cardinality groupCardinality = child.asGroup().getConstraint(
Cardinality.class);
// redeclare group child
ChildDefinition<?> redeclaredChild = DefinitionUtil.redeclareChild(
groupChild, child.asGroup().getDeclaringGroup());
// set group cardinality on child
if (redeclaredChild.asGroup() != null) {
redeclaredChild = new ConstraintOverrideGroupProperty(
redeclaredChild.asGroup(), groupCardinality);
}
else if (redeclaredChild.asProperty() != null) {
redeclaredChild = new ConstraintOverrideProperty(
redeclaredChild.asProperty(), groupCardinality);
}
// prevent the group from being added
skipAdd = true;
// add child
result.add(redeclaredChild);
}
}
}
if (!skipAdd) {
// add child as is
result.add(child);
}
}
return result;
}
}