/*
* 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.net.URI;
import java.util.HashMap;
import java.util.Map;
import javax.xml.namespace.QName;
import com.google.common.collect.ImmutableList;
import eu.esdihumboldt.hale.common.core.io.supplier.Locatable;
import eu.esdihumboldt.hale.common.schema.model.Definition;
import eu.esdihumboldt.hale.common.schema.model.DefinitionUtil;
import eu.esdihumboldt.hale.common.schema.model.constraint.ConstraintUtil;
import eu.esdihumboldt.hale.common.schema.model.constraint.DisplayName;
/**
* Basic definition implementation to be subclassed
*
* @param <C> the supported constraint type
*
* @author Simon Templer
*/
public abstract class AbstractDefinition<C> implements Definition<C> {
/**
* The qualified definition name
*/
protected final QName name;
/**
* The constraints set on the definition
*/
private final Map<Class<? extends C>, C> constraints = new HashMap<Class<? extends C>, C>();
/**
* The definition description
*/
private String description;
/**
* The definition location
*/
private URI location;
/**
* Creates a new definition with the given name. Description and location
* are not set.
*
* @param name the qualified definition name
*/
public AbstractDefinition(QName name) {
super();
this.name = name;
}
/**
* @see Definition#getConstraint(Class)
*/
@SuppressWarnings("unchecked")
@Override
public <T extends C> T getConstraint(Class<T> constraintType) {
synchronized (constraints) {
C constraint = constraints.get(constraintType);
if (constraint != null) {
return (T) constraint;
}
// support for inherited constraints
T inheritedConstraint = getInheritedConstraint(constraintType);
if (inheritedConstraint != null) {
constraints.put(constraintType, inheritedConstraint);
return inheritedConstraint;
}
// get default constraint and remember it
T defConstraint = ConstraintUtil.getDefaultConstraint(constraintType, this);
constraints.put(constraintType, defConstraint);
return defConstraint;
}
}
/**
* Get the inherited constraint of the given constraint type.<br>
* <br>
* This implementation returns <code>null</code>, as inheritance is not
* supported generally for definitions.
*
* @param constraintType the constraint type
* @return the inherited constraint or <code>null</code> if there is none or
* inheritance is not allowed
*/
protected <T extends C> T getInheritedConstraint(Class<T> constraintType) {
return null;
}
/**
* Determines if the constraint with the given type is set explicitly for
* the definition.
*
* @param constraintType the constraint type
* @return if the constraint is set explicitly
*/
public boolean hasConstraint(Class<? extends C> constraintType) {
synchronized (constraints) {
return constraints.get(constraintType) != null;
}
}
/**
* Set a constraint on the definition
*
* @param constraint the constraint to set
*/
@SuppressWarnings("unchecked")
public void setConstraint(C constraint) {
synchronized (constraints) {
// determine constraint type for constraint object
Class<?> constraintType = ConstraintUtil.getConstraintType(constraint.getClass());
constraints.put((Class<? extends C>) constraintType, constraint);
}
}
/**
* Set a constraint on the definition if none of the same type has been set
* yet.
*
* @param constraint the constraint to set
*/
@SuppressWarnings("unchecked")
public void setConstraintIfNotSet(C constraint) {
if (!hasConstraint((Class<? extends C>) ConstraintUtil.getConstraintType(constraint
.getClass()))) {
setConstraint(constraint);
}
}
@Override
public Iterable<C> getExplicitConstraints() {
return ImmutableList.copyOf(constraints.values());
}
/**
* Set the definition description
*
* @param description the description to set
*/
public void setDescription(String description) {
this.description = description;
}
/**
* Set the definition location
*
* @param location the location to set
*/
public void setLocation(URI location) {
this.location = location;
}
/**
* @see Locatable#getLocation()
*/
@Override
public URI getLocation() {
return location;
}
/**
* Returns the local part of the qualified name. Override to change this
* behavior.
*
* @see Definition#getDisplayName()
*/
@Override
public String getDisplayName() {
// special treatment for DisplayName constraint (as it can't match the
// generic type at this point)
String customName = null;
try {
DisplayName dn = (DisplayName) constraints.get(DisplayName.class);
if (dn != null) {
customName = dn.getCustomName();
}
// creating a default constraint is not done at this point because
// the default behavior of DisplayName is to provide no custom name
} catch (Throwable e) {
// ignore
}
if (customName != null) {
return customName;
}
return name.getLocalPart();
}
/**
* @see Definition#getName()
*/
@Override
public QName getName() {
return name;
}
/**
* @see Definition#getDescription()
*/
@Override
public String getDescription() {
return description;
}
/**
* @see Object#hashCode()
*/
@Override
public int hashCode() {
return name.hashCode();
}
/**
* Two definitions are equal if their name is equal (namespace and local
* part)
*
* @see Object#equals(Object)
*/
@Override
public boolean equals(Object obj) {
if (obj instanceof Definition)
return DefinitionUtil.equal(this, (Definition<?>) obj);
else
return false;
}
/**
* @see Comparable#compareTo(Object)
*/
@Override
public int compareTo(Definition<?> other) {
int result = name.getLocalPart().compareToIgnoreCase(other.getName().getLocalPart());
if (result == 0) {
return name.getNamespaceURI().compareToIgnoreCase(other.getName().getNamespaceURI());
}
return result;
}
/**
* @see Definition#getIdentifier()
*/
@Override
public String getIdentifier() {
return getName().toString();
}
/**
* @see Object#toString()
*/
@Override
public String toString() {
return getIdentifier();
}
}