Java tutorial
/** * Copyright (C) 2013 cherimojava (http://github.com/cherimojava/cherimodata) 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.github.cherimojava.data.mongo.entity; import static com.google.common.base.Preconditions.checkArgument; import static java.lang.String.format; import static org.apache.commons.lang3.StringUtils.isAllUpperCase; import static org.apache.commons.lang3.StringUtils.isNotEmpty; import static org.apache.commons.lang3.StringUtils.uncapitalize; import java.lang.reflect.Array; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.util.Collection; import java.util.Locale; import javax.inject.Named; import org.apache.commons.lang3.StringUtils; import com.github.cherimojava.data.mongo.entity.annotation.Id; import com.google.common.primitives.Primitives; /** * Utility Class holding commonly used functionality to work with Entities * * @author philnate * @since 1.0.0 */ public class EntityUtils { // TODO add validation that the given methods are really what they're supposed to be /** * Utility class no Need for Instance */ private EntityUtils() { } /** * decapitalizes a String. E.g. CamelCase will become camelCase while URL will stay URL, but URLe becomes uRLe. For * inversion see {@link #capitalize(String)}. * * @param name string to decapitalize} * @return decapitalized string * @see #capitalize(String) */ public static String decapitalize(String name) { if (name.length() == 1) { return name.toLowerCase(Locale.ENGLISH); } if (!isAllUpperCase(name)) { return uncapitalize(name); } else { return name; } } /** * Capitalizes a String. I.e. the first Letter will be converted into uppercase, all other letters will stay as is. * For inversion see {@link #decapitalize(String)}. * * @param name string to capitalize. * @return capitalized string * @see #decapitalize(String) */ public static String capitalize(String name) { return StringUtils.capitalize(name); } /** * Retrieves the pojo name from the given method if this is a valid set/get/add method * * @param m method to retrieve name from * @return propertyname derived from the given method */ static String getPojoNameFromMethod(Method m) { String methodName = m.getName(); boolean isMethod = methodName.startsWith("is"); checkArgument( ((methodName.startsWith("set") || methodName.startsWith("get") || methodName.startsWith("add")) && m.getName().length() > 3) || (isMethod && m.getName().length() > 2), "Don't know how to retrieve name from this method, got [%s]", m.getName()); // depending on what type of method it's extract the name return decapitalize(m.getName().substring(!isMethod ? 3 : 2)); } /** * retrieves the name of the property which is used on mongodb side. Accepts only get methods, will throw an * exception for all other methods * * @param m method to retrieve mongo name from * @return mongo name for the given getter method (parameter) */ static String getMongoNameFromMethod(Method m) { checkArgument(m.getName().startsWith("get") || m.getName().startsWith("is"), "Mongo name can only be retrieved from get/is methods, but was %s", m.getName()); checkArgument(!(m.isAnnotationPresent(Named.class) && m.isAnnotationPresent(Id.class)), "You can not annotate a property with @Name and @Id"); if (m.isAnnotationPresent(Named.class)) { checkArgument(!Entity.ID.equals(m.getAnnotation(Named.class).value()), "It's not allowed to use @Name annotation to declare id field, instead use @Id annotation"); return m.getAnnotation(Named.class).value(); } if (m.isAnnotationPresent(Id.class) || "id".equals(getPojoNameFromMethod(m).toLowerCase(Locale.US))) { return Entity.ID; } // differentiate between get and is methods return decapitalize(m.getName().substring(m.getName().startsWith("get") ? 3 : 2)); } /** * retrieves the name for the Entity, which might be different from the clazz name if the @Named annotation is * present * * @param clazz Entity from which to retrieve the collection name * @return the name of the Entity as declared with the Named annotation or the clazz name if annotation isn't * present as plural */ public static String getCollectionName(Class<? extends Entity> clazz) { Named name = clazz.getAnnotation(Named.class); if (name != null && isNotEmpty(name.value())) { return name.value(); } return uncapitalize(clazz.getSimpleName() + "s"); } /** * returns the setter method of the given getter method or throws an exception if no such method exists * * @param m getter method for which a matching setter method shall be found * @return setter method belonging to the given getter method * @throws java.lang.IllegalArgumentException if the given getter Method has no setter method */ static Method getSetterFromGetter(Method m) { try { return m.getDeclaringClass() .getMethod(m.getName().startsWith("get") ? m.getName().replaceFirst("g", "s") : m.getName().replaceFirst("is", "set"), m.getReturnType()); } catch (NoSuchMethodException e) { throw new IllegalArgumentException(format("Method %s has no corresponding setter method", m.getName())); } } /** * returns the getter method matching the given Adder method or throws an exception if no such method exists * * @param m adder method for which the matching getter method shall be found * @return getter method belonging to the given Adder method * @throws java.lang.IllegalArgumentException if the given Adder method has no corresponding getter method */ static Method getGetterFromAdder(Method m) { try { return m.getDeclaringClass().getMethod(m.getName().replaceFirst("add", "get")); } catch (NoSuchMethodException e) { throw new IllegalArgumentException(format("Method %s has no corresponding getter method", m.getName())); } } /** * returns the adder method matching the given Getter method or throws an exception if no such method exists * * @param m getter method for which a matching adder method shall be found * @return adder method belongign to the given Getter method * @throws java.lang.IllegalArgumentException if the given method has no matching adder method */ static Method getAdderFromGetter(Method m) { try { return m.getDeclaringClass().getMethod(m.getName().replaceFirst("get", "add"), (Class) ((ParameterizedType) m.getGenericReturnType()).getActualTypeArguments()[0]); } catch (Exception e) { try { // if we had no hit, try if there's a vararg adder return m.getDeclaringClass().getMethod(m.getName().replaceFirst("get", "add"), Array.newInstance( (Class) ((ParameterizedType) m.getGenericReturnType()).getActualTypeArguments()[0], 0).getClass()); } catch (Exception e1) { throw new IllegalArgumentException( format("Method %s has no corresponding adder method", m.getName())); } } } /** * returns the getter method belonging to the given setter method * * @param m setter method for which a matching getter method shall be found * @return getter method belonging to the given Setter method * @throws java.lang.IllegalArgumentException if the given method has no matching adder method */ static Method getGetterFromSetter(Method m) { try { Method getter = m.getDeclaringClass().getMethod(m.getName().replaceFirst("s", "g")); checkArgument(getter.getReturnType().equals(m.getParameterTypes()[0]), "You can only declare setter methods if there's a matching getter. Found %s without getter", m.getName()); return getter; } catch (NoSuchMethodException e) { try { // check if there's a is method available Method isser = m.getDeclaringClass().getMethod(m.getName().replaceFirst("set", "is")); // check that both have boolean as type checkArgument(boolean.class.equals(Primitives.unwrap(isser.getReturnType())) && boolean.class.equals(Primitives.unwrap(m.getParameterTypes()[0]))); return isser; } catch (NoSuchMethodException e1) { // if neither get nor is can be found throw an exception throw new IllegalArgumentException( format("Method %s has no corresponding get/is method", m.getName())); } } } /** * checks if the given method has a return type which is assignable from the declaring class * * @return true if the given method return type is assignable from the declaring class, false otherwise */ static boolean isAssignableFromClass(Method m) { return m.getReturnType().isAssignableFrom(m.getDeclaringClass()); } /** * marks an entity as persisted, which means that certain things might not be changed after on. * * @param e */ public static void persist(Entity e) { EntityInvocationHandler.getHandler(e).persist(); } /** * returns if the given Entity is already persisted or not * * @param e * @return */ public static boolean isPersisted(Entity e) { return EntityInvocationHandler.getHandler(e).persisted; } /** * returns true if this getter methods return type is either an entity or a list of entities. Otherwise false * * @param getter * @return true if return type is entity or list of entities */ public static boolean isValidReferenceClass(Method getter) { return (Entity.class.isAssignableFrom(getter.getReturnType()) || (Collection.class.isAssignableFrom(getter.getReturnType()) && Entity.class.isAssignableFrom( (Class) ((ParameterizedType) getter.getGenericReturnType()).getActualTypeArguments()[0]))); } }