Java tutorial
/* * #%L * ACS AEM Commons Bundle * %% * Copyright (C) 2017 Adobe * %% * 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. * #L% */ package com.adobe.acs.commons.mcp.util; import com.adobe.acs.commons.mcp.form.FieldComponent; import com.adobe.acs.commons.mcp.form.FormField; import org.apache.commons.lang3.reflect.FieldUtils; import org.apache.sling.api.resource.ValueMap; import org.apache.sling.api.scripting.SlingScriptHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.text.NumberFormat; import java.text.ParseException; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import static com.adobe.acs.commons.mcp.util.IntrospectionUtil.getCollectionComponentType; import static com.adobe.acs.commons.mcp.util.IntrospectionUtil.hasMultipleValues; import static com.adobe.acs.commons.mcp.util.ValueMapSerializer.serializeToStringArray; import java.io.IOException; import org.apache.sling.api.request.RequestParameter; /** * Processing routines for handing ProcessInput within a FormProcessor */ public class AnnotatedFieldDeserializer { private static final Logger LOG = LoggerFactory.getLogger(AnnotatedFieldDeserializer.class); public static void deserializeFormFields(Object target, ValueMap input) throws DeserializeException { List<Field> fields = FieldUtils.getFieldsListWithAnnotation(target.getClass(), FormField.class); deserializeFields(target, fields, input); } public static void deserializeFields(Object target, List<Field> fields, ValueMap input) throws DeserializeException { for (Field field : fields) { try { parseInput(target, input, field); } catch (ParseException | ReflectiveOperationException | NullPointerException ex) { throw new DeserializeException("Error when processing field " + field.getName(), ex); } } } @SuppressWarnings("squid:S3776") private static void parseInput(Object target, ValueMap input, Field field) throws ReflectiveOperationException, ParseException { FormField inputAnnotation = field.getAnnotation(FormField.class); Object value; if (input.get(field.getName()) == null) { if (inputAnnotation != null && inputAnnotation.required()) { if (field.getType() == Boolean.class || field.getType() == Boolean.TYPE) { value = false; } else { throw new NullPointerException("Required field missing: " + field.getName()); } } else { return; } } else { value = input.get(field.getName()); } if (hasMultipleValues(field.getType())) { parseInputList(target, serializeToStringArray(value), field); } else { Object val = value; if (value.getClass().isArray()) { val = ((Object[]) value)[0]; } if (val instanceof RequestParameter) { /** Special case handling uploaded files; Method call ~ copied from parseInputValue(..) **/ if (field.getType() == RequestParameter.class) { FieldUtils.writeField(field, target, val, true); } else { try { FieldUtils.writeField(field, target, ((RequestParameter) val).getInputStream(), true); } catch (IOException ex) { LOG.error("Unable to get InputStream for uploaded file [ {} ]", ((RequestParameter) val).getName(), ex); } } } else { parseInputValue(target, String.valueOf(val), field); } } } private static void parseInputList(Object target, String[] values, Field field) throws ReflectiveOperationException, ParseException { List convertedValues = new ArrayList(); Class type = getCollectionComponentType(field); for (String value : values) { Object val = convertValue(value, type); convertedValues.add(val); } if (field.getType().isArray()) { Object array = Array.newInstance(field.getType().getComponentType(), convertedValues.size()); for (int i = 0; i < convertedValues.size(); i++) { Array.set(array, i, convertedValues.get(i)); } FieldUtils.writeField(field, target, array, true); } else { Collection c = (Collection) getInstantiatableListType(field.getType()).newInstance(); c.addAll(convertedValues); FieldUtils.writeField(field, target, c, true); } } private static void parseInputValue(Object target, String value, Field field) throws ReflectiveOperationException, ParseException { FieldUtils.writeField(field, target, convertValue(value, field.getType()), true); } private static Object convertValue(String value, Class<?> type) throws ParseException { Class clazz = type.isArray() ? type.getComponentType() : type; if (clazz.isPrimitive() || Number.class.isAssignableFrom(clazz) || clazz == Boolean.class) { return convertPrimitiveValue(value, clazz); } else if (clazz == String.class) { return value; } else if (clazz.isEnum()) { return Enum.valueOf((Class<Enum>) clazz, value); } return null; } @SuppressWarnings("squid:S3776") private static Object convertPrimitiveValue(String value, Class<?> type) throws ParseException { if (type.equals(Boolean.class) || type.equals(Boolean.TYPE)) { return value.toLowerCase().trim().equals("true"); } else { NumberFormat numberFormat = NumberFormat.getNumberInstance(); Number num = numberFormat.parse(value); if (type.equals(Byte.class) || type.equals(Byte.TYPE)) { return num.byteValue(); } else if (type.equals(Double.class) || type.equals(Double.TYPE)) { return num.doubleValue(); } else if (type.equals(Float.class) || type.equals(Float.TYPE)) { return num.floatValue(); } else if (type.equals(Integer.class) || type.equals(Integer.TYPE)) { return num.intValue(); } else if (type.equals(Long.class) || type.equals(Long.TYPE)) { return num.longValue(); } else if (type.equals(Short.class) || type.equals(Short.TYPE)) { return num.shortValue(); } else { return null; } } } private static Class getInstantiatableListType(Class<?> type) { if (type == List.class || type == Collection.class || type == Iterable.class) { return ArrayList.class; } else if (type == Set.class) { return LinkedHashSet.class; } else { return type; } } public static Map<String, FieldComponent> getFormFields(Class source, SlingScriptHelper sling) { return FieldUtils.getFieldsListWithAnnotation(source, FormField.class).stream() .collect(Collectors.toMap(Field::getName, f -> { FormField fieldDefinition = f.getAnnotation(FormField.class); FieldComponent component; try { component = fieldDefinition.component().newInstance(); component.setup(f.getName(), f, fieldDefinition, sling); return component; } catch (InstantiationException | IllegalAccessException ex) { LOG.error("Unable to instantiate field component for " + f.getName(), ex); } return null; }, (a, b) -> a, LinkedHashMap::new)); } private AnnotatedFieldDeserializer() { // Utility class has no constructor } }