org.eclipse.skalli.core.persistence.EntityHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.skalli.core.persistence.EntityHelper.java

Source

/*******************************************************************************
 * Copyright (c) 2010-2014 SAP AG and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     SAP AG - initial API and implementation
 *******************************************************************************/
package org.eclipse.skalli.core.persistence;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;

import org.apache.commons.lang.StringUtils;
import org.eclipse.skalli.model.EntityBase;
import org.eclipse.skalli.model.ExtensibleEntityBase;
import org.eclipse.skalli.model.ExtensionEntityBase;

public final class EntityHelper {

    /**
     * Normalizes an entity and its extensions:
     * <ol>
     * <li>Replaces a <code>null</code> {@link java.lang.String} field with a blank ("") string.</li>
     * <li>Replaces a <code>null</code> {@link java.util.Collection}-like field with an empty collection
     * of matching type (i.e. an instance of {@link java.util.ArrayList} is assigned to a field of type
     * <code>ArrayList</code>, an instance of {@link java.util.HashSet} is assigned to a field of type
     * <code>HashSet</code> and so on). An <code>ArrayList</code> is assigned to a field of type
     * {@link java.util.List}, a <code>HashSet</code> is assigned to a field of type {@link java.util.Set} and
     * a {@link java.util.TreeSet} is assigned to a field of type {@link java.util.SortedSet}.</li>
     * <li>Replaces a <code>null</code> {@link java.util.Map}-like field with an empty map
     * of matching type (i.e. an instance of {@link java.util.HashMap} is assigned to a field of type
     * <code>HashMap</code>, and so on). A <code>HashMap</code> is assigned to a field type <code>Map</code>,
     * while a {@link java.util.TreeMap</code> is assigned to a field of type {@link java.utilSortedMap}.</li>
     * <li>Removes blank strings from {@link java.util.Collection}-like fields and from the values of
     * {@link java.util.Map}-like fields. Entries that are not strings are ignored.</li>
     * </ol>
     * All other kinds of fields are ignored.<br>
     * This method assumes, that the collection type to be
     * instantiated has either a constructor with a single integer argument (specifying the initial capacity
     * of the collection), or a parameterless constructor. The former is preferred.<br>
     * This method iterates the entity and, in case the entity is an instance
     * of {@link ExtensibleEntityBase}, all extensions of that entity, too.
     */
    public static void normalize(EntityBase entity) {
        doNormalize(entity);
        if (entity instanceof ExtensibleEntityBase) {
            ExtensibleEntityBase extensibleEntity = (ExtensibleEntityBase) entity;
            for (ExtensionEntityBase extension : extensibleEntity.getAllExtensions()) {
                extension.setExtensibleEntity(extensibleEntity);
                doNormalize(extension);
            }
        }
    }

    private static void doNormalize(EntityBase entity) {
        if (entity == null) {
            return;
        }
        Class<?> currentClass = entity.getClass();
        while (currentClass != null) {
            for (Field field : currentClass.getDeclaredFields()) {
                try {
                    // do not try to change constants or transient fields
                    int modifiers = field.getModifiers();
                    if (Modifier.isFinal(modifiers) || Modifier.isStatic(modifiers)
                            || Modifier.isTransient(modifiers)) {
                        continue;
                    }
                    field.setAccessible(true);

                    // ensure, thet the value is non null
                    Object value = field.get(entity);
                    if (value == null) {
                        instantiateField(entity, field);
                    } else {
                        // for non-null collections or maps, ensure that there
                        // are no null entries or empty strings
                        Class<?> type = field.getType();
                        if (Collection.class.isAssignableFrom(type)) {
                            Collection<?> collection = (Collection<?>) value;
                            ArrayList<Object> remove = new ArrayList<Object>();
                            for (Object entry : collection) {
                                if (entry instanceof String && StringUtils.isBlank((String) entry)) {
                                    remove.add(entry);
                                }
                            }
                            collection.removeAll(remove);
                            field.set(entity, collection);
                        } else if (Map.class.isAssignableFrom(type)) {
                            Map<?, ?> map = (Map<?, ?>) value;
                            ArrayList<Object> remove = new ArrayList<Object>();
                            for (Entry<?, ?> entry : map.entrySet()) {
                                if (entry.getValue() instanceof String
                                        && StringUtils.isBlank((String) entry.getValue())) {
                                    remove.add(entry.getKey());
                                }
                            }
                            for (Object key : remove) {
                                map.remove(key);
                            }
                            field.set(entity, map);
                        }
                    }
                } catch (UnsupportedOperationException e) {
                    //  TODO exception handling/logging
                    // some collections/map may not support remove
                    throw new RuntimeException(e);
                } catch (IllegalArgumentException e) {
                    //TODO exception handling/logging
                    throw new RuntimeException(e);
                } catch (IllegalAccessException e) {
                    //TODO exception handling/logging
                    throw new RuntimeException(e);
                }
            }
            currentClass = currentClass.getSuperclass();
        }
    }

    private static void instantiateField(EntityBase entity, Field field)
            throws IllegalArgumentException, IllegalAccessException {
        Class<?> type = field.getType();
        if (type.equals(String.class)) {
            field.set(entity, ""); //$NON-NLS-1$
        } else if (type.equals(List.class)) {
            field.set(entity, new ArrayList<Object>(0));
        } else if (type.equals(Set.class)) {
            field.set(entity, new HashSet<Object>(0));
        } else if (type.equals(SortedSet.class)) {
            field.set(entity, new TreeSet<Object>());
        } else if (type.equals(Map.class)) {
            field.set(entity, new HashMap<Object, Object>(0));
        } else if (type.equals(SortedMap.class)) {
            field.set(entity, new TreeMap<Object, Object>());
        } else if (Collection.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type)) {
            field.set(entity, getInstance(type));
        }
    }

    /**
     * Returns an instance of the given collection type.
     * This method assumes, that the type has either a constructor
     * with a single integer argument (specifying the initial capacity
     * of the collection), or a parameterless constructor. The former
     * is preferred.
     * @param collectionType  the collection type to instantiate.
     */
    private static Object getInstance(Class<?> collectionType) {
        Object instance = null;
        try {
            try {
                Constructor<?> constructor = collectionType.getConstructor(Integer.class);
                instance = constructor.newInstance(0);
            } catch (NoSuchMethodException e) {
                instance = collectionType.newInstance();
            }
        } catch (Exception e) {
            //TODO exception handling/logging
            throw new RuntimeException(e);
        }
        return instance;
    }
}