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

net.java.ao.Common Maven / Gradle / Ivy

/*
 * Copyright 2007 Daniel Spiewak
 * 
 * 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 net.java.ao;

import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import net.java.ao.schema.FieldNameConverter;
import net.java.ao.schema.FieldNameProcessor;
import net.java.ao.schema.Ignore;
import net.java.ao.schema.PrimaryKey;
import net.java.ao.schema.info.EntityInfo;
import net.java.ao.schema.info.FieldInfo;
import net.java.ao.sql.SqlUtils;
import net.java.ao.types.TypeInfo;
import net.java.ao.types.TypeManager;
import net.java.ao.util.StringUtils;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.Lists.newArrayList;

/**
 * WARNING: Not part of the public API.  This class is public only
 * to allow its use within other packages in the ActiveObjects library.
 *
 * @author Daniel Spiewak
 */
public final class Common {

    public static , K> T createPeer(EntityManager manager, Class type, K key) throws SQLException
    {
        return manager.peer(manager.resolveEntityInfo(type), key);
    }

	public static String convertSimpleClassName(String name) {
		String[] array = name.split("\\.");
		return array[array.length - 1];
	}

	public static String convertDowncaseName(String name) {
		StringBuilder back = new StringBuilder();

		back.append(Character.toLowerCase(name.charAt(0)));
		back.append(name.substring(1));

		return back.toString();
	}

    /**
     * @see AO-325
     */
    @Deprecated
    public static String[] getMappingFields(FieldNameConverter converter, Class> from, Class> to)
    {
		Set back = new LinkedHashSet();

		for (Method method : from.getMethods()) {
			Class attributeType = getAttributeTypeFromMethod(method);

			if (attributeType == null) {
				continue;
			}

            if (to.isAssignableFrom(attributeType)) {
				back.add(converter.getName(method));
			} else if (attributeType.getAnnotation(Polymorphic.class) != null
					&& attributeType.isAssignableFrom(to)) {
				back.add(converter.getName(method));
			}
		}

		return back.toArray(new String[back.size()]);
	}

    /**
     * @see AO-325
     */
    @Deprecated
    public static String[] getPolymorphicFieldNames(FieldNameConverter converter, Class> from,
			Class> to) {
		Set back = new LinkedHashSet();

		for (Method method : from.getMethods()) {
			Class attributeType = getAttributeTypeFromMethod(method);

            if (attributeType != null && attributeType.isAssignableFrom(to)
					&& attributeType.getAnnotation(Polymorphic.class) != null) {
				back.add(converter.getPolyTypeName(method));
			}
		}

		return back.toArray(new String[back.size()]);
	}

	/**
	 * Note: this method leads to the creation and quick discard of
	 * large numbers of {@link AnnotationDelegate} objects.  Need to
	 * do some research to determine whether or not this is actually
	 * a problem.
     *
     * @deprecated All annotation information should be resolved upfront using {@link net.java.ao.schema.info.EntityInfo}
	 */
    @Deprecated
	public static AnnotationDelegate getAnnotationDelegate(FieldNameConverter converter, Method method) {
		return new AnnotationDelegate(method, findCounterpart(converter, method));
	}

	/**
	 * Finds the corresponding method in an accessor/mutator pair based
	 * on the given method (or null if no corresponding method).
	 * @param converter TODO
	 */
	private static Method findCounterpart(FieldNameConverter converter, Method method) {
        return MethodFinder.getInstance().findCounterPartMethod(converter, method);
	}

    public static boolean isMutator(Method method)
    {
        return (isAnnotatedMutator(method) || isNamedAsSetter(method)) && isValidMutator(method);
    }

    public static boolean isAnnotatedMutator(Method method)
    {
        return method.isAnnotationPresent(Mutator.class);
    }

    private static boolean isNamedAsSetter(Method method)
    {
        return method.getName().startsWith("set");
    }

    private static boolean isValidMutator(Method method)
    {
        return method.getReturnType() == Void.TYPE && method.getParameterTypes().length == 1;
    }

    public static boolean isAccessor(Method method)
    {
        return (isAnnotatedAccessor(method) || isNamedAsGetter(method)) && isValidAccessor(method);
    }

    private static boolean isAnnotatedAccessor(Method method)
    {
        return method.isAnnotationPresent(Accessor.class);
    }

    private static boolean isNamedAsGetter(Method method)
    {
        return method.getName().startsWith("get")
                || method.getName().startsWith("is");
    }

    private static boolean isValidAccessor(Method method)
    {
        return method.getReturnType() != Void.TYPE && method.getParameterTypes().length == 0;
    }

    public static boolean isAnnotatedAsRelational(Method method)
    {
        return method.isAnnotationPresent(OneToOne.class)
                || method.isAnnotationPresent(OneToMany.class)
                || method.isAnnotationPresent(ManyToMany.class);
    }

    public static Class getAttributeTypeFromMethod(Method method)
    {
        if (isAnnotatedAsRelational(method))
        {
            return null;
        }
        if (isMutator(method))
        {
            return getMutatorParameterType(method);
        }
        if (isAccessor(method))
        {
            return getAccessorReturnType(method);
        }
        return null;
    }

    private static Class getMutatorParameterType(Method method)
    {
        checkArgument(isValidMutator(method), "Method '%s' on class '%s' is not a valid mutator", method.getName(), method.getDeclaringClass().getCanonicalName());
        return method.getParameterTypes()[0];
    }

    private static Class getAccessorReturnType(Method method)
    {
        checkArgument(isValidAccessor(method), "Method '%s' on class '%s' is not a valid accessor", method.getName(), method.getDeclaringClass().getCanonicalName());
        return method.getReturnType();
    }

    public static String getCallingClassName(int depth) {
        StackTraceElement[] stack = new Exception().getStackTrace();
        return stack[depth + 2].getClassName();
    }

	public static List getSearchableFields(EntityManager manager, Class> type) {
		List back = new ArrayList();

		for (Method m : type.getMethods()) {
			Searchable annot = getAnnotationDelegate(manager.getNameConverters().getFieldNameConverter(), m).getAnnotation(Searchable.class);

			if (annot != null) {
				Class attributeType = Common.getAttributeTypeFromMethod(m);
				String name = manager.getNameConverters().getFieldNameConverter().getName(m);

				// don't index Entity fields
                if (name != null && !RawEntity.class.isAssignableFrom(attributeType) && !back.contains(name)) {
					back.add(name);
				}
			}
		}

		return back;
	}

    private static Method getPrimaryKeyAccessor(Class> type)
    {
        final Iterable methods = MethodFinder.getInstance().findAnnotatedMethods(PrimaryKey.class, type);
        if (Iterables.isEmpty(methods))
        {
            throw new RuntimeException("Entity " + type.getSimpleName() + " has no primary key field");
        }

        for (Method method : methods)
        {
            if (!method.getReturnType().equals(Void.TYPE) && method.getParameterTypes().length == 0)
            {
                return method;
            }
        }

        return null;
    }

    public static String getPrimaryKeyField(Class> type, FieldNameConverter converter)
    {
        final Iterable methods = MethodFinder.getInstance().findAnnotatedMethods(PrimaryKey.class, type);
        if (Iterables.isEmpty(methods))
        {
            throw new RuntimeException("Entity " + type.getSimpleName() + " has no primary key field");
        }
        return converter.getName(methods.iterator().next());
    }

    public static  TypeInfo getPrimaryKeyType(TypeManager typeManager, Class> type) {
		return typeManager.getType(getPrimaryKeyClassType(type));
	}

    public static  Class getPrimaryKeyClassType(Class> type)
    {
        final Iterable methods = MethodFinder.getInstance().findAnnotatedMethods(PrimaryKey.class, type);
        if (Iterables.isEmpty(methods))
        {
            throw new RuntimeException("Entity " + type.getSimpleName() + " has no primary key field");
        }

        final Method m = methods.iterator().next();

        Class keyType = (Class) m.getReturnType();
        if (keyType.equals(Void.TYPE))
        {
            keyType = (Class) m.getParameterTypes()[0];
        }
        return keyType;
    }

    public static  K getPrimaryKeyValue(RawEntity entity) {
        if (entity instanceof EntityProxyAccessor) {
            return (K) ((EntityProxyAccessor) entity).getEntityProxy().getKey();
        }
		try {
			return (K) Common.getPrimaryKeyAccessor(entity.getEntityType()).invoke(entity);
		} catch (IllegalArgumentException e) {
			return null;
		} catch (IllegalAccessException e) {
			return null;
		} catch (InvocationTargetException e) {
			return null;
		}
	}

    public static  void validatePrimaryKey(FieldInfo primaryKeyInfo, Object value)
    {
        if(null == value)
        {
            throw new IllegalArgumentException("Cannot set primary key to NULL");
        }

        TypeInfo typeInfo = primaryKeyInfo.getTypeInfo();
        Class javaTypeClass = primaryKeyInfo.getJavaType();

        if(!typeInfo.isAllowedAsPrimaryKey())
        {
            throw new ActiveObjectsException(javaTypeClass.getName() + " cannot be used as a primary key!");
        }

        typeInfo.getLogicalType().validate(value);

        if((value instanceof String) && StringUtils.isBlank((String)value))
        {
            throw new ActiveObjectsException("Cannot set primary key to blank String");
        }
    }

	public static boolean fuzzyCompare(TypeManager typeManager, Object a, Object b) {
		if (a == null && b == null) {
			return true;
		} else if (a == null || b == null) {	// implicitly, one or other is null, not both
			return false;
		}

		Object array = null;
		Object other = null;

		if (a.getClass().isArray()) {
			array = a;
			other = b;
		} else if (b.getClass().isArray()) {
			array = b;
			other = a;
		}

		if (array != null) {
			for (int i = 0; i < Array.getLength(array); i++) {
				if (fuzzyCompare(typeManager, Array.get(array, i), other)) {
					return true;
				}
			}
		}

		return typeManager.getType(a.getClass()).getLogicalType().valueEquals(a, b)
			|| typeManager.getType(b.getClass()).getLogicalType().valueEquals(b, a);
	}

	public static boolean fuzzyTypeCompare(int typeA, int typeB) {
		if (typeA == Types.BOOLEAN) {
			switch (typeB) {
				case Types.BIGINT:
					return true;

				case Types.BIT:
					return true;

				case Types.INTEGER:
					return true;

				case Types.NUMERIC:
					return true;

				case Types.SMALLINT:
					return true;

				case Types.TINYINT:
					return true;
			}
		}

		if ((typeA == Types.BIGINT || typeA == Types.BIT || typeA == Types.INTEGER || typeA == Types.NUMERIC
				|| typeA == Types.SMALLINT || typeA == Types.TINYINT) && typeB == Types.BOOLEAN) {
			return true;
		} else if (typeA == Types.CLOB && (typeB == Types.LONGVARCHAR || typeB == Types.VARCHAR)) {
			return true;
		} else if ((typeA == Types.LONGVARCHAR || typeA == Types.VARCHAR) && typeB == Types.CLOB) {
			return true;
		} else if ((typeA == Types.BIGINT || typeA == Types.BIT || typeA == Types.DECIMAL || typeA == Types.DOUBLE
				|| typeA == Types.FLOAT || typeA == Types.INTEGER || typeA == Types.REAL || typeA == Types.SMALLINT
				|| typeA == Types.TINYINT) && typeB == Types.NUMERIC) {
			return true;
		}

		return typeA == typeB;
	}

    /**
     * Gets all the methods of an entity that correspond to a value field. This means fields that are stored as values
     * in the database as opposed to fields (IDs) that define a relationship to another table in the database.
     * Note that the values are retrieved based on the relationship annotations, at the field level,
     * which the user may not have entered.
     * @param entity the entity to look up the methods from
     * @param converter the field name converter currently in use for entities
     * @return the set of method found
     */
    public static Set getValueFieldsMethods(final Class> entity, final FieldNameConverter converter)
    {
        return Sets.filter(Sets.newHashSet(entity.getMethods()), new Predicate()
        {
            public boolean apply(Method m)
            {
                final AnnotationDelegate annotations = getAnnotationDelegate(converter, m);
                return !annotations.isAnnotationPresent(Ignore.class)
                        && !annotations.isAnnotationPresent(OneToOne.class)
                        && !annotations.isAnnotationPresent(OneToMany.class)
                        && !annotations.isAnnotationPresent(ManyToMany.class);
            }
        });
    }

    /**
     * Gets all the names of fields of an entity that correspond to a value field. This means fields that are stored as
     * values in the database as opposed to fields (IDs) that define a relationship to another table in the database.
     * @param entityInfo the entity to look up the methods from
     * @return the set of names found
     */
    public static ImmutableSet getValueFieldsNames(final EntityInfo, ?> entityInfo, final FieldNameConverter converter)
    {
        List valueFieldsNames = new ArrayList();

        for (FieldInfo fieldInfo : entityInfo.getFields())
        {
            // filter out just the value fields - we need to remove any entities from polymorphic relationships
            if (!Entity.class.isAssignableFrom(fieldInfo.getJavaType()))
            {
                // apply the name converter if we have a getter
                if (fieldInfo.hasAccessor())
                {
                    valueFieldsNames.add(converter.getName(fieldInfo.getAccessor()));
                }
            }
        }

        return ImmutableSet.copyOf(valueFieldsNames);
    }

    public static List preloadValue(Preload preload, final FieldNameConverter fnc)
    {
        final List value = newArrayList(preload.value());
        if (fnc instanceof FieldNameProcessor)
        {
            return Lists.transform(value, new Function()
            {
                @Override
                public String apply(String from)
                {
                    return ((FieldNameProcessor) fnc).convertName(from);
                }
            });
        }
        else
        {
            return value;
        }
    }

    public static String where(OneToOne oneToOne, final FieldNameConverter fnc)
    {
        return where(oneToOne.where(), fnc);
    }

    public static String where(OneToMany oneToMany, final FieldNameConverter fnc)
    {
        return where(oneToMany.where(), fnc);
    }

    public static String where(ManyToMany manyToMany, final FieldNameConverter fnc)
    {
        return where(manyToMany.where(), fnc);
    }

    private static String where(String where, final FieldNameConverter fnc)
    {
        if (fnc instanceof FieldNameProcessor)
        {
            final Matcher matcher = SqlUtils.WHERE_CLAUSE.matcher(where);
            final StringBuffer sb = new StringBuffer();
            while (matcher.find())
            {
                matcher.appendReplacement(sb, convert(fnc, matcher.group(1)));
            }
            matcher.appendTail(sb);
            return sb.toString();
        }
        else
        {
            return where;
        }
    }

    public static String convert(FieldNameConverter fnc, String column)
    {
        if (fnc instanceof FieldNameProcessor)
        {
            return ((FieldNameProcessor) fnc).convertName(column);
        }
        else
        {
            return column;
        }
    }

    /**
     * Closes the {@link java.sql.ResultSet} in a {@code null} safe manner and quietly, i.e without throwing nor logging
     * any exception
     * @param resultSet the result set to close
     */
    @Deprecated
    public static void closeQuietly(ResultSet resultSet)
    {
        if (resultSet != null)
        {
            try
            {
                resultSet.close();
            }
            catch (SQLException e)
            {
                // ignored
            }
        }
    }

    /**
     * Closes the {@link java.sql.Statement} in a {@code null} safe manner and quietly, i.e without throwing nor logging
     * any exception
     * @param statement the statement to close
     */
    @Deprecated
    public static void closeQuietly(Statement statement)
    {
        if (statement != null)
        {
            try
            {
                statement.close();
            }
            catch (SQLException e)
            {
                // ignored
            }
        }
    }

    /**
     * Closes the {@link java.sql.Connection} in a {@code null} safe manner and quietly, i.e without throwing nor logging
     * any exception
     * @param connection the connection to close, can be {@code null}
     */
    @Deprecated
    public static void closeQuietly(Connection connection)
    {
        if (connection != null)
        {
            try
            {
                connection.close();
            }
            catch (SQLException e)
            {
                // ignored
            }
        }
    }

    /**
     * Shortens a String to the given length if necessary. The process of shortening is stable.
     */
    public static String shorten(String s, int length)
    {
        if (s == null || s.length() <= length)
        {
            return s;
        }

        final int tailLength = length / 3;
        final int hash = Math.abs((int) (s.hashCode() % Math.round(Math.pow(10, tailLength))));

        return s.substring(0, length - tailLength - 1) + hash;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy