/*
* 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.util.definition;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import eu.esdihumboldt.util.Pair;
/**
* Provides support for converting certain objects to a definition string and
* vice versa based on the {@link ObjectDefinition}ies available for the
* supported object type and its sub-types.
*
* @param <T> the supported object type
* @param <D> the supported definition type
*
* @author Simon Templer
*/
public abstract class AbstractObjectFactory<T, D extends ObjectDefinition<? extends T>> {
/**
* The cached sorted definitions.
*/
private Iterable<D> sortedDefinitions;
/**
* Default constructor.
*/
public AbstractObjectFactory() {
super();
}
/**
* Get all available definitions compatible with the supported type.
*
* @return the definitions
*/
protected abstract List<D> getDefinitions();
/**
* Represent the given object as a definition string, so that it can be used
* to again create an object instance using {@link #parse(String)}.
*
* @param <X> the object type, an {@link ObjectDefinition} supporting this
* type must be available
* @param object the object to create a string representation for
* @return the string representation of the object or <code>null</code> if
* no corresponding {@link ObjectDefinition} is available
*
* @see #getDefinitions()
*/
public <X extends T> String asString(X object) {
Pair<String, String> idString = asPair(object);
if (idString != null) {
return idString.getFirst() + ':' + idString.getSecond();
}
return null;
}
/**
* Get the object type identifier and the string representation of the
* object. Please note that the string representation is not the same as the
* definition string retrieved through {@link #asString(Object)}.
*
* @param <X> the object type, an {@link ObjectDefinition} supporting this
* type must be available
* @param object the object to create a string representation for
* @return the object type identifier and string representation of the
* object, <code>null</code> if no corresponding
* {@link ObjectDefinition} is available
*
* @see #getDefinitions()
*/
@SuppressWarnings("unchecked")
public <X extends T> Pair<String, String> asPair(X object) {
// check if the given object is null
if (object == null) {
return null;
}
for (D definition : getSortedDefinitions()) {
if (definition.getObjectClass() == null) {
continue;
}
// comparison based on classes
if (definition.getObjectClass() != null
&& definition.getObjectClass().equals(object.getClass())) {
return new Pair<String, String>(definition.getIdentifier(),
((ObjectDefinition<T>) definition).asString(object));
}
// compare based on interfaces
else if (definition.getObjectClass().isInterface() && compare(object, definition)) {
return new Pair<String, String>(definition.getIdentifier(),
((ObjectDefinition<T>) definition).asString(object));
}
}
return null;
}
/**
* Compares two objects based on there implemented interfaces.
*
* @param object object to check
* @param definition definition to check on
* @return true if they implemented the same interfaces
*/
private boolean compare(T object, D definition) {
if (definition.getObjectClass().isAssignableFrom(object.getClass())) {
return true;
}
return false;
}
/**
* Provides the definitions in a topological order.
*
* @return sorted definitions
*/
private Iterable<D> getSortedDefinitions() {
if (sortedDefinitions == null) {
// create a copy of the definition list to ensure we are dealing
// with the same instances
List<D> list = new ArrayList<D>(getDefinitions());
// result list
ArrayList<D> result = new ArrayList<D>(list.size());
while (!list.isEmpty()) {
Iterator<D> iter = list.iterator();
while (iter.hasNext()) {
D def = iter.next();
boolean isSuper = false;
for (D d : list) {
// skip if it's the same object
if (def.equals(d))
continue;
// check if it's super class
if (def.getObjectClass().isAssignableFrom(d.getObjectClass())) {
isSuper = true;
break;
}
}
/*
* if it's not a super class/interface add it to result and
* remove it from list
*/
if (!isSuper) {
result.add(def);
iter.remove();
}
}
}
sortedDefinitions = result;
}
// return the sorted list
return sortedDefinitions;
}
/**
* Parse the given definition string and create a corresponding object.
*
* @param value the definition string to parse
* @return the created object or <code>null</code>
*/
public T parse(String value) {
if (value == null || value.isEmpty()) {
return null;
}
for (D definition : getDefinitions()) {
String prefix = definition.getIdentifier() + ":"; //$NON-NLS-1$
if (value.startsWith(prefix)) {
String main = value.substring(prefix.length());
return definition.parse(main);
}
}
return null;
}
/**
* Recreate an object from type identifier and string representation as
* retrieved using {@link #asPair(Object)}.
*
* @param typeId the type identifier
* @param value the object string representation
* @return the created object or <code>null</code>
*/
public T from(String typeId, String value) {
for (D definition : getDefinitions()) {
if (definition.getIdentifier().equals(typeId)) {
return definition.parse(value);
}
}
return null;
}
}