/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2002-2008, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotools.xml;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EFactory;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
/**
* Utility methods for working with emf model objects.
*
* @author Justin Deoliveira, The Open Planning Project, jdeolive@openplans.org
*
*
*
* @source $URL$
*/
public class EMFUtils {
/**
* Determines if an eobject has a particular property.
*
* @param eobject The eobject.
* @param property The property to check for.
*
* @return <code>true</code> if the property exists, otherwise <code>false</code.
*/
public static boolean has(EObject eobject, String property) {
return feature(eobject, property) != null;
}
/**
* Sets a property of an eobject.
*
* @param eobject THe object.
* @param property The property to set.
* @param value The value of the property.
*/
public static void set(EObject eobject, String property, Object value) {
EStructuralFeature feature = feature(eobject, property);
eobject.eSet(feature, value);
}
/**
* Gets the property of an eobject.
*
* @param eobject The object.
* @param property The property to get.
*
* @return The value of the property.
*/
public static Object get(EObject eobject, String property) {
EStructuralFeature feature = feature(eobject, property);
return eobject.eGet(feature);
}
/**
* Adds a value to a multi-valued propert of an eobject.
* <p>
* The <param>property</param> must map to a multi-valued property of the
* eobject. The {@link #isCollection(EObject, String)} method can be used
* to test this.
* </p>
*
* @param eobject The object.
* @param property The multi-valued property.
* @param value The value to add.
*/
public static void add(EObject eobject, String property, Object value) {
EStructuralFeature feature = feature(eobject, property);
if ((feature != null)) {
add(eobject, feature, value);
}
}
/**
* Adds a value to a multi-valued propert of an eobject.
* <p>
* The <param>feature</param> must map to a multi-valued property of the
* eobject. The {@link #isCollection(EStructuralFeature)} method can be used
* to test this.
* </p>
*
* @param eobject The object.
* @param feature The multi-valued feature.
* @param value The value to add.
*/
public static void add(EObject eobject, EStructuralFeature feature, Object value) {
if (isCollection(eobject, feature)) {
Collection collection = (Collection) eobject.eGet(feature);
if (collection == null) {
//most likely not an ECollection
collection = createEmptyCollection(feature);
eobject.eSet(feature, collection);
}
collection.addAll(collection(value));
}
}
static Collection createEmptyCollection(EStructuralFeature feature) {
Class clazz = feature.getEType().getInstanceClass();
if (EList.class.isAssignableFrom(clazz)) {
return new BasicEList();
}
if (List.class.isAssignableFrom(clazz)) {
return new ArrayList();
}
if (Set.class.isAssignableFrom(clazz)) {
return new HashSet();
}
throw new IllegalArgumentException("Unable to create collection for " + clazz);
}
/**
* Returns a collection view for value, taking care of the case where value
* is of an array type, in which case the collection returned contains the
* array elements, not the array itself.
*
* @param value a value to be added to an EObject collection property
* @return value wrapped in a collection, or a collection containing the
* array elements in case value is an array.
* @see #add(EObject, String, Object)
*/
private static Collection collection(Object value) {
if (null == value) {
return Collections.EMPTY_LIST;
} else if (value.getClass().isArray()) {
final int len = java.lang.reflect.Array.getLength(value);
List list = new ArrayList(len);
for (int i = 0; i < len; i++) {
Object val = Array.get(value, i);
list.add(val);
}
return list;
} else if (value instanceof Collection) {
return (Collection) value;
}
return Collections.singletonList(value);
}
/**
* Determines if a property of an eobject is a collection.
* <p>
* In the event the property does not exist, this method will return
* <code>false</code>
* </p>
*
* @return <code>true</code> if hte property is a collection, otherwise
* <code>false</code>
*/
public static boolean isCollection(EObject eobject, String property) {
Object o = get(eobject, property);
//first check the actual value
if (o != null) {
return o instanceof Collection;
}
//value was null, try checking the class
EStructuralFeature feature = feature(eobject, property);
if (feature == null) {
return false;
}
return isCollection(eobject, feature);
}
/**
* Determines if a feature of an eobject is a collection.
*
* @return <code>true</code> if the feature is a collection, otherwise
* <code>false</code>
*/
public static boolean isCollection(EObject eobject, EStructuralFeature feature) {
Object o = eobject.eGet(feature);
if (o != null) {
return o instanceof Collection;
}
if (Collection.class.isAssignableFrom(feature.getEType().getInstanceClass())) {
return true;
}
return false;
}
/**
* Method which looks up a structure feature of an eobject, first doing
* an exact name match, then a case insensitive one.
*
* @param eobject The eobject.
* @param property The property
*
* @return The structure feature, or <code>null</code> if not found.
*/
public static EStructuralFeature feature(EObject eobject, String property) {
EStructuralFeature feature = eobject.eClass().getEStructuralFeature(property);
if (feature != null) {
return feature;
}
//do a case insentive check, need to do the walk up the type hierarchy
for (Iterator itr = eobject.eClass().getEAllStructuralFeatures().iterator(); itr.hasNext();) {
feature = (EStructuralFeature) itr.next();
if (feature.getName().equalsIgnoreCase(property)) {
return feature;
}
}
return null;
}
/**
* Method which looks up structural features of an eobject by type.
*
* @param eobject The eobject.
* @param propertyType The type of the properties.
*
* @return The list of structure features, or an empty list if none are found.
*/
public static List /*<EStructuralFeature>*/ features(EObject eobject, Class propertyType) {
List match = new ArrayList();
List features = eobject.eClass().getEAllStructuralFeatures();
for (Iterator itr = features.iterator(); itr.hasNext();) {
EStructuralFeature feature = (EStructuralFeature) itr.next();
if (feature.getEType().getInstanceClass().isAssignableFrom(propertyType)) {
match.add(feature);
}
}
return match;
}
/**
* Sets a particular property on each {@link EObject} in a list to a particular value.
* <p>
* The following must hold:
* <code>
* objects.size() == values.size()
* </code>
* </p>
*
* @param objects A list of {@link EObject}.
* @param property The property to set on each eobject in <code>objects</code>
* @param values The value to set on each eobjct in <code>objects</code>
*/
public static void set(List objects, String property, List values) {
for (int i = 0; i < objects.size(); i++) {
EObject eobject = (EObject) objects.get(i);
set(eobject, property, values.get(i));
}
}
/**
* Sets a particular property on each {@link EObject} in a list to a particular value.
* <p>
*
* @param objects A list of {@link EObject}.
* @param property The property to set on each eobject in <code>objects</code>
* @param value The value to set on each eobjct in <code>objects</code>
*/
public static void set(List objects, String property, Object value) {
for (int i = 0; i < objects.size(); i++) {
EObject eobject = (EObject) objects.get(i);
set(eobject, property, value);
}
}
/**
* Obtains the values of a particular property on each {@link EObject} in a list.
*
* @param objects A list of {@link EObject}.
* @param property The property to obtain.
*
* @return The list of values.
*/
public static List get(List objects, String property) {
List values = new ArrayList();
for (int i = 0; i < objects.size(); i++) {
EObject eobject = (EObject) objects.get(i);
EStructuralFeature feature = feature(eobject, property);
values.add(eobject.eGet(feature));
}
return values;
}
/**
* Determines if a particular propety has been set on an eobject.
*
* @param eobjects The eobject.
* @param property The property to check.
*
* @return <code>true</code> if the property has been set, otherwise <code>false</code>
*/
public static boolean isSet(EObject eobject, String property) {
EStructuralFeature feature = feature(eobject, property);
return eobject.eIsSet(feature);
}
/**
* Determines if a particular propety has been set on each {@link EObject} in a list.
*
* @param objects A list of {@link EObject}
* @param property The property to check.
*
* @return <code>true</code> if every element in the list has been set, otherwise <code>false</code>
*/
public static boolean isSet(List objects, String property) {
for (int i = 0; i < objects.size(); i++) {
EObject eobject = (EObject) objects.get(i);
if (!isSet(eobject, property)) {
return false;
}
}
return true;
}
/**
* Determines if a particular propety is unset on each {@link EObject} in a list.
*
* @param objects A list of {@link EObject}
* @param property The property to check.
*
* @return <code>true</code> if every element in the list is unset, otherwise <code>false</code>
*/
public static boolean isUnset(List objects, String property) {
for (int i = 0; i < objects.size(); i++) {
EObject eobject = (EObject) objects.get(i);
if (isSet(eobject, property)) {
return false;
}
}
return true;
}
/**
* Clones an eobject.
*
* @param prototype The object to be cloned from.
* @param factory The factory used to create the clone.
*
* @return THe cloned object, with all properties the same to the original.
* @deprecated use {@link #clone(EObject, EFactory, boolean)}.
*/
public static EObject clone(EObject prototype, EFactory factory) {
return clone( prototype, factory, false );
}
/**
* Clones an eobject, with the option of performing a deep clone in which referenced eobjects
* are also cloned.
*
* @param prototype The object to be cloned from.
* @param factory The factory used to create the clone.
* @param deep�Flag indicating wether to perform a deep clone.
*
* @return THe cloned object, with all properties the same to the original.
*/
public static EObject clone(EObject prototype, EFactory factory, boolean deep) {
EObject clone = factory.create(prototype.eClass());
for (Iterator i = clone.eClass().getEStructuralFeatures().iterator(); i.hasNext();) {
EStructuralFeature feature = (EStructuralFeature) i.next();
Object value = prototype.eGet(feature);
if ( deep && value instanceof EObject ) {
EObject evalue = (EObject) value;
//recursively call
//TODO:handle cycles in reference graph
value = clone( evalue, evalue.eClass().getEPackage().getEFactoryInstance(), deep );
}
clone.eSet(feature, value );
}
return clone;
}
/**
* Copies all the properties from one object to anoter.
*
* @param source The object to copy from.
* @param target The object to copy to.
*/
public static void copy(EObject source, EObject target) {
for (Iterator i = source.eClass().getEStructuralFeatures().iterator(); i.hasNext();) {
EStructuralFeature feature = (EStructuralFeature) i.next();
target.eSet(feature, source.eGet(feature));
}
}
}