All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.olingo.odata2.annotation.processor.core.util.AnnotationHelper Maven / Gradle / Ivy

/**
 * *****************************************************************************
 * 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.odata2.annotation.processor.core.util;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import org.apache.olingo.odata2.api.annotation.edm.EdmComplexType;
import org.apache.olingo.odata2.api.annotation.edm.EdmEntitySet;
import org.apache.olingo.odata2.api.annotation.edm.EdmEntityType;
import org.apache.olingo.odata2.api.annotation.edm.EdmKey;
import org.apache.olingo.odata2.api.annotation.edm.EdmNavigationProperty;
import org.apache.olingo.odata2.api.annotation.edm.EdmNavigationProperty.Multiplicity;
import org.apache.olingo.odata2.api.annotation.edm.EdmProperty;
import org.apache.olingo.odata2.api.edm.EdmLiteralKind;
import org.apache.olingo.odata2.api.edm.EdmMultiplicity;
import org.apache.olingo.odata2.api.edm.EdmSimpleTypeException;
import org.apache.olingo.odata2.api.edm.EdmSimpleTypeKind;
import org.apache.olingo.odata2.api.edm.FullQualifiedName;
import org.apache.olingo.odata2.api.exception.ODataException;

// TODO: Auto-generated Javadoc
/**
 * The Class AnnotationHelper.
 */
public class AnnotationHelper {

  /** The Constant DEFAULT_CONTAINER_NAME. */
  public static final String DEFAULT_CONTAINER_NAME = "DefaultContainer";

  /**
   * Compare keys of both instances.
   *
   * @param firstInstance the first instance
   * @param secondInstance the second instance
   * @return true, if successful
   */
  public boolean keyMatch(final Object firstInstance, final Object secondInstance) {
    if (firstInstance == null || secondInstance == null) {
      return false;
    } else if (firstInstance.getClass() != secondInstance.getClass()) {
      return false;
    }

    Map firstKeyFields = getValueForAnnotatedFields(firstInstance, EdmKey.class);
    Map secondKeyFields = getValueForAnnotatedFields(secondInstance, EdmKey.class);
    if (firstKeyFields.isEmpty() && secondKeyFields.isEmpty()) {
      throw new AnnotationRuntimeException("Both object instances does not have EdmKey fields defined ["
          + "firstClass=" + firstInstance.getClass().getName()
          + " secondClass=" + secondInstance.getClass().getName() + "].");
    }

    return keyValuesMatch(firstKeyFields, secondKeyFields);
  }

  /**
   * Compare keys of instance with key values in map.
   *
   * @param instance the instance
   * @param keyName2Value the key name 2 value
   * @return true, if successful
   */
  public boolean keyMatch(final Object instance, final Map keyName2Value) {
    Map instanceKeyFields = getValueForAnnotatedFields(instance, EdmKey.class);
    return keyValuesMatch(instanceKeyFields, keyName2Value);
  }

  /**
   * Key values match.
   *
   * @param firstKeyValues the first key values
   * @param secondKeyValues the second key values
   * @return true, if successful
   */
  private boolean keyValuesMatch(final Map firstKeyValues, final Map secondKeyValues) {
    if (firstKeyValues.size() != secondKeyValues.size()) {
      return false;
    } else if (firstKeyValues.isEmpty()) {
      throw new AnnotationRuntimeException("No keys given for key value matching.");
    } else {
      Set> entries = firstKeyValues.entrySet();
      for (Map.Entry entry : entries) {
        Object firstKey = entry.getValue();
        Object secondKey = secondKeyValues.get(entry.getKey());
        if (!isEqual(firstKey, secondKey)) {
          return false;
        }
      }
      return true;
    }
  }

  /**
   * Checks if is equal.
   *
   * @param firstKey the first key
   * @param secondKey the second key
   * @return true, if is equal
   */
  private boolean isEqual(final Object firstKey, final Object secondKey) {
    if (firstKey == null) {
      return secondKey == null || secondKey.equals(firstKey);
    } else {
      return firstKey.equals(secondKey);
    }
  }

  /**
   * Extract entity type name.
   *
   * @param enp the enp
   * @param fallbackClass the fallback class
   * @return the string
   */
  public String extractEntityTypeName(final EdmNavigationProperty enp, final Class fallbackClass) {
    Class entityTypeClass = enp.toType();
    return extractEntityTypeName(entityTypeClass == Object.class ? fallbackClass : entityTypeClass);
  }

  /**
   * Extract entity type name.
   *
   * @param enp the enp
   * @param field the field
   * @return the string
   */
  public String extractEntityTypeName(final EdmNavigationProperty enp, final Field field) {
    Class entityTypeClass = enp.toType();
    if (entityTypeClass == Object.class) {
      Class toClass = field.getType();
      return extractEntityTypeName((toClass.isArray() || Collection.class.isAssignableFrom(toClass) ?
          (Class) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0] : toClass));
    } else {
      return extractEntityTypeName(entityTypeClass);
    }
  }

  /**
   * Returns NULL if given class is not annotated. If annotated the set entity type name is returned and if
   * no name is set the default name is generated from the simple class name.
   *
   * @param annotatedClass the annotated class
   * @return the string
   */
  public String extractEntityTypeName(final Class annotatedClass) {
    return extractTypeName(annotatedClass, EdmEntityType.class);
  }

  /**
   * Returns NULL if given class is not annotated.
   * If annotated the entity set name is returned and if
   * no name is set a default name is generated based on the simple class name.
   *
   * @param annotatedClass the annotated class
   * @return the string
   */
  public String extractEntitySetName(final Class annotatedClass) {
    if (annotatedClass == Object.class) {
      return null;
    }
    EdmEntitySet entitySet = annotatedClass.getAnnotation(EdmEntitySet.class);
    if (entitySet == null) {
      return null;
    }

    String name = entitySet.name();
    if (name.isEmpty()) {
      return getCanonicalName(annotatedClass) + "Set";
    }
    return name;
  }

  /**
   * Extract entity type fqn.
   *
   * @param type the type
   * @param annotatedClass the annotated class
   * @return the full qualified name
   */
  public FullQualifiedName extractEntityTypeFqn(final EdmEntityType type, final Class annotatedClass) {
    if (type.namespace().isEmpty()) {
      return new FullQualifiedName(generateNamespace(annotatedClass), extractEntityTypeName(annotatedClass));
    }
    return new FullQualifiedName(type.namespace(), extractEntityTypeName(annotatedClass));
  }

  /**
   * Extract entity type fqn.
   *
   * @param annotatedClass the annotated class
   * @return the full qualified name
   */
  public FullQualifiedName extractEntityTypeFqn(final Class annotatedClass) {
    EdmEntityType type = annotatedClass.getAnnotation(EdmEntityType.class);
    if (type == null) {
      return null;
    }
    return extractEntityTypeFqn(type, annotatedClass);
  }

  /**
   * Extract complex type fqn.
   *
   * @param annotatedClass the annotated class
   * @return the full qualified name
   */
  public FullQualifiedName extractComplexTypeFqn(final Class annotatedClass) {
    EdmComplexType type = annotatedClass.getAnnotation(EdmComplexType.class);
    if (type == null) {
      return null;
    }
    return extractComplexTypeFqn(type, annotatedClass);
  }

  /**
   * Extract complex type fqn.
   *
   * @param type the type
   * @param annotatedClass the annotated class
   * @return the full qualified name
   */
  public FullQualifiedName extractComplexTypeFqn(final EdmComplexType type, final Class annotatedClass) {
    if (type.namespace().isEmpty()) {
      return new FullQualifiedName(generateNamespace(annotatedClass), extractComplexTypeName(annotatedClass));
    }
    return new FullQualifiedName(type.namespace(), extractComplexTypeName(annotatedClass));
  }

  /**
   * Extract complex type name.
   *
   * @param annotatedClass the annotated class
   * @return the string
   */
  public String extractComplexTypeName(final Class annotatedClass) {
    return extractTypeName(annotatedClass, EdmComplexType.class);
  }

  /**
   * Generate namespace.
   *
   * @param annotatedClass the annotated class
   * @return the string
   */
  public String generateNamespace(final Class annotatedClass) {
    return annotatedClass.getPackage().getName();
  }

  /**
   * Extract type name.
   *
   * @param  must be EdmEntityType or EdmComplexType annotation
   * @param annotatedClass the annotated class
   * @param typeAnnotation the type annotation
   * @return null if annotatedClass is not annotated or name set via annotation or generated via
   * {@link #getCanonicalName(java.lang.Class)}
   */
  private  String extractTypeName(final Class annotatedClass, final Class typeAnnotation) {
    if (annotatedClass == Object.class) {
      return null;
    }
    T type = annotatedClass.getAnnotation(typeAnnotation);
    if (type == null) {
      return null;
    }

    String name;
    if (typeAnnotation == EdmEntityType.class) {
      name = ((EdmEntityType) type).name();
    } else if (typeAnnotation == EdmComplexType.class) {
      name = ((EdmComplexType) type).name();
    } else {
      return null;
    }

    if (name.isEmpty()) {
      return getCanonicalName(annotatedClass);
    }
    return name;
  }

  /**
   * Get the set property name from an EdmProperty or EdmNavigationProperty annotation.
   *
   * @param field the field
   * @return the property name from annotation
   */
  public String getPropertyNameFromAnnotation(final Field field) {
    EdmProperty property = field.getAnnotation(EdmProperty.class);
    if (property == null) {
      EdmNavigationProperty navProperty = field.getAnnotation(EdmNavigationProperty.class);
      if (navProperty == null) {
        throw new EdmAnnotationException("Given field '" + field
            + "' has no EdmProperty or EdmNavigationProperty annotation.");
      }
      return navProperty.name();
    }
    return property.name();
  }

  /**
   * Gets the property name.
   *
   * @param field the field
   * @return the property name
   */
  public String getPropertyName(final Field field) {
    String propertyName = getPropertyNameFromAnnotation(field);
    if (propertyName.isEmpty()) {
      propertyName = getCanonicalName(field);
    }
    return propertyName;
  }

  /**
   * Extract to role name.
   *
   * @param enp the enp
   * @param field the field
   * @return the string
   */
  public String extractToRoleName(final EdmNavigationProperty enp, final Field field) {
    String role = enp.toRole();
    if (role.isEmpty()) {
      role = getCanonicalRoleName(field.getName());
    }
    return role;
  }

  /**
   * Extract from role entity name.
   *
   * @param field the field
   * @return the string
   */
  public String extractFromRoleEntityName(final Field field) {
    return extractEntityTypeName(field.getDeclaringClass());
  }

  /**
   * Extract to role entity name.
   *
   * @param enp the enp
   * @param field the field
   * @return the string
   */
  public String extractToRoleEntityName(final EdmNavigationProperty enp, final Field field) {
    Class clazz = enp.toType();
    if (clazz == Object.class) {
      if(field.getType().isArray() || Collection.class.isAssignableFrom(field.getType())) {
        clazz = (Class) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
      } else {
        clazz = field.getType();
      }
    }
    return extractEntityTypeName(clazz);
  }

  /**
   * Gets the canonical role name.
   *
   * @param name the name
   * @return the canonical role name
   */
  public String getCanonicalRoleName(String name) {
    return "r_" + name.substring(0, 1).toUpperCase(Locale.ENGLISH) + name.substring(1);
  }

  /**
   * Extract relationship name.
   *
   * @param enp the enp
   * @param field the field
   * @return the string
   */
  public String extractRelationshipName(final EdmNavigationProperty enp, final Field field) {
    String relationshipName = enp.association();
    if (relationshipName.isEmpty()) {
      final String fromRole = extractFromRoleEntityName(field);
      final String toRole = extractToRoleEntityName(enp, field);
      return createCanonicalRelationshipName(fromRole, toRole);
    }
    return relationshipName;
  }

  /**
   * Creates the canonical relationship name.
   *
   * @param fromRole the from role
   * @param toRole the to role
   * @return the string
   */
  public String createCanonicalRelationshipName(String fromRole, String toRole) {
      if (fromRole.compareTo(toRole) > 0) {
        return toRole + "_2_" + fromRole;
      } else {
        return fromRole + "_2_" + toRole;
      }
  }

  /**
   * Extract multiplicity.
   *
   * @param enp the enp
   * @param field the field
   * @return the edm multiplicity
   */
  public EdmMultiplicity extractMultiplicity(final EdmNavigationProperty enp, final Field field) {
    EdmMultiplicity multiplicity = mapMultiplicity(enp.toMultiplicity());
    final boolean isCollectionType = field.getType().isArray() || Collection.class.isAssignableFrom(field.getType());

    if (multiplicity == EdmMultiplicity.ONE && isCollectionType) {
      return EdmMultiplicity.MANY;
    }
    return multiplicity;
  }

  /**
   * Set key fields based on values in map.
   * If an key field is not available or NULL in the map
   * it will be not set as NULL at the instance object.
   *
   * @param  the generic type
   * @param instance the instance
   * @param keys the keys
   * @return the t
   */
  public  T setKeyFields(final T instance, final Map keys) {
    List fields = getAnnotatedFields(instance, EdmKey.class);

    for (Field field : fields) {
      String propertyName = getPropertyName(field);
      Object keyValue = keys.get(propertyName);
      setValueForProperty(instance, propertyName, keyValue);
    }

    return instance;
  }

  /**
   * The Class ODataAnnotationException.
   */
  public static final class ODataAnnotationException extends ODataException {
    
    /** The Constant serialVersionUID. */
    private static final long serialVersionUID = 42L;

    /**
     * Instantiates a new o data annotation exception.
     *
     * @param message the message
     */
    public ODataAnnotationException(final String message) {
      super(message);
    }

    /**
     * Instantiates a new o data annotation exception.
     *
     * @param message the message
     * @param cause the cause
     */
    public ODataAnnotationException(final String message, final Exception cause) {
      super(message, cause);
    }
  }

  /**
   * The Class AnnotatedNavInfo.
   */
  public class AnnotatedNavInfo {
    
    /** The from field. */
    private final Field fromField;
    
    /** The to field. */
    private final Field toField;
    
    /** The from navigation. */
    private final EdmNavigationProperty fromNavigation;
    
    /** The to navigation. */
    private final EdmNavigationProperty toNavigation;

    /**
     * Instantiates a new annotated nav info.
     *
     * @param fromField the from field
     * @param toField the to field
     * @param fromNavigation the from navigation
     * @param toNavigation the to navigation
     */
    public AnnotatedNavInfo(final Field fromField, final Field toField, final EdmNavigationProperty fromNavigation,
        final EdmNavigationProperty toNavigation) {
      this.fromField = fromField;
      this.toField = toField;
      this.fromNavigation = fromNavigation;
      this.toNavigation = toNavigation;
    }

    /**
     * Gets the from field.
     *
     * @return the from field
     */
    public Field getFromField() {
      return fromField;
    }

    /**
     * Gets the from role name.
     *
     * @return the from role name
     */
    public String getFromRoleName() {
      if(isBiDirectional()) {
        return extractFromRoleEntityName(toField);
      }
      return extractToRoleName(toNavigation, toField);
    }

    /**
     * Gets the to field.
     *
     * @return the to field
     */
    public Field getToField() {
      return toField;
    }

    /**
     * Gets the to role name.
     *
     * @return the to role name
     */
    public String getToRoleName() {
      if(isBiDirectional()) {
        return extractToRoleName(toNavigation, toField);
      }
      return extractToRoleName(fromNavigation, fromField);
    }

    /**
     * Gets the from multiplicity.
     *
     * @return the from multiplicity
     */
    public EdmMultiplicity getFromMultiplicity() {
      if(isBiDirectional()) {
        return EdmMultiplicity.ONE;
      }
      return extractMultiplicity(toNavigation, toField);
    }

    /**
     * Gets the to multiplicity.
     *
     * @return the to multiplicity
     */
    public EdmMultiplicity getToMultiplicity() {
      if(isBiDirectional()) {
        return extractMultiplicity(toNavigation, toField);
      }
      return extractMultiplicity(fromNavigation, fromField);
    }

    /**
     * Checks if is bi directional.
     *
     * @return true, if is bi directional
     */
    public boolean isBiDirectional() {
      return fromNavigation == null;
    }

    /**
     * Gets the relationship name.
     *
     * @return the relationship name
     */
    public String getRelationshipName() {
      String toAssociation = toNavigation.association();
      String fromAssociation = "";
      if(!isBiDirectional()) {
        fromAssociation = fromNavigation.association();
      }

      if(fromAssociation.isEmpty() && fromAssociation.equals(toAssociation)) {
        return createCanonicalRelationshipName(getFromRoleName(), getToRoleName());
      } else if(toAssociation.isEmpty()) {
        return fromAssociation;
      } else if(!toAssociation.equals(fromAssociation)) {
        throw new AnnotationRuntimeException("Invalid associations for navigation properties '" +
            this.toString() + "'");
      }
      return toAssociation;
    }

    /**
     * Gets the from type name.
     *
     * @return the from type name
     */
    public String getFromTypeName() {
      if(isBiDirectional()) {
        return extractEntityTypeName(toField.getDeclaringClass());
      }
      return extractEntityTypeName(fromField.getDeclaringClass());
    }

    /**
     * Gets the to type name.
     *
     * @return the to type name
     */
    public String getToTypeName() {
      if(isBiDirectional()) {
        return extractEntityTypeName(ClassHelper.getFieldType(toField));
      }
      return extractEntityTypeName(toField.getDeclaringClass());
    }

    /**
     * To string.
     *
     * @return the string
     */
    @Override
    public String toString() {
      if(isBiDirectional()) {
        return "AnnotatedNavInfo{biDirectional = true" +
            ", toField=" + toField.getName() +
            ", toNavigation=" + toNavigation.name() + '}';
      }
      return "AnnotatedNavInfo{" +
          "fromField=" + fromField.getName() +
          ", toField=" + toField.getName() +
          ", fromNavigation=" + fromNavigation.name() +
          ", toNavigation=" + toNavigation.name() + '}';
    }
  }


  /**
   * Gets the common navigation info.
   *
   * @param sourceClass the source class
   * @param targetClass the target class
   * @return the common navigation info
   */
  public AnnotatedNavInfo getCommonNavigationInfo(final Class sourceClass, final Class targetClass) {
    List sourceFields = getAnnotatedFields(sourceClass, EdmNavigationProperty.class);
    List targetFields = getAnnotatedFields(targetClass, EdmNavigationProperty.class);

    if(sourceClass == targetClass) {
      // special case, actual handled as bi-directional
      return getCommonNavigationInfoBiDirectional(sourceClass, targetClass);
    }

    // first try via association name to get full navigation information
    for (Field sourceField : sourceFields) {
      if(ClassHelper.getFieldType(sourceField) == targetClass) {
        final EdmNavigationProperty sourceNav = sourceField.getAnnotation(EdmNavigationProperty.class);
        final String sourceAssociation = extractRelationshipName(sourceNav, sourceField);
        for (Field targetField : targetFields) {
          if(ClassHelper.getFieldType(targetField) == sourceClass) {
            final EdmNavigationProperty targetNav = targetField.getAnnotation(EdmNavigationProperty.class);
            final String targetAssociation = extractRelationshipName(targetNav, targetField);
            if (sourceAssociation.equals(targetAssociation)) {
              return new AnnotatedNavInfo(sourceField, targetField, sourceNav, targetNav);
            }
          }
        }
      }
    }

    // if nothing was found assume/guess none bi-directional navigation
    return getCommonNavigationInfoBiDirectional(sourceClass, targetClass);
  }

  /**
   * Gets the common navigation info bi directional.
   *
   * @param sourceClass the source class
   * @param targetClass the target class
   * @return the common navigation info bi directional
   */
  private AnnotatedNavInfo getCommonNavigationInfoBiDirectional(final Class sourceClass,
                                                                final Class targetClass) {
    List sourceFields = getAnnotatedFields(sourceClass, EdmNavigationProperty.class);

    String targetEntityTypeName = extractEntityTypeName(targetClass);
    for (Field sourceField : sourceFields) {
      final EdmNavigationProperty sourceNav = sourceField.getAnnotation(EdmNavigationProperty.class);
      final String navTargetEntityName = extractEntityTypeName(sourceNav, sourceField);

      if (navTargetEntityName.equals(targetEntityTypeName)) {
        return new AnnotatedNavInfo(null, sourceField, null, sourceNav);
      }
    }

    return null;
  }

  /**
   * Gets the field type for property.
   *
   * @param clazz the clazz
   * @param propertyName the property name
   * @return the field type for property
   * @throws ODataAnnotationException the o data annotation exception
   */
  public Class getFieldTypeForProperty(final Class clazz, final String propertyName)
      throws ODataAnnotationException {
    if (clazz == null) {
      return null;
    }

    Field field = getFieldForPropertyName(propertyName, clazz, true);
    if (field == null) {
      throw new ODataAnnotationException("No field for property '" + propertyName
          + "' found at class '" + clazz + "'.");
    }
    return field.getType();
  }

  /**
   * Gets the field type for property.
   *
   * @param instance the instance
   * @param propertyName the property name
   * @return the field type for property
   * @throws ODataAnnotationException the o data annotation exception
   */
  public Class getFieldTypeForProperty(final Object instance, final String propertyName)
      throws ODataAnnotationException {
    if (instance == null) {
      return null;
    }

    return getFieldTypeForProperty(instance.getClass(), propertyName);
  }

  /**
   * Gets the value for property.
   *
   * @param instance the instance
   * @param propertyName the property name
   * @return the value for property
   * @throws ODataAnnotationException the o data annotation exception
   */
  public Object getValueForProperty(final Object instance, final String propertyName) throws ODataAnnotationException {
    if (instance == null) {
      return null;
    }

    Field field = getFieldForPropertyName(propertyName, instance.getClass(), true);
    if (field == null) {
      throw new ODataAnnotationException("No field for property '" + propertyName
          + "' found at class '" + instance.getClass() + "'.");
    }
    return getFieldValue(instance, field);
  }

  /**
   * Sets the value for property.
   *
   * @param instance the instance
   * @param propertyName the property name
   * @param propertyValue the property value
   */
  public void setValueForProperty(final Object instance, final String propertyName, final Object propertyValue) {
    if (instance != null) {
      Field field = getFieldForPropertyName(propertyName, instance.getClass(), true);
      if (field != null) {
        setFieldValue(instance, field, propertyValue);
      }
    }
  }

  /**
   * Gets the field for property name.
   *
   * @param propertyName the property name
   * @param resultClass the result class
   * @param inherited the inherited
   * @return the field for property name
   */
  private Field getFieldForPropertyName(final String propertyName,
      final Class resultClass, final boolean inherited) {

    Field[] fields = resultClass.getDeclaredFields();
    for (Field field : fields) {
      EdmProperty property = field.getAnnotation(EdmProperty.class);
      if (property != null) {
        if (property.name().isEmpty() && getCanonicalName(field).equals(propertyName)) {
          return field;
        } else if (property.name().equals(propertyName)) {
          return field;
        }
      }
    }

    Class superClass = resultClass.getSuperclass();
    if (inherited && superClass != Object.class) {
      return getFieldForPropertyName(propertyName, superClass, true);
    }

    return null;
  }

  /**
   * Gets the value for field.
   *
   * @param instance the instance
   * @param fieldName the field name
   * @param annotation the annotation
   * @return the value for field
   */
  public Object getValueForField(final Object instance, final String fieldName,
      final Class annotation) {
    if (instance == null) {
      return null;
    }
    return getValueForField(instance, fieldName, instance.getClass(), annotation, true);
  }

  /**
   * Gets the value for field.
   *
   * @param instance the instance
   * @param annotation the annotation
   * @return the value for field
   */
  public Object getValueForField(final Object instance, final Class annotation) {
    if (instance == null) {
      return null;
    }
    return getValueForField(instance, instance.getClass(), annotation, true);
  }

  /**
   * Gets the value for field.
   *
   * @param instance the instance
   * @param resultClass the result class
   * @param annotation the annotation
   * @param inherited the inherited
   * @return the value for field
   */
  private Object getValueForField(final Object instance, final Class resultClass,
      final Class annotation, final boolean inherited) {
    return getValueForField(instance, null, resultClass, annotation, inherited);
  }

  /**
   * Gets the value for annotated fields.
   *
   * @param instance the instance
   * @param annotation the annotation
   * @return the value for annotated fields
   */
  public Map getValueForAnnotatedFields(final Object instance,
      final Class annotation) {
    return getValueForAnnotatedFields(instance, instance.getClass(), annotation, true);
  }

  /**
   * Gets the value for annotated fields.
   *
   * @param instance the instance
   * @param resultClass the result class
   * @param annotation the annotation
   * @param inherited the inherited
   * @return the value for annotated fields
   */
  private Map getValueForAnnotatedFields(final Object instance, final Class resultClass,
      final Class annotation, final boolean inherited) {
    if (instance == null) {
      return null;
    }

    Field[] fields = resultClass.getDeclaredFields();
    Map fieldName2Value = new HashMap();

    for (Field field : fields) {
      if (field.getAnnotation(annotation) != null) {
        Object value = getFieldValue(instance, field);
        final String name = extractPropertyName(field);
        fieldName2Value.put(name, value);
      }
    }

    Class superClass = resultClass.getSuperclass();
    if (inherited && superClass != Object.class) {
      Map tmp = getValueForAnnotatedFields(instance, superClass, annotation, true);
      fieldName2Value.putAll(tmp);
    }

    return fieldName2Value;
  }

  /**
   * Extract property name.
   *
   * @param field the field
   * @return the string
   */
  private String extractPropertyName(final Field field) {
    final EdmProperty property = field.getAnnotation(EdmProperty.class);
    if (property == null || property.name().isEmpty()) {
      return getCanonicalName(field);
    } else {
      return property.name();
    }
  }

  /**
   * Sets the value for annotated field.
   *
   * @param instance the instance
   * @param annotation the annotation
   * @param value the value
   * @throws ODataAnnotationException the o data annotation exception
   */
  public void setValueForAnnotatedField(final Object instance, final Class annotation,
      final Object value)
      throws ODataAnnotationException {
    List fields = getAnnotatedFields(instance, annotation);

    if (fields.isEmpty()) {
      throw new ODataAnnotationException("No field found for annotation '" + annotation
          + "' on instance '" + instance + "'.");
    } else if (fields.size() > 1) {
      throw new ODataAnnotationException("More then one field found for annotation '" + annotation
          + "' on instance '" + instance + "'.");
    }

    setFieldValue(instance, fields.get(0), value);
  }

  /**
   * Sets the values to annotated fields.
   *
   * @param instance the instance
   * @param annotation the annotation
   * @param fieldName2Value the field name 2 value
   */
  public void setValuesToAnnotatedFields(final Object instance,
      final Class annotation, final Map fieldName2Value) {
    List fields = getAnnotatedFields(instance, annotation);

    // XXX: refactore
    for (Field field : fields) {
      final String canonicalName = getCanonicalName(field);
      if (fieldName2Value.containsKey(canonicalName)) {
        Object value = fieldName2Value.get(canonicalName);
        setFieldValue(instance, field, value);
      }
    }
  }

  /**
   * Gets the annotated fields.
   *
   * @param instance the instance
   * @param annotation the annotation
   * @return the annotated fields
   */
  public List getAnnotatedFields(final Object instance, final Class annotation) {
    if (instance == null) {
      return null;
    }
    return getAnnotatedFields(instance.getClass(), annotation, true);
  }

  /**
   * Gets the annotated fields.
   *
   * @param fieldClass the field class
   * @param annotation the annotation
   * @return the annotated fields
   */
  public List getAnnotatedFields(final Class fieldClass, final Class annotation) {
    return getAnnotatedFields(fieldClass, annotation, true);
  }

  /**
   * Gets the annotated fields.
   *
   * @param resultClass the result class
   * @param annotation the annotation
   * @param inherited the inherited
   * @return the annotated fields
   */
  private List getAnnotatedFields(final Class resultClass,
      final Class annotation, final boolean inherited) {
    if (resultClass == null) {
      return null;
    }

    Field[] fields = resultClass.getDeclaredFields();
    List annotatedFields = new ArrayList();

    for (Field field : fields) {
      if (field.getAnnotation(annotation) != null) {
        annotatedFields.add(field);
      }
    }

    Class superClass = resultClass.getSuperclass();
    if (inherited && superClass != Object.class) {
      List tmp = getAnnotatedFields(superClass, annotation, true);
      annotatedFields.addAll(tmp);
    }

    return annotatedFields;
  }

  /**
   * Gets the value for field.
   *
   * @param instance the instance
   * @param fieldName the field name
   * @param resultClass the result class
   * @param annotation the annotation
   * @param inherited the inherited
   * @return the value for field
   */
  private Object getValueForField(final Object instance, final String fieldName, final Class resultClass,
      final Class annotation, final boolean inherited) {
    if (instance == null) {
      return null;
    }

    Field[] fields = resultClass.getDeclaredFields();
    for (Field field : fields) {
      if (field.getAnnotation(annotation) != null
          && (fieldName == null || field.getName().equals(fieldName))) {
        return getFieldValue(instance, field);
      }
    }

    Class superClass = resultClass.getSuperclass();
    if (inherited && superClass != Object.class) {
      return getValueForField(instance, fieldName, superClass, annotation, true);
    }

    return null;
  }

  /**
   * Gets the field value.
   *
   * @param instance the instance
   * @param field the field
   * @return the field value
   */
  private Object getFieldValue(final Object instance, final Field field) {
    try {
      boolean access = field.isAccessible();
      field.setAccessible(true);
      Object value = field.get(instance);
      field.setAccessible(access);
      return value;
    } catch (IllegalArgumentException ex) { // should never happen
      throw new AnnotationRuntimeException(ex);
    } catch (IllegalAccessException ex) { // should never happen
      throw new AnnotationRuntimeException(ex);
    }
  }

  /**
   * Sets the field value.
   *
   * @param instance the instance
   * @param field the field
   * @param value the value
   */
  private void setFieldValue(final Object instance, final Field field, final Object value) {
    try {
      Object usedValue = value;
      if (value != null
          && field.getType() != value.getClass()
          && value.getClass() == String.class) {
        usedValue = convert(field, (String) value);
      }
      boolean access = field.isAccessible();
      field.setAccessible(true);
      field.set(instance, usedValue);
      field.setAccessible(access);
    } catch (IllegalArgumentException ex) { // should never happen
      throw new AnnotationRuntimeException(ex);
    } catch (IllegalAccessException ex) { // should never happen
      throw new AnnotationRuntimeException(ex);
    }
  }

  /**
   * Convert.
   *
   * @param field the field
   * @param propertyValue the property value
   * @return the object
   */
  private Object convert(final Field field, final String propertyValue) {
    Class fieldClass = field.getType();
    try {
      EdmProperty property = field.getAnnotation(EdmProperty.class);
      EdmSimpleTypeKind type = mapTypeKind(property.type());
      return type.getEdmSimpleTypeInstance().valueOfString(propertyValue,
          EdmLiteralKind.DEFAULT, null, fieldClass);
    } catch (EdmSimpleTypeException ex) {
      throw new AnnotationRuntimeException("Conversion failed for string property ["
          + propertyValue + "] on field ["
          + field + "] with error: " + ex.getMessage(), ex);
    }
  }

  /**
   * Checks if is edm annotated.
   *
   * @param object the object
   * @return true, if is edm annotated
   */
  public boolean isEdmAnnotated(final Object object) {
    if (object == null) {
      return false;
    }
    return isEdmAnnotated(object.getClass());
  }

  /**
   * Checks if is edm type annotated.
   *
   * @param clazz the clazz
   * @return true, if is edm type annotated
   */
  public boolean isEdmTypeAnnotated(final Class clazz) {
    boolean isComplexEntity = clazz.getAnnotation(EdmComplexType.class) != null;
    boolean isEntity = clazz.getAnnotation(EdmEntityType.class) != null;
    return isComplexEntity || isEntity;
  }

  /**
   * Checks if is edm annotated.
   *
   * @param clazz the clazz
   * @return true, if is edm annotated
   */
  public boolean isEdmAnnotated(final Class clazz) {
    if (clazz == null) {
      return false;
    } else {
      final boolean isEntity = null != clazz.getAnnotation(EdmEntityType.class);
      final boolean isEntitySet = null != clazz.getAnnotation(EdmEntitySet.class);
      final boolean isComplexEntity = null != clazz.getAnnotation(EdmComplexType.class);
      return isEntity || isComplexEntity || isEntitySet;
    }
  }

  /**
   * Gets the canonical name.
   *
   * @param field the field
   * @return the canonical name
   */
  public String getCanonicalName(final Field field) {
    return firstCharToUpperCase(field.getName());
  }

  /**
   * Gets the canonical name.
   *
   * @param clazz the clazz
   * @return the canonical name
   */
  public String getCanonicalName(final Class clazz) {
    return firstCharToUpperCase(clazz.getSimpleName());
  }

  /**
   * First char to upper case.
   *
   * @param content the content
   * @return the string
   */
  private String firstCharToUpperCase(final String content) {
    if (content == null || content.isEmpty()) {
      return content;
    }
    return content.substring(0, 1).toUpperCase(Locale.ENGLISH) + content.substring(1);
  }

  /**
   * Map type kind.
   *
   * @param type the type
   * @return the edm simple type kind
   */
  public EdmSimpleTypeKind mapTypeKind(final org.apache.olingo.odata2.api.annotation.edm.EdmType type) {
    switch (type) {
    case BINARY:
      return EdmSimpleTypeKind.Binary;
    case BOOLEAN:
      return EdmSimpleTypeKind.Boolean;
    case BYTE:
      return EdmSimpleTypeKind.Byte;
    case COMPLEX:
      return EdmSimpleTypeKind.Null;
    case DATE_TIME:
      return EdmSimpleTypeKind.DateTime;
    case DATE_TIME_OFFSET:
      return EdmSimpleTypeKind.DateTimeOffset;
    case DECIMAL:
      return EdmSimpleTypeKind.Decimal;
    case DOUBLE:
      return EdmSimpleTypeKind.Double;
    case GUID:
      return EdmSimpleTypeKind.Guid;
    case INT16:
      return EdmSimpleTypeKind.Int16;
    case INT32:
      return EdmSimpleTypeKind.Int32;
    case INT64:
      return EdmSimpleTypeKind.Int64;
    case NULL:
      return EdmSimpleTypeKind.Null;
    case SBYTE:
      return EdmSimpleTypeKind.SByte;
    case SINGLE:
      return EdmSimpleTypeKind.Single;
    case STRING:
      return EdmSimpleTypeKind.String;
    case TIME:
      return EdmSimpleTypeKind.Time;
    default:
      throw new AnnotationRuntimeException("Unknown type '" + type
          + "' for mapping to EdmSimpleTypeKind.");
    }
  }

  /**
   * Map multiplicity.
   *
   * @param multiplicity the multiplicity
   * @return the edm multiplicity
   */
  public EdmMultiplicity mapMultiplicity(final Multiplicity multiplicity) {
    switch (multiplicity) {
    case ZERO_OR_ONE:
      return EdmMultiplicity.ZERO_TO_ONE;
    case ONE:
      return EdmMultiplicity.ONE;
    case MANY:
      return EdmMultiplicity.MANY;
    default:
      throw new AnnotationRuntimeException("Unknown type '" + multiplicity + "' for mapping to EdmMultiplicity.");
    }
  }

  /**
   * The Class EdmAnnotationException.
   */
  private static class EdmAnnotationException extends RuntimeException {

    /** The Constant serialVersionUID. */
    private static final long serialVersionUID = 42L;

    /**
     * Instantiates a new edm annotation exception.
     *
     * @param message the message
     */
    public EdmAnnotationException(final String message) {
      super(message);
    }
  }

  /**
   * Gets the canonical namespace.
   *
   * @param aClass the a class
   * @return the canonical namespace
   */
  public String getCanonicalNamespace(final Class aClass) {
    return generateNamespace(aClass);
  }

  /**
   * Extract container name.
   *
   * @param aClass the a class
   * @return the string
   */
  public String extractContainerName(final Class aClass) {
    EdmEntitySet entitySet = aClass.getAnnotation(EdmEntitySet.class);
    if (entitySet != null) {
      String containerName = entitySet.container();
      if (!containerName.isEmpty()) {
        return containerName;
      }
    }
    return DEFAULT_CONTAINER_NAME;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy