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

com.microsoft.windowsazure.services.table.client.PropertyPair Maven / Gradle / Ivy

There is a newer version: 0.4.6
Show newest version
/**
 * Copyright Microsoft Corporation
 * 
 * Licensed 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 com.microsoft.windowsazure.services.table.client;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.UUID;

import com.microsoft.windowsazure.services.core.storage.utils.Utility;

/**
 * Reserved for internal use. A class used internally during the reflection process to determine which properties should
 * be serialized.
 */
class PropertyPair {
    /**
     * Reserved for internal use. A static factory method to generate a map of property names to {@link PropertyPair}
     * instances for the specified class type. Uses reflection to find pairs of getter and setter methods that are
     * annotated with {@link StoreAs} with a common property name, or of the form getPropertyName
     * and setPropertyName, with a common type for the getter return value and the
     * setter parameter, and stores the methods and the property name for each pair found in a map for use in
     * serializing and deserializing entity data.
     * 
     * @param clazzType
     *            The class type to check for matching getter and setter methods with a common return and parameter
     *            type, respectively.
     */
    protected static HashMap generatePropertyPairs(final Class clazzType) {
        final Method[] methods = clazzType.getMethods();
        final HashMap propMap = new HashMap();

        String propName = null;
        PropertyPair currProperty = null;

        for (final Method m : methods) {
            if (m.getName().length() < 4 || (!m.getName().startsWith("get") && !m.getName().startsWith("set"))) {
                continue;
            }

            // TODO add logging
            // System.out.println(m.getName());

            propName = m.getName().substring(3);

            // Skip interface methods, these will be called explicitly
            if (propName.equals(TableConstants.PARTITION_KEY) || propName.equals(TableConstants.ROW_KEY)
                    || propName.equals(TableConstants.TIMESTAMP) || propName.equals("Etag")
                    || propName.equals("LastModified")) {
                continue;
            }

            if (propMap.containsKey(propName)) {
                currProperty = propMap.get(propName);
            }
            else {
                currProperty = new PropertyPair();
                currProperty.name = propName;
                propMap.put(propName, currProperty);
            }

            // TODO add logging
            // System.out.println(m.getReturnType());
            if (m.getName().startsWith("get") && m.getParameterTypes().length == 0) {
                currProperty.getter = m;
            }
            else if (m.getName().startsWith("set") && m.getParameterTypes().length == 1
                    && void.class.equals(m.getReturnType())) {
                currProperty.setter = m;
            }

            // Check for StoreAs Annotation
            final StoreAs storeAsInstance = m.getAnnotation(StoreAs.class);
            if (storeAsInstance != null) {
                if (Utility.isNullOrEmpty(storeAsInstance.name())) {
                    throw new IllegalArgumentException(String.format(
                            "StoreAs Annotation found for property %s with empty value", currProperty.name));
                }

                if (currProperty.effectiveName != null && !currProperty.effectiveName.equals(currProperty.name)
                        && !currProperty.effectiveName.equals(storeAsInstance.name())) {
                    throw new IllegalArgumentException(
                            String.format(
                                    "StoreAs Annotation found for both getter and setter for property %s with non equal values",
                                    currProperty.name));
                }

                if (!currProperty.name.equals(storeAsInstance.name())) {
                    currProperty.effectiveName = storeAsInstance.name();
                }
            }
        }

        // Return only processable pairs
        final ArrayList keysToRemove = new ArrayList();
        final ArrayList keysToAlter = new ArrayList();

        for (final Entry e : propMap.entrySet()) {
            if (!e.getValue().shouldProcess()) {
                keysToRemove.add(e.getKey());
                continue;
            }

            if (!Utility.isNullOrEmpty(e.getValue().effectiveName)) {
                keysToAlter.add(e.getKey());
            }
            else {
                e.getValue().effectiveName = e.getValue().name;
            }
        }

        // remove all entries for keys that should not process
        for (final String key : keysToRemove) {
            propMap.remove(key);
        }

        // Any store as properties should be re-stored into the hash under the efective name.
        for (final String key : keysToAlter) {
            final PropertyPair p = propMap.get(key);
            propMap.remove(key);
            propMap.put(p.effectiveName, p);
        }

        return propMap;
    }

    private Method getter = null;
    private Method setter = null;
    private String name = null;
    String effectiveName = null;

    /**
     * Reserved for internal use. Invokes the setter method on the specified instance parameter with the value of the
     * {@link EntityProperty} deserialized as the appropriate type.
     * 
     * @param prop
     *            The {@link EntityProperty} containing the value to pass to the setter on the instance.
     * @param instance
     *            An instance of a class supporting this property with getter and setter methods of the
     *            appropriate name and parameter or return type.
     * 
     * @throws IllegalArgumentException
     *             if the specified instance parameter is not an instance of the class
     *             or interface declaring the setter method (or of a subclass or implementor thereof).
     * @throws IllegalAccessException
     *             if the setter method is inaccessible.
     * @throws InvocationTargetException
     *             if the setter method throws an exception.
     */
    protected void consumeTableProperty(final EntityProperty prop, final Object instance)
            throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
        if (prop.getEdmType() == EdmType.STRING) {
            this.setter.invoke(instance, prop.getValueAsString());
        }
        else if (prop.getEdmType() == EdmType.BINARY) {
            if (this.setter.getParameterTypes()[0].equals(Byte[].class)) {
                this.setter.invoke(instance, (Object) prop.getValueAsByteObjectArray());
            }
            else {
                this.setter.invoke(instance, prop.getValueAsByteArray());
            }
        }
        else if (prop.getEdmType() == EdmType.BOOLEAN) {
            this.setter.invoke(instance, prop.getValueAsBoolean());
        }
        else if (prop.getEdmType() == EdmType.DOUBLE) {
            this.setter.invoke(instance, prop.getValueAsDouble());
        }
        else if (prop.getEdmType() == EdmType.GUID) {
            this.setter.invoke(instance, prop.getValueAsUUID());
        }
        else if (prop.getEdmType() == EdmType.INT32) {
            this.setter.invoke(instance, prop.getValueAsInteger());
        }
        else if (prop.getEdmType() == EdmType.INT64) {
            this.setter.invoke(instance, prop.getValueAsLong());
        }
        else if (prop.getEdmType() == EdmType.DATE_TIME) {
            this.setter.invoke(instance, prop.getValueAsDate());
        }
        else {
            throw new IllegalArgumentException(String.format("Property %s with Edm Type %s cannot be de-serialized.",
                    this.name, prop.getEdmType().toString()));
        }
    }

    /**
     * Reserved for internal use. Generates an {@link EntityProperty} from the result of invoking the getter method for
     * this property on the specified instance parameter.
     * 
     * @param instance
     *            An instance of a class supporting this property with getter and setter methods of the
     *            appropriate name and parameter or return type.
     * 
     * @return
     *         An {@link EntityProperty} with the data type and value returned by the invoked getter on the instance.
     * 
     * @throws IllegalArgumentException
     *             if the specified instance parameter is not an instance of the class
     *             or interface declaring the getter method (or of a subclass or implementor thereof).
     * @throws IllegalAccessException
     *             if the getter method is inaccessible.
     * @throws InvocationTargetException
     *             if the getter method throws an exception.
     */
    protected EntityProperty generateTableProperty(final Object instance) throws IllegalArgumentException,
            IllegalAccessException, InvocationTargetException {
        final Class getType = this.getter.getReturnType();
        Object val = this.getter.invoke(instance, (Object[]) null);

        if (getType.equals(byte[].class)) {
            return val != null ? new EntityProperty((byte[]) val) : new EntityProperty(EdmType.BINARY);
        }
        else if (getType.equals(Byte[].class)) {
            return val != null ? new EntityProperty((Byte[]) val) : new EntityProperty(EdmType.BINARY);
        }
        else if (getType.equals(String.class)) {
            return val != null ? new EntityProperty((String) val) : new EntityProperty(EdmType.STRING);
        }
        else if (getType.equals(boolean.class) || getType.equals(Boolean.class)) {
            return val != null ? new EntityProperty((Boolean) val) : new EntityProperty(EdmType.BOOLEAN);
        }
        else if (getType.equals(double.class) || getType.equals(Double.class)) {
            return val != null ? new EntityProperty((Double) val) : new EntityProperty(EdmType.DOUBLE);
        }
        else if (getType.equals(UUID.class)) {
            return val != null ? new EntityProperty((UUID) val) : new EntityProperty(EdmType.GUID);
        }
        else if (getType.equals(int.class) || getType.equals(Integer.class)) {
            return val != null ? new EntityProperty((Integer) val) : new EntityProperty(EdmType.INT32);
        }
        else if (getType.equals(long.class) || getType.equals(Long.class)) {
            return val != null ? new EntityProperty((Long) val) : new EntityProperty(EdmType.INT64);
        }
        else if (getType.equals(Date.class)) {
            return val != null ? new EntityProperty((Date) val) : new EntityProperty(EdmType.DATE_TIME);
        }
        else {
            throw new IllegalArgumentException(String.format("Property %s with return type %s cannot be serialized.",
                    this.getter.getName(), this.getter.getReturnType()));
        }
    }

    /**
     * Reserved for internal use. A utility function that returns true if this property is accessible
     * through reflection.
     * 
     * @return
     */
    protected boolean shouldProcess() {
        if (Utility.isNullOrEmpty(this.name) || this.getter == null || this.getter.isAnnotationPresent(Ignore.class)
                || this.setter == null || this.setter.isAnnotationPresent(Ignore.class)
                || (!this.getter.getReturnType().equals(this.setter.getParameterTypes()[0]))) {
            return false;
        }

        // TODO add logging
        // System.out.println("Valid property " + this.name + " Storing as " + this.effectiveName);
        return true;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy