/** * Copyright 2005-2014 Restlet * * The contents of this file are subject to the terms of one of the following * open source licenses: Apache 2.0 or or EPL 1.0 (the "Licenses"). You can * select the license that you prefer but you may not use this file except in * compliance with one of these Licenses. * * You can obtain a copy of the Apache 2.0 license at * http://www.opensource.org/licenses/apache-2.0 * * You can obtain a copy of the EPL 1.0 license at * http://www.opensource.org/licenses/eclipse-1.0 * * See the Licenses for the specific language governing permissions and * limitations under the Licenses. * * Alternatively, you can obtain a royalty free commercial license with less * limitations, transferable or non-transferable, directly at * http://restlet.com/products/restlet-framework * * Restlet is a registered trademark of Restlet S.A.S. */ package org.restlet.ext.odata.internal.edm; import java.io.IOException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.restlet.data.Reference; import org.restlet.ext.xml.SaxRepresentation; import org.restlet.representation.Representation; /** * Represents the metadata of an OData service. * * @author Thierry Boileau */ public class Metadata extends SaxRepresentation { /** The list of entity containers. */ private List<EntityContainer> containers; /** The list of declared property mappings. */ private List<Mapping> mappings; /** The URI of the metadata resource. */ private Reference metadataRef; /** The data services schemas. */ private List<Schema> schemas; /** * Constructor. * * @param metadata * The representation of the metadata. * @param metadataRef * Its optional URI. * @throws IOException */ public Metadata(Representation metadata, Reference metadataRef) throws IOException { super(metadata); setNamespaceAware(true); this.metadataRef = metadataRef; parse(new MetadataReader(this)); } /** * Returns the Association object that corresponds to the given property of * the given entity type. * * @param type * The entity type. * @param propertyName * The property name. * @return the Association object that corresponds to the given property of * the given entity type. */ public AssociationEnd getAssociation(EntityType type, String propertyName) { AssociationEnd result = null; for (NavigationProperty association : type.getAssociations()) { if (association.getNormalizedName().equals(propertyName)) { result = association.getToRole(); break; } } return result; } /** * Returns the complectType that corresponds to a given entity class. * * @param entityClass * The entity class. * @return The ComplexType that corresponds to a given entity class. */ public ComplexType getComplexType(Class<?> entityClass) { ComplexType result = null; // Try to match the entity class names (without package); String className = entityClass.getName(); int index = className.lastIndexOf("."); if (index != -1) { className = className.substring(index + 1); } for (Iterator<Schema> iec = getSchemas().iterator(); result == null && iec.hasNext();) { Schema schema = iec.next(); for (Iterator<ComplexType> ies = schema.getComplexTypes() .iterator(); result == null && ies.hasNext();) { ComplexType type = ies.next(); if (type.getClassName().equals(className)) { result = type; } } } return result; } /** * Returns the list of entity containers. * * @return The list of entity containers. */ public List<EntityContainer> getContainers() { if (containers == null) { containers = new ArrayList<EntityContainer>(); } return containers; } /** * Returns the subpath of the entitySet of the given entity type. * * @param entity * The entity. * @return The subpath of the entitySet of the given entity type. */ public String getEntitySetSubpath(EntityType entityType) { String result = null; if (entityType == null) { return result; } // Try to match the entity class names (without package); for (EntityContainer entityContainer : getContainers()) { for (EntitySet entitySet : entityContainer.getEntities()) { EntityType type = entitySet.getType(); if (type.equals(entityType)) { result = "/" + entitySet.getName(); } } } return result; } /** * Returns the entityType that corresponds to a given entity class. * * @param entityClass * The entity class. * @return The entityType that corresponds to a given entity class. */ public EntityType getEntityType(Class<?> entityClass) { EntityType result = null; // Try to match the entity class names (without package); String className = entityClass.getName(); int index = className.lastIndexOf("."); if (index != -1) { className = className.substring(index + 1); } for (Iterator<EntityContainer> iec = getContainers().iterator(); result == null && iec.hasNext();) { EntityContainer entityContainer = iec.next(); for (Iterator<EntitySet> ies = entityContainer.getEntities() .iterator(); result == null && ies.hasNext();) { EntitySet entitySet = ies.next(); EntityType type = entitySet.getType(); if (type.getClassName().equals(className)) { result = type; } } } return result; } /** * Returns the String representation of the value of the key of the given * entity that should be used in all URIs. * * @param type * The type descriptor of the object. * @param entity * The entity. * @return The value of the key of the given entity or null in case of * error. */ public String getKeyValue(EntityType type, Object entity) { StringBuffer result = new StringBuffer(); if (type.getKeys() != null && !type.getKeys().isEmpty()) { if (type.getKeys().size() == 1) { Property key = type.getKeys().get(0); String keyName = key.getNormalizedName(); String getterName = "get" + keyName.substring(0, 1).toUpperCase() + keyName.substring(1); try { Method getter = entity.getClass().getDeclaredMethod( getterName, (Class[]) null); Object value = getter.invoke(entity, (Object[]) null); String strValue = TypeUtils.toEdmKey(value, key.getType()); if (strValue != null) { result.append(strValue); } else { result.append("''"); } } catch (Exception e) { // Nothing } } else { Iterator<Property> it = type.getKeys().iterator(); while (it.hasNext()) { Property key = it.next(); String keyName = key.getNormalizedName(); result.append(key.getName()).append("="); String getterName = "get" + keyName.substring(0, 1).toUpperCase() + keyName.substring(1); try { Method getter = entity.getClass().getDeclaredMethod( getterName, (Class[]) null); Object value = getter.invoke(entity, (Object[]) null); String strValue = TypeUtils.toEdmKey(value, key.getType()); if (strValue != null) { result.append(strValue); } else { result.append("''"); } } catch (Exception e) { // Nothing } if (it.hasNext()) { result.append(","); } } } } return result.toString(); } /** * Returns the value of the key of the given entity that should be used in * all URIs. * * @param entity * The entity. * @return The value of the key of the given entity or null in case of * error. */ public String getKeyValue(Object entity) { String result = null; if (entity != null) { result = getKeyValue(getEntityType(entity.getClass()), entity); } return result; } /** * Returns the list of declared property mappings. * * @return The list of declared property mappings. */ public List<Mapping> getMappings() { if (mappings == null) { mappings = new ArrayList<Mapping>(); } return mappings; } /** * Returns the URI of the metadata resource. * * @return The URI of the metadata resource. */ public Reference getMetadataRef() { return metadataRef; } /** * Returns the EDM type of the given property of the given entity. * * @param entity * The entity. * @param propertyName * The name of the property. * @return The EDM type of the given property of the given entity. */ public Property getProperty(Object entity, String propertyName) { Property result = null; if (entity != null) { EntityType et = getEntityType(entity.getClass()); if (et != null) { for (Property property : et.getProperties()) { if (property.getName().equals(propertyName) || property.getNormalizedName() .equals(propertyName)) { result = property; break; } } } else { ComplexType ct = getComplexType(entity.getClass()); if (ct != null) { for (Property property : ct.getProperties()) { if (property.getName().equals(propertyName) || property.getNormalizedName().equals( propertyName)) { result = property; break; } } } } } return result; } /** * Returns the data service schema. * * @return The data service schema. */ public List<Schema> getSchemas() { if (schemas == null) { schemas = new ArrayList<Schema>(); } return schemas; } /** * According to the metadata of the service, returns the path of the given * entity relatively to the current WCF service. * * @param entity * The entity. * @return The path of the given entity relatively to the current WCF * service. */ public String getSubpath(Object entity) { String result = null; if (entity == null) { return result; } // Try to match the entity class names (without package); String entityClass = entity.getClass().getName(); int index = entityClass.lastIndexOf("."); if (index != -1) { entityClass = entityClass.substring(index + 1); } for (EntityContainer entityContainer : getContainers()) { for (EntitySet entitySet : entityContainer.getEntities()) { EntityType type = entitySet.getType(); if (type.getClassName().equals(entityClass)) { String value = getKeyValue(type, entity); if (value != null) { result = "/" + entitySet.getName() + "(" + value + ")"; } } } } return result; } /** * According to the metadata of the service, returns the path of the given * entity's property relatively to the current WCF service. * * @param entity * The entity. * @param propertyName * The name of the property. * @return The path of the given entity's property relatively to the current * WCF service. */ public String getSubpath(Object entity, String propertyName) { return getSubpath(entity) + "/" + propertyName; } /** * According to the metadata of the service, returns the relative path of * the given target entity linked to the source entity via the source * property. * * @param source * The source entity to update. * @param sourceProperty * The name of the property of the source entity. * @param target * The entity linked to the source entity. * @return */ public String getSubpath(Object source, String sourceProperty, Object target) { return getSubpath(source) + "/" + sourceProperty + "(" + getKeyValue(target) + ")"; } /** * Sets the list of entity containers. * * @param containers * The list of entity containers. */ public void setContainers(List<EntityContainer> containers) { this.containers = containers; } /** * Sets the data service schemas * * @param schemas * The data service schemas. */ public void setSchemas(List<Schema> schemas) { this.schemas = schemas; } }