package jalse.entities.functions; import static jalse.attributes.Attributes.newNamedUnknownType; import static jalse.entities.functions.Functions.checkNotDefault; import static jalse.entities.functions.Functions.firstGenericTypeArg; import static jalse.entities.functions.Functions.hasReturnType; import static jalse.entities.functions.Functions.isPrimitive; import static jalse.entities.functions.Functions.returnTypeIs; import static jalse.entities.functions.Functions.toClass; import static jalse.entities.functions.Functions.wrap; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.Optional; import jalse.attributes.AttributeContainer; import jalse.entities.DefaultEntityProxyFactory; import jalse.entities.annotations.SetAttribute; import jalse.entities.methods.SetAttributeMethod; /** * This is a method function for {@link SetAttribute} annotation. It will resolve an * {@link SetAttributeMethod} to be used by the entity typing system.<br> * <br> * The next example signatures will resolve to * {@link AttributeContainer#setAttribute(String, jalse.attributes.AttributeType, Object)} supplied * with the name {@code scary}, type {@code Boolean} and the specified input. * * <pre> * <code> * {@code @SetAttribute(name = "scary")} * Boolean setScary(Boolean scary); * * {@code @SetAttribute} * void setScary(Boolean scary); * </code> * </pre> * * The next example signature will resolve to * {@link AttributeContainer#setOptAttribute(String, jalse.attributes.AttributeType, Object)} * supplied with the name {@code scary}, type {@code Boolean} and the specified input. * * <pre> * <code> * {@code @SetAttribute(name = "scary")} * Optional{@code <Boolean>} setScary(Boolean scary); * </code> * </pre> * * When {@code null} is supplied as an input to the above signatures it will be translated to * {@link AttributeContainer#removeAttribute(String, jalse.attributes.AttributeType)}. <br> * <br> * NOTE: This function will throw exceptions if {@link SetAttribute} is present but the method * signature is invalid. * * @author Elliot Ford * * @see DefaultEntityProxyFactory * */ public class SetAttributeFunction implements EntityMethodFunction { private static final String SET_PREFIX = "set"; @Override public SetAttributeMethod apply(final Method m) { // Check for annotation final SetAttribute annonation = m.getAnnotation(SetAttribute.class); if (annonation == null) { return null; } // Basic check method signature checkNotDefault(m); if (m.getParameterCount() != 1) { throw new IllegalArgumentException("Must have only one param"); } // Work out attribute name String name = annonation.name(); if (name.length() == 0) { String methodName = m.getName(); if (methodName.startsWith(SET_PREFIX)) { // setAmount -> Amount methodName = methodName.substring(SET_PREFIX.length()); } // Amount -> amount name = Character.toLowerCase(methodName.charAt(0)) + methodName.substring(1); } // Check suitable name if (name.length() == 0) { // Must have a name. throw new IllegalArgumentException("Attribute name is empty"); } Type attrType = m.getGenericParameterTypes()[0]; boolean primitive = false; // Check not primitive if (isPrimitive(attrType)) { attrType = wrap(toClass(attrType)); primitive = true; } // Check return type matches. final boolean optional = false; if (hasReturnType(m)) { Type returnAttrType = m.getGenericReturnType(); // Check optional if (returnTypeIs(m, Optional.class)) { if (primitive) { throw new IllegalArgumentException("Optional is not required for primitive types"); } returnAttrType = firstGenericTypeArg(returnAttrType); } // Check types match if (!attrType.equals(returnAttrType)) { throw new IllegalArgumentException("Both parameter and return attribute types should match"); } } // Create set attribute method return new SetAttributeMethod(newNamedUnknownType(name, attrType), primitive, optional); } }