org.sinekartads.dto.BaseDTO.java Source code

Java tutorial

Introduction

Here is the source code for org.sinekartads.dto.BaseDTO.java

Source

/*
 * Copyright (C) 2010 - 2012 Jenia Software.
 *
 * This file is part of Sinekarta
 *
 * Sinekarta is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Sinekarta is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */
package org.sinekartads.dto;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.sinekartads.dto.tools.DTODeserializer.Base64Deserializer;
import org.sinekartads.dto.tools.DTODeserializer.HexDeserializer;
import org.sinekartads.dto.tools.DTODeserializer.JSONDeserializer;
import org.sinekartads.dto.tools.DTODeserializer.XMLDeserializer;
import org.sinekartads.dto.tools.DTOPropertyType;
import org.sinekartads.dto.tools.DTOSerializer.Base64Serializer;
import org.sinekartads.dto.tools.DTOSerializer.HexSerializer;
import org.sinekartads.dto.tools.DTOSerializer.JSONSerializer;
import org.sinekartads.dto.tools.DTOSerializer.XMLSerializer;
import org.sinekartads.util.TemplateUtils;

public abstract class BaseDTO implements Serializable {

    private static final long serialVersionUID = -4276897678144607001L;

    public static boolean isEmpty(BaseDTO dto) {
        if (dto == null)
            return true;
        return dto.isEmpty();
    }

    public static boolean isNotEmpty(BaseDTO dto) {
        if (dto == null)
            return false;
        return !dto.isEmpty();
    }

    /**
     * @deprecated ignore this field - fake field for serialization only proposes
     */
    @SuppressWarnings("unused")
    private boolean empty;

    /**
     * @deprecated ignore this setter - fake field for serialization only proposes
     */
    public void setEmpty(boolean empty) {
        this.empty = empty;
    }

    public boolean isEmpty() {
        return false;
    }

    public BaseDTO() {
        Class<? extends BaseDTO> dtoClass = getClass();

        try {
            String property;
            Class<?> fieldType;
            for (Field field : dtoClass.getDeclaredFields()) {
                // Ignore the static field 
                if ((field.getModifiers() & java.lang.reflect.Modifier.STATIC) > 0)
                    continue;

                // Set the property value to "" if it represents a String
                property = field.getName();
                fieldType = field.getType();
                if (String.class.isAssignableFrom(fieldType)) {
                    BeanUtils.setProperty(this, property, "");
                }
            }
        } catch (IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }

    public String toString() {
        return TemplateUtils.Encoding.serializeJSON(this);
    }

    public boolean equals(Object obj) {
        boolean equals = false;
        if (obj != null && obj instanceof BaseDTO) {
            equals = StringUtils.equals(TemplateUtils.Encoding.serializeJSON(this),
                    TemplateUtils.Encoding.serializeJSON((BaseDTO) obj));
        }
        return equals;
    }

    // -----
    // --- Hex serialization
    // -

    public static <DTO extends BaseDTO> DTO fromHex(InputStream is, Class<DTO> clazz) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        IOUtils.copy(is, baos);
        String hex = new String(baos.toByteArray());
        return fromHex(hex, clazz);
    }

    public static <DTO extends BaseDTO> DTO fromHex(String hex, Class<DTO> clazz) {
        return TemplateUtils.Encoding.deserializeHex(clazz, hex);
    }

    public String toHex() {
        return TemplateUtils.Encoding.serializeHex(this);
    }

    public static <DTO extends BaseDTO> String serializeHex(DTO dto) {
        return new HexSerializer<DTO>().transform(dto);
    }

    @SafeVarargs
    public static <DTO extends BaseDTO> String[] serializeHex(DTO... dtos) {
        return TemplateUtils.Transformation.transform(new HexSerializer<DTO>(), dtos);
    }

    public static <DTO extends BaseDTO> Collection<String> serializeHex(Collection<DTO> dtoCol) {
        return TemplateUtils.Transformation.transform(new HexSerializer<DTO>(), dtoCol);
    }

    public static <DTO extends BaseDTO> DTO deserializeHex(Class<DTO> dtoClass, String hex) {
        return (DTO) TemplateUtils.Transformation.transform(new HexDeserializer<DTO>(dtoClass), hex);
    }

    public static <DTO extends BaseDTO> DTO[] deserializeHex(Class<DTO> dtoClass, String... hexArray) {
        return (DTO[]) TemplateUtils.Transformation.transform(new HexDeserializer<DTO>(dtoClass), hexArray);
    }

    public static <DTO extends BaseDTO, DTOCol extends Collection<DTO>> DTOCol deserializeHex(Class<DTO> dtoClass,
            Collection<String> hexCol) {
        return TemplateUtils.Transformation.transform(new HexDeserializer<DTO>(dtoClass), hexCol);
    }

    // -----
    // --- Base64 serialization
    // -

    public static <DTO extends BaseDTO> DTO fromBase64(InputStream is, Class<DTO> clazz) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        IOUtils.copy(is, baos);
        String base64 = new String(baos.toByteArray());
        return fromBase64(base64, clazz);
    }

    public static <DTO extends BaseDTO> DTO fromBase64(String base64, Class<DTO> clazz) {
        return TemplateUtils.Encoding.deserializeBase64(clazz, base64);
    }

    public String toBase64() {
        return TemplateUtils.Encoding.serializeBase64(this);
    }

    public static <DTO extends BaseDTO> String serializeBase64(DTO dto) {
        return new Base64Serializer<DTO>().transform(dto);
    }

    @SafeVarargs
    public static <DTO extends BaseDTO> String[] serializeBase64(DTO... dtos) {
        return TemplateUtils.Transformation.transform(new Base64Serializer<DTO>(), dtos);
    }

    public static <DTO extends BaseDTO> Collection<String> serializeBase64(Collection<DTO> dtoCol) {
        return TemplateUtils.Transformation.transform(new Base64Serializer<DTO>(), dtoCol);
    }

    public static <DTO extends BaseDTO> DTO deserializeBase64(Class<DTO> dtoClass, String base64) {
        return (DTO) TemplateUtils.Transformation.transform(new Base64Deserializer<DTO>(dtoClass), base64);
    }

    public static <DTO extends BaseDTO> DTO[] deserializeBase64(Class<DTO> dtoClass, String... base64Array) {
        return (DTO[]) TemplateUtils.Transformation.transform(new Base64Deserializer<DTO>(dtoClass), base64Array);
    }

    public static <DTO extends BaseDTO, DTOCol extends Collection<DTO>> DTOCol deserializeBase64(
            Class<DTO> dtoClass, Collection<String> base64Col) {
        return TemplateUtils.Transformation.transform(new Base64Deserializer<DTO>(dtoClass), base64Col);
    }

    // -----
    // --- JSON serialization
    // -

    public static <DTO extends BaseDTO> DTO fromJSON(Class<? extends DTO> dtoClass, String json) {
        return TemplateUtils.Encoding.deserializeJSON(dtoClass, json);
    }

    public static <DTO extends BaseDTO> DTO fromJSON(InputStream is, Class<? extends DTO> dtoClass)
            throws IOException {
        String json = new String(IOUtils.toByteArray(is));
        return TemplateUtils.Encoding.deserializeJSON(dtoClass, json);
    }

    public void toJSON(OutputStream os) throws IOException {
        String json = toJSON();
        os.write(json.getBytes());
        os.flush();
    }

    public String toJSON() {
        return TemplateUtils.Encoding.serializeJSON(this);
    }

    public static <DTO extends BaseDTO> String serializeJSON(DTO dto) {
        return new JSONSerializer<DTO>().transform(dto);
    }

    @SafeVarargs
    public static <DTO extends BaseDTO> String[] serializeJSON(DTO... dtos) {
        return TemplateUtils.Transformation.transform(new JSONSerializer<DTO>(), dtos);
    }

    public static <DTO extends BaseDTO> Collection<String> serializeJSON(Collection<DTO> dtoCol) {
        return TemplateUtils.Transformation.transform(new JSONSerializer<DTO>(), dtoCol);
    }

    public static <DTO extends BaseDTO> DTO[] deserializeJSON(Class<DTO> dtoClass, String... jsonArray) {
        return TemplateUtils.Transformation.transform(new JSONDeserializer<DTO>(dtoClass), jsonArray);
    }

    public static <DTO extends BaseDTO> DTO deserializeJSON(Class<DTO> dtoClass, String json) {
        return new JSONDeserializer<DTO>(dtoClass).transform(json);
    }

    public static <DTO extends BaseDTO, DTOCol extends Collection<DTO>> DTOCol deserializeJSON(Class<DTO> dtoClass,
            Collection<String> jsonCol) {
        return TemplateUtils.Transformation.transform(new JSONDeserializer<DTO>(dtoClass), jsonCol);
    }

    // -----
    // --- XML serialization
    // -

    public static <DTO extends BaseDTO> DTO fromXML(String xml, Class<DTO> clazz) throws JAXBException {
        return fromXML(new ByteArrayInputStream(xml.getBytes()), clazz);
    }

    @SuppressWarnings("unchecked")
    public static <DTO extends BaseDTO> DTO fromXML(InputStream is, Class<DTO> clazz) throws JAXBException {
        Unmarshaller u = JAXBContext.newInstance(clazz).createUnmarshaller();
        return (DTO) u.unmarshal(is);
    }

    public void toXML(OutputStream os) throws JAXBException {
        JAXBContext jcupr = JAXBContext.newInstance(this.getClass());
        Marshaller m = jcupr.createMarshaller();
        m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        m.marshal(this, os);
    }

    public String toXML() throws JAXBException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        toXML(baos);
        IOUtils.closeQuietly(baos);
        return new String(baos.toByteArray());
    }

    public static <DTO extends BaseDTO> String serializeXML(DTO dto) {
        return new XMLSerializer<DTO>().transform(dto);
    }

    @SafeVarargs
    public static <DTO extends BaseDTO> String[] serializeXML(DTO... dtos) {
        return TemplateUtils.Transformation.transform(new XMLSerializer<DTO>(), dtos);
    }

    public static <DTO extends BaseDTO> Collection<String> serializeXML(Collection<DTO> dtoCol) {
        return TemplateUtils.Transformation.transform(new XMLSerializer<DTO>(), dtoCol);
    }

    public static <DTO extends BaseDTO> DTO[] deserializeXML(Class<DTO> dtoClass, String... xmlArray) {
        return TemplateUtils.Transformation.transform(new XMLDeserializer<DTO>(dtoClass), xmlArray);
    }

    public static <DTO extends BaseDTO> DTO deserializeXML(Class<DTO> dtoClass, String xml) {
        return new XMLDeserializer<DTO>(dtoClass).transform(xml);
    }

    public static <DTO extends BaseDTO, DTOCol extends Collection<DTO>> DTOCol deserializeXML(Class<DTO> dtoClass,
            Collection<String> hexCol) {
        return TemplateUtils.Transformation.transform(new XMLDeserializer<DTO>(dtoClass), hexCol);
    }

    // -----
    // --- Field internationalization 
    // -

    //   private final static Map<Class<? extends Annotation>, DTOPropertyType> ANNOTATION_MAPPING = annotationMapping(Arrays.asList(DTOPropertyType.values()));

    //   private static Map<Class<? extends Annotation>, DTOPropertyType> annotationMapping(Collection<DTOPropertyType> dtoPropertyTypes) {
    //      Map<Class<? extends Annotation>, DTOPropertyType> mapping = 
    //            new HashMap<Class<? extends Annotation>, DTOPropertyType>();
    //      for(DTOPropertyType dtoPropertyType : dtoPropertyTypes) {
    //         mapping.put(dtoPropertyType.getAnnot(), dtoPropertyType);
    //      }
    //      return mapping;
    //   }

    protected DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
    protected DateFormat dateTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
    protected DateFormat timeFormat = new SimpleDateFormat("HH:mm:ss.SSS");
    protected NumberFormat integerFormat = new DecimalFormat("0");
    protected NumberFormat decimalFormat = new DecimalFormat("0.00");

    protected void formatValues(Map<DTOPropertyType, String> dtoPropertyFormats) throws IllegalArgumentException {
        // Mapping annotation -> dtoPropertyType restricted on the managed annotations
        Map<Class<? extends Annotation>, DTOPropertyType> annotationTypes = new HashMap<Class<? extends Annotation>, DTOPropertyType>();
        for (Map.Entry<DTOPropertyType, String> entry : dtoPropertyFormats.entrySet()) {
            annotationTypes.put(entry.getKey().getAnnot(), entry.getKey());
        }

        try {
            String newFormat;
            DateFormat newDateFormat = null;
            DateFormat newTimeFormat = null;
            DateFormat newDateTimeFormat = null;
            NumberFormat newIntegerFormat = null;
            NumberFormat newDecimalFormat = null;
            // create the the custom formatters 
            newFormat = dtoPropertyFormats.get(DTOPropertyType.Date);
            if (newFormat != null) {
                newDateFormat = new SimpleDateFormat(newFormat);
            }
            newFormat = dtoPropertyFormats.get(DTOPropertyType.Time);
            if (newFormat != null) {
                newTimeFormat = new SimpleDateFormat(newFormat);
            }
            newFormat = dtoPropertyFormats.get(DTOPropertyType.DateTime);
            if (newFormat != null) {
                newDateTimeFormat = new SimpleDateFormat(newFormat);
            }
            newFormat = dtoPropertyFormats.get(DTOPropertyType.Integer);
            if (newFormat != null) {
                newIntegerFormat = new DecimalFormat(newFormat);
            }
            newFormat = dtoPropertyFormats.get(DTOPropertyType.Decimal);
            if (newFormat != null) {
                newDecimalFormat = new DecimalFormat(newFormat);
            }

            // change formats into the dto and all its children
            Field[] fields = getClass().getDeclaredFields();
            String property;
            Class<?> fieldType;
            DTOPropertyType dtoPropertyType;
            for (Field field : fields) {
                // ignore the static field 
                if ((field.getModifiers() & java.lang.reflect.Modifier.STATIC) > 0)
                    continue;

                property = field.getName();
                fieldType = field.getType();
                if (BaseDTO.class.isAssignableFrom(fieldType)) {
                    // recursion on the children (as single member)
                    BaseDTO dto = (BaseDTO) PropertyUtils.getProperty(this, property);
                    dto.formatValues(dtoPropertyFormats);
                } else if (BaseDTO[].class.isAssignableFrom(fieldType)) {
                    // recursion on the children (as an array)
                    for (BaseDTO dto : (BaseDTO[]) PropertyUtils.getProperty(this, property)) {
                        dto.formatValues(dtoPropertyFormats);
                    }
                } else {
                    // format the other (String) values
                    String strValue = (String) PropertyUtils.getProperty(this, property);
                    if (StringUtils.isNotBlank(strValue)) {
                        Object value = null;
                        for (Annotation annot : field.getAnnotations()) {
                            // dtoPropertyType of the current field (or null)
                            dtoPropertyType = annotationTypes.get(annot.annotationType());
                            // newFormat to be applied to the current field's dtoPropertyType 
                            newFormat = dtoPropertyFormats.get(dtoPropertyType);
                            // if not null (the annotation is owned by a managed DtoPropertyType)
                            if (newFormat != null) {
                                // in every managed case, parse the value and apply to it the new format
                                switch (dtoPropertyType) {
                                case Flag: {
                                    value = BooleanUtils.toBoolean(strValue);
                                    PropertyUtils.setProperty(this, property, value);
                                    break;
                                }
                                case Date: {
                                    value = dateFormat.parse(strValue);
                                    PropertyUtils.setProperty(this, property, newDateFormat.format(value));
                                    break;
                                }
                                case Time: {
                                    value = timeFormat.parse(strValue);
                                    PropertyUtils.setProperty(this, property, newTimeFormat.format(value));
                                    break;
                                }
                                case DateTime: {
                                    value = dateTimeFormat.parse(strValue);
                                    PropertyUtils.setProperty(this, property, newDateTimeFormat.format(value));
                                    break;
                                }
                                case Integer: {
                                    value = integerFormat.parse(strValue).intValue();
                                    PropertyUtils.setProperty(this, property, newIntegerFormat.format(value));
                                    break;
                                }
                                case Decimal: {
                                    value = decimalFormat.parse(strValue);
                                    PropertyUtils.setProperty(this, property, newDecimalFormat.format(value));
                                }
                                default:
                                    throw new IllegalArgumentException(
                                            "unimplemented format: " + dtoPropertyType.name());
                                } // end switch
                            }
                        }
                        // non-annotated fields are not modified
                    }
                }
            }

            // update the internal formatters
            if (newDateFormat != null)
                dateFormat = newDateFormat;
            if (newTimeFormat != null)
                timeFormat = newTimeFormat;
            if (newDateTimeFormat != null)
                dateTimeFormat = newDateTimeFormat;
            if (newIntegerFormat != null)
                integerFormat = newIntegerFormat;
            if (newDecimalFormat != null)
                decimalFormat = newDecimalFormat;
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            // errors that should happen only during the test phase
            throw new RuntimeException(e);
        }
    }

    //   protected Object getAnnotatedProperty(String property) throws IllegalArgumentException {
    //      Field field;
    //      // verify the presence of the required property
    //      try {
    //         field = getClass().getDeclaredField(property);
    //      } catch(NoSuchFieldException e) {
    //         throw new IllegalArgumentException("Property not found: " + property, e);
    //      }
    //      Object value = null;
    //      // apply the suitable formatter to parse the property value
    //      try {
    //         String strValue = (String)PropertyUtils.getProperty(this, property);
    //         if(StringUtils.isBlank(strValue)) {
    //            DTOPropertyType dtoPropertyType;
    //            Annotation[] annotations = field.getAnnotations();
    //            for(int i=0; i<annotations.length && value == null; i++) {
    //               dtoPropertyType = ANNOTATION_MAPPING.get(annotations[i].annotationType());
    //               switch(dtoPropertyType) {
    //               case Flag:       value = BooleanUtils.toBoolean(strValue);   break;
    //               case Date:       value = dateFormat.parse(strValue);         break;
    //               case Time:       value = timeFormat.parse(strValue);         break;
    //               case DateTime:    value = dateTimeFormat.parse(strValue);      break;
    //               case Integer:     value = integerFormat.parse(strValue);      break;
    //               case Decimal:     value = decimalFormat.parse(strValue);      break;
    //               default: throw new IllegalArgumentException("unimplemented format: " + dtoPropertyType.name());
    //               }
    //            }
    //            // value must has been set or the property is wrongly annotated
    //            if(value == null) {
    //               throw new IllegalArgumentException("unable to parse from the property " + property);
    //            }
    //         } else {
    //            value = null;
    //         }
    //      } catch(RuntimeException e) {
    //         throw e;
    //      } catch(Exception e) {
    //         // errors that should happen only during the test phase
    //         throw new RuntimeException(e);
    //      }
    //      return value;
    //   }

    //   protected void setAnnotatedProperty(String property, Object value) throws IllegalArgumentException {
    //      Field field;
    //      // verify the presence of the required property
    //      try {
    //         field = getClass().getDeclaredField(property);
    //      } catch(NoSuchFieldException e) {
    //         throw new IllegalArgumentException("Property not found: " + property, e);
    //      }
    //      try {
    //         String strValue = null;
    //         // apply the suitable formatter to parse the property value
    //         if(value != null) {
    //            DTOPropertyType dtoPropertyType;
    //            Annotation[] annotations = field.getAnnotations();
    //            for(int i=0; i<annotations.length && strValue == null; i++) {
    //               dtoPropertyType = ANNOTATION_MAPPING.get(annotations[i].annotationType());
    //               switch(dtoPropertyType) {
    //               case Flag:       strValue = ((Boolean)value).toString();      break;
    //               case Date:       strValue = dateFormat.format(value);      break;
    //               case Time:       strValue = timeFormat.format(value);      break;
    //               case DateTime:    strValue = dateTimeFormat.format(value);   break;
    //               case Integer:     strValue = integerFormat.format(value);      break;
    //               case Decimal:     strValue = decimalFormat.format(value);      break;
    //               default: throw new IllegalArgumentException("unimplemented format: " + dtoPropertyType.name());
    //               }
    //            }
    //            // strValue must has been set or the property is wrongly annotated
    //            if(StringUtils.isBlank(strValue)) {
    //               throw new IllegalArgumentException("unable to format to the property " + property);
    //            }
    //            PropertyUtils.setProperty(this, property, strValue);
    //         }
    //      } catch(RuntimeException e) {
    //         throw e;         
    //      } catch(Exception e) {
    //         // errors that should happen only during the test phase
    //         throw new RuntimeException(e);
    //      }
    //   }

}