com.github.gekoh.yagen.util.FieldInfo.java Source code

Java tutorial

Introduction

Here is the source code for com.github.gekoh.yagen.util.FieldInfo.java

Source

/*
 * FieldInfo
 * Copyright (c) 2012 CREDIT SUISSE Technology and Operations. All Rights Reserved.
 * This software is the proprietary information of CREDIT SUISSE Technology and Operations.
 * Use is subject to license and non-disclosure terms.
 */
package com.github.gekoh.yagen.util;

import com.github.gekoh.yagen.api.Constants;
import org.apache.commons.lang.StringUtils;
import org.hibernate.annotations.Type;

import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.Basic;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Enumerated;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
import javax.persistence.Transient;
import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* @author Georg Kohlweiss (F477448)
*/
public class FieldInfo {

    private static Pattern NULLABLE_PATTERN = Pattern.compile("nullable=((true)|(false))");
    private static Pattern UNIQUE_PATTERN = Pattern.compile("unique=((true)|(false))");
    private static Pattern STRING_ATTR_PATTERN = Pattern
            .compile("[\\(|\\s*](name|columnDefinition|type)=([^)\\s*]*)[,\\)]");

    private Class type;
    private String name;
    private String columnName;
    private String columnAnnotation;
    private Field field;

    private boolean isEnum;
    private boolean isEmbedded;

    public FieldInfo(Class type, String name) {
        this.type = type;
        this.name = name;
    }

    public FieldInfo(Class type, String name, AttributeOverrides overrides) {
        this.type = type;
        this.name = name;
        this.columnName = null;
        isEnum = false;
        isEmbedded = true;

        StringBuilder columnAnnotation = new StringBuilder();
        if (overrides != null) {
            columnAnnotation.append(formatAnnotation(overrides));
        }

        this.columnAnnotation = concatOverrides(columnAnnotation.toString(), getAttributeOverrides(type).values());
    }

    public FieldInfo(Class type, String name, AttributeOverride override) {
        this(type, name, (AttributeOverrides) null);

        Map<String, String> overrides = new LinkedHashMap<String, String>();
        if (override != null) {
            addAttributeOverride(overrides, formatAnnotation(override));
        }
        addAttributeOverrides(overrides, "", type);

        this.columnAnnotation = concatOverrides("", overrides.values());
    }

    public FieldInfo(Class type, String name, boolean anEnum, Column column) {
        this.type = type;
        this.name = name;
        this.columnName = MappingUtils.deriveColumnName(column, name).toLowerCase();
        isEnum = anEnum;
        isEmbedded = false;
        this.columnAnnotation = formatAnnotation(column);
    }

    public FieldInfo(Class type, String name, String columnName, int columnLength) {
        this.type = type;
        this.name = name;
        this.columnName = columnName.toLowerCase();
        columnAnnotation = !isCollection()
                ? "@" + Column.class.getName() + "(name = \"" + escapeAttributeValue(columnName) + "\", length = "
                        + columnLength + ")"
                : null;
        isEnum = false;
        isEmbedded = false;
    }

    public FieldInfo(Class type, String name, String columnName, boolean nullable, String typeAnnotation) {
        this.type = type;
        this.name = name;
        this.columnName = columnName.toLowerCase();
        columnAnnotation = "@" + Column.class.getName() + "(name = \"" + escapeAttributeValue(columnName)
                + "\", nullable = " + nullable + ")"
                + (typeAnnotation != null ? " @" + Type.class.getName() + "(type = \"" + typeAnnotation + "\")"
                        : "");
        isEnum = false;
        isEmbedded = false;
    }

    public Class getType() {
        return type;
    }

    public String getName() {
        return name;
    }

    public String getColumnName() {
        return columnName;
    }

    public String getColumnAnnotation() {
        return columnAnnotation;
    }

    public boolean isEnum() {
        return isEnum;
    }

    public boolean isEmbedded() {
        return isEmbedded;
    }

    public boolean isBooleanType() {
        return type == Boolean.class || type == boolean.class;
    }

    public boolean isCollection() {
        return Collection.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type);
    }

    public void addAnnotation(Annotation annotation) {
        columnAnnotation = (columnAnnotation != null ? columnAnnotation + "\n    " : "")
                + formatAnnotation(annotation);
    }

    public Field getField() {
        return field;
    }

    public void setField(Field field) {
        this.field = field;
    }

    private static String formatAnnotation(Annotation annotation) {
        String a = annotation.toString();
        StringBuilder result = new StringBuilder();

        // wrap string value of attribute "name" into double quotes as needed for java code
        Matcher m = STRING_ATTR_PATTERN.matcher(a);
        int idx = 0;
        while (m.find(idx)) {
            result.append(a.substring(idx, m.start(2)));
            result.append("\"").append(escapeAttributeValue(m.group(2))).append("\"");
            result.append(a.substring(m.end(2), m.end()));
            idx = m.end();
        }
        result.append(a.substring(idx));

        a = result.toString();
        result = new StringBuilder();

        // remove empty attributes like (columnDefinition=)
        m = Pattern.compile("\\(?(,?\\s*[A-Za-z]*=)[,|\\)]").matcher(a);
        idx = 0;
        while (m.find(idx)) {
            result.append(a.substring(idx, m.start(1)));
            idx = m.end(1);
        }
        result.append(a.substring(idx));

        // set nullable=true
        m = NULLABLE_PATTERN.matcher(result);
        idx = 0;
        while (m.find(idx)) {
            if (m.group(1).equals("false")) {
                result.replace(m.start(1), m.end(1), "true");
            }
            idx = m.start(1) + 1;
            m = NULLABLE_PATTERN.matcher(result);
        }

        // set unique=false
        m = UNIQUE_PATTERN.matcher(result);
        idx = 0;
        while (m.find(idx)) {
            if (m.group(1).equals("true")) {
                result.replace(m.start(1), m.end(1), "false");
            }
            idx = m.start(1) + 1;
            m = UNIQUE_PATTERN.matcher(result);
        }

        return result.toString().replaceAll("=\\[([^\\]]*)\\]", "={$1}");
    }

    private static String escapeAttributeValue(String value) {
        return value.replace("\"", "\\\"");
    }

    private static final Pattern ATTR_OVERR_NAME = Pattern
            .compile("AttributeOverride\\s*\\(\\s*name\\s*=\\s*\"([^\"]+)\"");

    private static String addNamePrefixToAttributeOverride(String annotation, String prefix) {
        Matcher matcher = ATTR_OVERR_NAME.matcher(annotation);
        if (matcher.find()) {
            return annotation.substring(0, matcher.start(1)) + prefix + annotation.substring(matcher.start(1));
        }
        throw new IllegalArgumentException("no AttributeOverride found in '" + annotation + "'");
    }

    private static String getNameFromAttributeOverride(String attributeOverride) {
        Matcher matcher = ATTR_OVERR_NAME.matcher(attributeOverride);
        if (matcher.find()) {
            return matcher.group(1);
        }
        throw new IllegalArgumentException("no name of AttributeOverride found in '" + attributeOverride + "'");
    }

    /**
     * generates a collection of javax.persistence.AttributeOverride annotations needed to override nullable=false
     * columns since this is not allowed in *Hst-Entities
     *
     * @param type embeddable class with non-nullable fields
     * @return collection of javax.persistence.AttributeOverride annotations needed to set columns to nullable=true
     */
    private static Map<String, String> getAttributeOverrides(Class type) {
        Map<String, String> overrides = new LinkedHashMap<String, String>();
        addAttributeOverrides(overrides, "", type);
        return overrides;
    }

    private static void addAttributeOverrides(Map<String, String> overrides, String path, Class type) {
        for (Field field : type.getDeclaredFields()) {
            String curPath = path + field.getName() + ".";
            Column column;
            if (field.isAnnotationPresent(AttributeOverride.class)) {
                addAttributeOverride(overrides, addNamePrefixToAttributeOverride(
                        formatAnnotation(field.getAnnotation(AttributeOverride.class)), curPath));
            } else if (field.isAnnotationPresent(AttributeOverrides.class)) {
                for (AttributeOverride attributeOverride : field.getAnnotation(AttributeOverrides.class).value()) {
                    addAttributeOverride(overrides,
                            addNamePrefixToAttributeOverride(formatAnnotation(attributeOverride), curPath));
                }
            } else if (((column = field.getAnnotation(Column.class)) != null
                    && (!column.nullable() || column.unique()))
                    || (field.isAnnotationPresent(Basic.class) && !field.getAnnotation(Basic.class).optional())) {
                String columnName = column != null ? column.name() : field.getName();
                int length = column != null ? column.length() : 255;

                String override = "@javax.persistence.AttributeOverride(name=\"" + path + field.getName()
                        + "\", column=" + "@javax.persistence.Column(name=\"" + columnName + "\", length=" + length
                        + ", nullable=true, unique=false))";

                addAttributeOverride(overrides, override);
            }

            if (field.isAnnotationPresent(Embedded.class)) {
                addAttributeOverrides(overrides, curPath, field.getType());
            }
        }
    }

    private static void addAttributeOverride(Map<String, String> overrides, String attributeOverride) {
        String name = getNameFromAttributeOverride(attributeOverride);
        if (!overrides.containsKey(name)) {
            overrides.put(name, attributeOverride);
        }
    }

    /**
     * merges given collection of javax.persistence.AttributeOverride elements into an optionally existing
     * javax.persistence.AttributeOverrides annotation with optionally pre-existing javax.persistence.AttributeOverride elements.
     *
     * @param annotation existing javax.persistence.AttributeOverrides annotation, if any, otherwise it will be created
     * @param attributeOverrides collection of javax.persistence.AttributeOverride annotation to be appended
     * @return merged AttributeOverrides annotation
     */
    private static String concatOverrides(String annotation, Collection<String> attributeOverrides) {
        StringBuilder columnAnnotation = new StringBuilder(annotation != null ? annotation : "");

        for (String addOverride : attributeOverrides) {
            if (columnAnnotation.length() < 1) {
                columnAnnotation.append("@javax.persistence.AttributeOverrides({").append(addOverride).append("})");
                continue;
            }

            columnAnnotation.insert(columnAnnotation.length() - 2, ", ");
            columnAnnotation.insert(columnAnnotation.length() - 2, addOverride);
        }

        return columnAnnotation.toString();
    }

    public static List<FieldInfo> convertDeclaredAndInheritedFields(Class baseEntity) {
        List<FieldInfo> fields = new ArrayList<FieldInfo>();
        Class clazz = baseEntity;
        while (clazz != null) {
            convertFields(fields, clazz);
            clazz = clazz.getSuperclass();
        }
        return fields;
    }

    public static List<FieldInfo> convertFields(Class baseEntity) {
        return convertFields(new ArrayList<FieldInfo>(), baseEntity);
    }

    private static List<FieldInfo> convertFields(List<FieldInfo> fields, Class baseEntity) {

        for (Field field : baseEntity.getDeclaredFields()) {
            FieldInfo fi;
            Class type = field.getType();
            String name = field.getName();
            Column column = field.getAnnotation(Column.class);
            if (field.isAnnotationPresent(Embedded.class)) {
                if (field.isAnnotationPresent(AttributeOverride.class)) {
                    fi = new FieldInfo(type, name, field.getAnnotation(AttributeOverride.class));
                } else {
                    fi = new FieldInfo(type, name, field.getAnnotation(AttributeOverrides.class));
                }
            } else if (field.isAnnotationPresent(Enumerated.class)) {
                fi = new FieldInfo(type, name, true, column);
            } else if (column != null && !field.isAnnotationPresent(CollectionTable.class)) {
                if (type.isPrimitive()) {
                    if (type.equals(Boolean.TYPE)) {
                        type = Boolean.class;
                    } else if (type.equals(Long.TYPE)) {
                        type = Long.class;
                    } else if (type.equals(Integer.TYPE)) {
                        type = Integer.class;
                    } else if (type.equals(Short.TYPE)) {
                        type = Short.class;
                    } else if (type.equals(Byte.TYPE)) {
                        type = Byte.class;
                    } else if (type.equals(Double.TYPE)) {
                        type = Double.class;
                    } else if (type.equals(Float.TYPE)) {
                        type = Float.class;
                    } else if (type.equals(Character.TYPE)) {
                        type = Character.class;
                    }
                }
                fi = new FieldInfo(type, name, false, column);
            } else if ((field.isAnnotationPresent(ManyToOne.class) && !field.isAnnotationPresent(JoinTable.class))
                    || (field.isAnnotationPresent(OneToOne.class)
                            && StringUtils.isEmpty(field.getAnnotation(OneToOne.class).mappedBy()))) {
                String columnName = field.isAnnotationPresent(JoinColumn.class)
                        ? field.getAnnotation(JoinColumn.class).name()
                        : field.getName();
                fi = getIdFieldInfo(type, name, columnName);
            } else if (!field.isAnnotationPresent(Transient.class)
                    && (Collection.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type))
                    && (field.isAnnotationPresent(JoinColumn.class) || field.isAnnotationPresent(JoinTable.class)
                            || field.isAnnotationPresent(CollectionTable.class))) {
                fi = new FieldInfo(type, name);
            } else {
                continue;
            }
            if (field.isAnnotationPresent(Type.class)) {
                fi.addAnnotation(field.getAnnotation(Type.class));
            }
            fi.setField(field);
            fields.add(fi);
        }

        return fields;
    }

    public static FieldInfo getIdFieldInfo(Class type, String namePrefix, String columnName) {
        AccessibleObject id = getIdFieldOrMethod(type);
        Column column = id.getAnnotation(Column.class);
        String suffix;
        if (id instanceof Field) {
            type = ((Field) id).getType();
            suffix = ((Field) id).getName().substring(0, 1).toUpperCase() + ((Field) id).getName().substring(1);
        } else {
            type = ((Method) id).getReturnType();
            suffix = ((Method) id).getName().replace("get", "").replace("set", "");
        }
        String name = namePrefix + suffix;
        return new FieldInfo(type, name, columnName, column.length());
    }

    public static List<FieldInfo> convertInverseFKs(List<AccessibleObject> inverseFKfieldsOrMethods) {
        List<FieldInfo> fields = new ArrayList<FieldInfo>();
        for (AccessibleObject inverseFK : inverseFKfieldsOrMethods) {
            String joinColName = inverseFK.getAnnotation(JoinColumn.class).name();
            fields.add(new FieldInfo(String.class, toCamelCase("INV_FK_" + joinColName), joinColName,
                    Constants.UUID_LEN));
        }
        return fields;
    }

    public static AccessibleObject getIdFieldOrMethod(Class entityClass) {
        for (Field field : entityClass.getDeclaredFields()) {
            if (field.isAnnotationPresent(Id.class)) {
                return field;
            }
        }
        for (Method method : entityClass.getDeclaredMethods()) {
            if (method.isAnnotationPresent(Id.class)) {
                return method;
            }
        }
        return entityClass.getSuperclass() != null ? getIdFieldOrMethod(entityClass.getSuperclass()) : null;
    }

    public static String toCamelCase(String columnName) {
        StringBuilder s = new StringBuilder();
        int idx = -1, lastIdx = 0;

        while ((idx = columnName.indexOf('_', idx + 1)) >= 0) {
            if (lastIdx > 0) {
                s.append(columnName.substring(lastIdx + 1, lastIdx + 2).toUpperCase());
                s.append(columnName.substring(lastIdx + 2, idx).toLowerCase());
            } else {
                s.append(columnName.substring(lastIdx, idx).toLowerCase());
            }
            lastIdx = idx;
        }

        if (lastIdx > 0) {
            s.append(columnName.substring(lastIdx + 1, lastIdx + 2).toUpperCase());
            s.append(columnName.substring(lastIdx + 2).toLowerCase());
        } else {
            s.append(columnName.substring(lastIdx).toLowerCase());
        }

        return s.toString();
    }
}