org.apache.olingo.ext.proxy.utils.CoreUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.olingo.ext.proxy.utils.CoreUtils.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.olingo.ext.proxy.utils;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.net.URI;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.apache.commons.lang3.StringUtils;
import org.apache.olingo.client.api.EdmEnabledODataClient;
import org.apache.olingo.client.api.uri.URIBuilder;
import org.apache.olingo.client.core.uri.URIUtils;
import org.apache.olingo.client.api.domain.ClientAnnotatable;
import org.apache.olingo.client.api.domain.ClientAnnotation;
import org.apache.olingo.client.api.domain.ClientComplexValue;
import org.apache.olingo.client.api.domain.ClientEntity;
import org.apache.olingo.client.api.domain.ClientEnumValue;
import org.apache.olingo.client.api.domain.ClientLink;
import org.apache.olingo.client.api.domain.ClientPrimitiveValue;
import org.apache.olingo.client.api.domain.ClientProperty;
import org.apache.olingo.client.api.domain.ClientValue;
import org.apache.olingo.commons.api.edm.EdmElement;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
import org.apache.olingo.commons.api.edm.EdmTerm;
import org.apache.olingo.commons.api.edm.EdmType;
import org.apache.olingo.commons.api.edm.FullQualifiedName;
import org.apache.olingo.client.core.domain.ClientAnnotationImpl;
import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory;
import org.apache.olingo.commons.core.edm.EdmTypeInfo;
import org.apache.olingo.ext.proxy.AbstractService;
import org.apache.olingo.ext.proxy.api.AbstractTerm;
import org.apache.olingo.ext.proxy.api.ComplexCollection;
import org.apache.olingo.ext.proxy.api.EntityCollection;
import org.apache.olingo.ext.proxy.api.annotations.ComplexType;
import org.apache.olingo.ext.proxy.api.annotations.CompoundKey;
import org.apache.olingo.ext.proxy.api.annotations.CompoundKeyElement;
import org.apache.olingo.ext.proxy.api.annotations.EnumType;
import org.apache.olingo.ext.proxy.api.annotations.Key;
import org.apache.olingo.ext.proxy.api.annotations.Namespace;
import org.apache.olingo.ext.proxy.api.annotations.NavigationProperty;
import org.apache.olingo.ext.proxy.api.annotations.Property;
import org.apache.olingo.ext.proxy.api.annotations.Term;
import org.apache.olingo.ext.proxy.commons.AbstractStructuredInvocationHandler;
import org.apache.olingo.ext.proxy.commons.ComplexInvocationHandler;
import org.apache.olingo.ext.proxy.commons.EntityInvocationHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public final class CoreUtils {

    /**
     * Logger.
     */
    private static final Logger LOG = LoggerFactory.getLogger(CoreUtils.class);

    private CoreUtils() {
        // Empty private constructor for static utility classes
    }

    public static ClientValue getODataValue(final EdmEnabledODataClient client, final EdmTypeInfo type,
            final Object obj) {

        final ClientValue value;

        if (type.isCollection()) {
            value = client.getObjectFactory().newCollectionValue(type.getFullQualifiedName().toString());

            final EdmTypeInfo intType = new EdmTypeInfo.Builder().setEdm(client.getCachedEdm())
                    .setTypeExpression(type.getFullQualifiedName().toString()).build();

            for (Object collectionItem : (Collection<?>) obj) {
                if (intType.isPrimitiveType()) {
                    value.asCollection().add(getODataValue(client, intType, collectionItem).asPrimitive());
                } else if (intType.isEnumType()) {
                    value.asCollection().add((getODataValue(client, intType, collectionItem)).asEnum());
                } else if (intType.isComplexType()) {
                    value.asCollection().add(getODataValue(client, intType, collectionItem).asComplex());
                } else {
                    throw new UnsupportedOperationException(
                            "Unsupported object type " + intType.getFullQualifiedName());
                }
            }
        } else if (type.isComplexType()) {

            final Object objHandler = Proxy.getInvocationHandler(obj);
            if (objHandler instanceof ComplexInvocationHandler) {
                value = ((ComplexInvocationHandler) objHandler).getComplex();

                final Class<?> typeRef = ((ComplexInvocationHandler) objHandler).getTypeRef();

                for (Map.Entry<String, Object> changes : ((ComplexInvocationHandler) objHandler)
                        .getPropertyChanges().entrySet()) {
                    try {
                        value.asComplex().add(getODataComplexProperty(client, type.getFullQualifiedName(),
                                changes.getKey(), changes.getValue()));
                    } catch (Exception ignore) {
                        // ignore value
                        LOG.warn("Error attaching complex {} for field '{}.{}'", type.getFullQualifiedName(),
                                typeRef.getName(), changes.getKey(), ignore);
                    }
                }

            } else {
                throw new IllegalArgumentException(objHandler.getClass().getName() + "' is not a complex value");
            }

        } else if (type.isEnumType()) {
            value = client.getObjectFactory().newEnumValue(type.getFullQualifiedName().toString(),
                    ((Enum<?>) obj).name());
        } else {
            value = client.getObjectFactory().newPrimitiveValueBuilder().setType(type.getPrimitiveTypeKind())
                    .setValue(obj).build();
        }

        return value;
    }

    private static ClientProperty getODataEntityProperty(final EdmEnabledODataClient client,
            final FullQualifiedName entity, final String property, final Object obj) {

        final EdmElement edmProperty = client.getCachedEdm().getEntityType(entity).getProperty(property);
        return getODataProperty(client, edmProperty, property, obj);
    }

    private static ClientProperty getODataComplexProperty(final EdmEnabledODataClient client,
            final FullQualifiedName complex, final String property, final Object obj) {

        final EdmElement edmProperty = client.getCachedEdm().getComplexType(complex).getProperty(property);
        return getODataProperty(client, edmProperty, property, obj);
    }

    private static ClientProperty getODataProperty(final EdmEnabledODataClient client, final EdmElement edmProperty,
            final String property, final Object obj) {

        final EdmTypeInfo type;
        if (edmProperty == null) {
            // maybe opentype ...
            type = null;
        } else {
            final EdmType edmType = edmProperty.getType();

            type = new EdmTypeInfo.Builder().setEdm(client.getCachedEdm())
                    .setTypeExpression(edmProperty.isCollection()
                            ? "Collection(" + edmType.getFullQualifiedName().toString() + ")"
                            : edmType.getFullQualifiedName().toString())
                    .build();
        }

        return getODataProperty(client, property, type, obj);
    }

    public static ClientAnnotation getODataAnnotation(final EdmEnabledODataClient client, final String term,
            final EdmType type, final Object obj) {

        ClientAnnotation annotation;

        if (obj == null) {
            annotation = new ClientAnnotationImpl(term, null);
        } else {
            final EdmTypeInfo valueType = type == null ? guessTypeFromObject(client, obj)
                    : new EdmTypeInfo.Builder().setEdm(client.getCachedEdm())
                            .setTypeExpression(type.getFullQualifiedName().toString()).build();

            annotation = new ClientAnnotationImpl(term, getODataValue(client, valueType, obj));
        }

        return annotation;
    }

    public static ClientProperty getODataProperty(final EdmEnabledODataClient client, final String name,
            final EdmTypeInfo type, final Object obj) {

        ClientProperty property;

        try {
            if (obj == null) {
                property = client.getObjectFactory().newPrimitiveProperty(name, null);
            } else {
                final EdmTypeInfo valueType = type == null ? guessTypeFromObject(client, obj) : type;
                final ClientValue value = getODataValue(client, valueType, obj);

                if (valueType.isCollection()) {
                    property = client.getObjectFactory().newCollectionProperty(name, value.asCollection());
                } else if (valueType.isPrimitiveType()) {
                    property = client.getObjectFactory().newPrimitiveProperty(name, value.asPrimitive());
                } else if (valueType.isComplexType()) {
                    property = client.getObjectFactory().newComplexProperty(name, value.asComplex());
                } else if (valueType.isEnumType()) {
                    property = client.getObjectFactory().newEnumProperty(name, value.asEnum());
                } else {
                    throw new UnsupportedOperationException(
                            "Usupported object type " + valueType.getFullQualifiedName());
                }
            }

            return property;
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
    }

    private static EdmTypeInfo guessTypeFromObject(final EdmEnabledODataClient client, final Object obj) {

        final EdmTypeInfo.Builder edmTypeInfo = new EdmTypeInfo.Builder().setEdm(client.getCachedEdm());

        if (Collection.class.isAssignableFrom(obj.getClass())) {
            final EdmTypeInfo type = guessPrimitiveType(client, ClassUtils.extractTypeArg(obj.getClass(),
                    EntityCollection.class, ComplexCollection.class, Collection.class));
            return edmTypeInfo.setTypeExpression("Collection(" + type.getFullQualifiedName() + ")").build();
        } else if (obj instanceof Proxy) {
            final Class<?> typeRef = obj.getClass().getInterfaces()[0];
            final String ns = typeRef.getAnnotation(Namespace.class).value();
            final String name = typeRef.getAnnotation(ComplexType.class).name();
            return edmTypeInfo.setTypeExpression(new FullQualifiedName(ns, name).toString()).build();
        } else if (obj.getClass().getAnnotation(EnumType.class) != null) {
            final Class<?> typeRef = obj.getClass();
            final String ns = typeRef.getAnnotation(Namespace.class).value();
            final String name = typeRef.getAnnotation(EnumType.class).name();
            return edmTypeInfo.setTypeExpression(new FullQualifiedName(ns, name).toString()).build();
        } else {
            return guessPrimitiveType(client, obj.getClass());
        }
    }

    private static EdmTypeInfo guessPrimitiveType(final EdmEnabledODataClient client, final Class<?> clazz) {
        EdmPrimitiveTypeKind bckCandidate = null;

        for (EdmPrimitiveTypeKind kind : EdmPrimitiveTypeKind.values()) {
            final Class<?> target = EdmPrimitiveTypeFactory.getInstance(kind).getDefaultType();

            if (clazz.equals(target)) {
                return new EdmTypeInfo.Builder().setEdm(client.getCachedEdm()).setTypeExpression(kind.toString())
                        .build();
            } else if (target.isAssignableFrom(clazz)) {
                bckCandidate = kind;
            } else if (target == Timestamp.class && kind == EdmPrimitiveTypeKind.DateTimeOffset) {
                bckCandidate = kind;
            }
        }

        if (bckCandidate == null) {
            throw new IllegalArgumentException(clazz.getSimpleName() + " is not a simple type");
        } else {
            return new EdmTypeInfo.Builder().setEdm(client.getCachedEdm())
                    .setTypeExpression(bckCandidate.toString()).build();
        }
    }

    public static void addProperties(final EdmEnabledODataClient client, final Map<String, Object> changes,
            final ClientEntity entity) {

        for (Map.Entry<String, Object> entry : changes.entrySet()) {
            entity.getProperties()
                    .add(getODataEntityProperty(client, entity.getTypeName(), entry.getKey(), entry.getValue()));
        }
    }

    public static void addProperties(final EdmEnabledODataClient client, final Map<String, Object> changes,
            final ClientComplexValue entity) {

        for (Map.Entry<String, Object> entry : changes.entrySet()) {
            entity.add(getODataComplexProperty(client, new FullQualifiedName(entity.getTypeName()), entry.getKey(),
                    entry.getValue()));
        }
    }

    public static void addAnnotations(final EdmEnabledODataClient client,
            final Map<Class<? extends AbstractTerm>, Object> annotations, final ClientAnnotatable annotatable) {

        for (Map.Entry<Class<? extends AbstractTerm>, Object> entry : annotations.entrySet()) {
            final Namespace nsAnn = entry.getKey().getAnnotation(Namespace.class);
            final Term termAnn = entry.getKey().getAnnotation(Term.class);
            final FullQualifiedName termName = new FullQualifiedName(nsAnn.value(), termAnn.name());
            final EdmTerm term = client.getCachedEdm().getTerm(termName);
            if (term == null) {
                LOG.error("Could not find term for class {}", entry.getKey().getName());
            } else {
                annotatable.getAnnotations().add(getODataAnnotation(client, term.getFullQualifiedName().toString(),
                        term.getType(), entry.getValue()));
            }
        }
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    private static Enum<?> enumValueToObject(final ClientEnumValue value, final Class<?> reference) {
        final Namespace namespace = reference.getAnnotation(Namespace.class);
        final EnumType enumType = reference.getAnnotation(EnumType.class);
        if (value.getTypeName().equals(namespace.value() + "." + enumType.name())) {
            return Enum.valueOf((Class<Enum>) reference, value.getValue());
        }

        return null;
    }

    private static Object primitiveValueToObject(final ClientPrimitiveValue value, final Class<?> reference) {
        Object obj;

        try {
            obj = reference == null ? value.toValue() : value.toCastValue(reference);
        } catch (EdmPrimitiveTypeException e) {
            LOG.warn("While casting primitive value {} to {}", value, reference, e);
            obj = value.toValue();
        }

        return obj;
    }

    private static void setPropertyValue(final Object bean, final Method getter, final Object value)
            throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException,
            InvocationTargetException {

        // Assumption: setter is always prefixed by 'set' word
        final String setterName = getter.getName().replaceFirst("get", "set");
        bean.getClass().getMethod(setterName, getter.getReturnType()).invoke(bean, value);
    }

    private static Class<?> getPropertyClass(final Class<?> entityClass, final String propertyName) {
        Class<?> propertyClass = null;
        try {
            final Method getter = entityClass.getMethod("get" + StringUtils.capitalize(propertyName));
            if (getter != null) {
                propertyClass = getter.getReturnType();
            }
        } catch (Exception e) {
            LOG.error("Could not determine the Java type of {}", propertyName, e);
        }
        return propertyClass;
    }

    public static URIBuilder buildEditLink(final EdmEnabledODataClient client, final String entitySetURI,
            final Object key) {

        if (key == null) {
            return null;
        }

        final URIBuilder uriBuilder = StringUtils.isNotBlank(entitySetURI) ? client.newURIBuilder(entitySetURI)
                : client.newURIBuilder();

        if (key.getClass().getAnnotation(CompoundKey.class) == null) {
            LOG.debug("Append key segment '{}'", key);
            uriBuilder.appendKeySegment(key);
        } else {
            LOG.debug("Append compound key segment '{}'", key);
            uriBuilder.appendKeySegment(CoreUtils.getCompoundKey(key));
        }

        return uriBuilder;
    }

    public static Map<String, Object> getCompoundKey(final Object key) {
        final Set<CompoundKeyElementWrapper> elements = new TreeSet<CompoundKeyElementWrapper>();

        for (Method method : key.getClass().getMethods()) {
            final Annotation annotation = method.getAnnotation(CompoundKeyElement.class);
            if (annotation instanceof CompoundKeyElement) {
                elements.add(new CompoundKeyElementWrapper(((CompoundKeyElement) annotation).name(), method,
                        ((CompoundKeyElement) annotation).position()));
            }
        }

        final LinkedHashMap<String, Object> map = new LinkedHashMap<String, Object>();

        for (CompoundKeyElementWrapper element : elements) {
            try {
                map.put(element.getName(), element.getMethod().invoke(key));
            } catch (Exception e) {
                LOG.warn("Error retrieving compound key element '{}' value", element.getName(), e);
            }
        }

        return map;
    }

    public static Object getKey(final EdmEnabledODataClient client, final EntityInvocationHandler typeHandler,
            final Class<?> entityTypeRef, final ClientEntity entity) {

        Object res = null;

        if (!entity.getProperties().isEmpty()) {
            final Class<?> keyRef = ClassUtils.getCompoundKeyRef(entityTypeRef);
            if (keyRef == null) {
                final ClientProperty property = entity.getProperty(firstValidEntityKey(entityTypeRef));
                if (property != null && property.hasPrimitiveValue()) {
                    res = primitiveValueToObject(property.getPrimitiveValue(),
                            getPropertyClass(entityTypeRef, property.getName()));
                }
            } else {
                try {
                    res = keyRef.newInstance();
                    populate(client, typeHandler, res, CompoundKeyElement.class, entity.getProperties().iterator());
                } catch (Exception e) {
                    LOG.error("Error population compound key {}", keyRef.getSimpleName(), e);
                    throw new IllegalArgumentException("Cannot populate compound key");
                }
            }
        }

        return res;
    }

    private static void populate(final EdmEnabledODataClient client, final EntityInvocationHandler typeHandler,
            final Object bean, final Class<? extends Annotation> getterAnn,
            final Iterator<? extends ClientProperty> propItor) {

        if (bean != null) {
            final Class<?> typeRef;
            if (bean instanceof Proxy) {
                final InvocationHandler handler = Proxy.getInvocationHandler(bean);
                if (handler instanceof AbstractStructuredInvocationHandler) {
                    typeRef = ((ComplexInvocationHandler) handler).getTypeRef();
                } else {
                    throw new IllegalStateException("Invalid bean " + bean);
                }
            } else {
                typeRef = bean.getClass();
            }
            populate(client, typeHandler, bean, typeRef, getterAnn, propItor);
        }
    }

    @SuppressWarnings({ "unchecked" })
    private static void populate(final EdmEnabledODataClient client, final EntityInvocationHandler typeHandler,
            final Object bean, final Class<?> typeRef, final Class<? extends Annotation> getterAnn,
            final Iterator<? extends ClientProperty> propItor) {

        if (bean != null) {
            while (propItor.hasNext()) {
                final ClientProperty property = propItor.next();

                final Method getter = ClassUtils.findGetterByAnnotatedName(typeRef, getterAnn, property.getName());

                if (getter == null) {
                    LOG.warn("Could not find any property annotated as {} in {}", property.getName(),
                            bean.getClass().getName());
                } else {
                    try {
                        if (property.hasNullValue()) {
                            setPropertyValue(bean, getter, null);
                        } else if (property.hasPrimitiveValue()) {
                            setPropertyValue(bean, getter, primitiveValueToObject(property.getPrimitiveValue(),
                                    getPropertyClass(typeRef, property.getName())));
                        } else if (property.hasComplexValue()) {
                            final Object complex = Proxy.newProxyInstance(
                                    Thread.currentThread().getContextClassLoader(),
                                    new Class<?>[] { getter.getReturnType() },
                                    ComplexInvocationHandler.getInstance(typeHandler, getter.getReturnType()));

                            populate(client, typeHandler, complex, Property.class,
                                    property.getValue().asComplex().iterator());
                            setPropertyValue(bean, getter, complex);
                        } else if (property.hasCollectionValue()) {
                            final ParameterizedType collType = (ParameterizedType) getter.getGenericReturnType();
                            final Class<?> collItemClass = (Class<?>) collType.getActualTypeArguments()[0];

                            Collection<Object> collection = (Collection<Object>) getter.invoke(bean);
                            if (collection == null) {
                                collection = new ArrayList<Object>();
                                setPropertyValue(bean, getter, collection);
                            }

                            final Iterator<ClientValue> collPropItor = property.getValue().asCollection()
                                    .iterator();
                            while (collPropItor.hasNext()) {
                                final ClientValue value = collPropItor.next();
                                if (value.isPrimitive()) {
                                    collection.add(primitiveValueToObject(value.asPrimitive(),
                                            getPropertyClass(typeRef, property.getName())));
                                } else if (value.isComplex()) {
                                    final Object collItem = Proxy.newProxyInstance(
                                            Thread.currentThread().getContextClassLoader(),
                                            new Class<?>[] { collItemClass },
                                            ComplexInvocationHandler.getInstance(typeHandler, collItemClass));

                                    populate(client, typeHandler, collItem, Property.class,
                                            value.asComplex().iterator());
                                    collection.add(collItem);
                                }
                            }
                        }
                    } catch (Exception e) {
                        LOG.error("Could not set property {} on {}", getter, bean, e);
                    }
                }
            }
        }
    }

    public static Object getObjectFromODataValue(final ClientValue value, final Type typeRef,
            final AbstractService<?> service) throws InstantiationException, IllegalAccessException {

        Class<?> internalRef;
        if (typeRef == null) {
            internalRef = null;
        } else {
            try {
                internalRef = (Class<?>) ((ParameterizedType) typeRef).getActualTypeArguments()[0];
            } catch (ClassCastException e) {
                internalRef = (Class<?>) typeRef;
            }
        }

        return getObjectFromODataValue(value, internalRef, service);
    }

    public static Object getObjectFromODataValue(final ClientValue value, final Class<?> ref,
            final AbstractService<?> service) throws InstantiationException, IllegalAccessException {

        final Object res;

        if (value == null) {
            res = null;
        } else if (value.isComplex()) {
            // complex types supports inheritance in V4, best to re-read actual type
            Class<?> internalRef = getComplexTypeRef(service, value);
            res = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                    new Class<?>[] { internalRef },
                    ComplexInvocationHandler.getInstance(value.asComplex(), internalRef, service));
        } else if (value.isCollection()) {
            final ArrayList<Object> collection = new ArrayList<Object>();

            final Iterator<ClientValue> collPropItor = value.asCollection().iterator();
            while (collPropItor.hasNext()) {
                final ClientValue itemValue = collPropItor.next();
                if (itemValue.isPrimitive()) {
                    collection.add(CoreUtils.primitiveValueToObject(itemValue.asPrimitive(), ref));
                } else if (itemValue.isComplex()) {
                    Class<?> internalRef = getComplexTypeRef(service, value);
                    final Object collItem = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                            new Class<?>[] { internalRef },
                            ComplexInvocationHandler.getInstance(itemValue.asComplex(), internalRef, service));

                    collection.add(collItem);
                }
            }

            res = collection;
        } else if (value instanceof ClientEnumValue) {
            res = enumValueToObject((ClientEnumValue) value, ref == null ? getEnumTypeRef(service, value) : ref);
        } else {
            res = primitiveValueToObject(value.asPrimitive(), ref);
        }

        return res;
    }

    public static Collection<Class<? extends AbstractTerm>> getAnnotationTerms(final AbstractService<?> service,
            final List<ClientAnnotation> annotations) {

        final List<Class<? extends AbstractTerm>> res = new ArrayList<Class<? extends AbstractTerm>>();

        for (ClientAnnotation annotation : annotations) {
            final Class<? extends AbstractTerm> clazz = service.getTermClass(annotation.getTerm());
            if (clazz != null) {
                res.add(clazz);
            }
        }

        return res;
    }

    private static Class<?> getEnumTypeRef(final AbstractService<?> service, final ClientValue value) {
        return service
                .getEnumTypeClass(value.getTypeName().replaceAll("^Collection\\(", "").replaceAll("\\)$", ""));
    }

    public static Class<?> getComplexTypeRef(final AbstractService<?> service, final ClientValue value) {
        return service
                .getComplexTypeClass(value.getTypeName().replaceAll("^Collection\\(", "").replaceAll("\\)$", ""));
    }

    private static String firstValidEntityKey(final Class<?> entityTypeRef) {
        for (Method method : entityTypeRef.getDeclaredMethods()) {
            if (method.getAnnotation(Key.class) != null) {
                final Annotation ann = method.getAnnotation(Property.class);
                if (ann != null) {
                    return ((Property) ann).name();
                }
            }
        }
        return null;
    }

    public static URI getMediaEditLink(final String name, final ClientEntity entity) {
        final ClientLink mediaEditLink = entity.getMediaEditLink(name);
        return mediaEditLink == null ? URIUtils.getURI(entity.getEditLink(), name) : mediaEditLink.getLink();
    }

    public static URI getTargetEntitySetURI(final EdmEnabledODataClient client, final NavigationProperty property) {
        final URIBuilder uriBuilder = client.newURIBuilder(client.getServiceRoot());
        uriBuilder.appendEntitySetSegment(property.targetEntitySet());
        return uriBuilder.build();
    }
}