All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.atlasmap.java.core.JavaWriterUtil Maven / Gradle / Ivy

package io.atlasmap.java.core;

import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

import org.slf4j.LoggerFactory;

import io.atlasmap.api.AtlasConversionService;
import io.atlasmap.api.AtlasException;
import io.atlasmap.core.AtlasPath;
import io.atlasmap.core.AtlasPath.SegmentContext;
import io.atlasmap.java.inspect.ClassHelper;
import io.atlasmap.java.inspect.JdkPackages;
import io.atlasmap.java.inspect.StringUtil;
import io.atlasmap.v2.Field;

class JavaWriterUtil {
    private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(JavaWriterUtil.class);
    protected AtlasConversionService conversionService = null;

    public JavaWriterUtil(AtlasConversionService conversionService) {
        this.conversionService = conversionService;
    }

    /**
     * Instantiate the given class.
     *
     * @param JavaField
     *            The JavaField to instantiate.
     * @param segmentContext
     *            Provided for error clarity, no use to actual functionality here.
     * @return
     * @throws AtlasException
     *             If class is abstract or an unsupported collectionType is
     *             specified.
     */
    public Object instantiateObject(Class clz, SegmentContext segmentContext, boolean createWrapperArray)
            throws AtlasException {
        try {
            if (Modifier.isAbstract(clz.getModifiers()) && !clz.isPrimitive() && !clz.isArray()) {
                throw new AtlasException("Cannot instantiate object, class is abstract: " + clz.getName()
                        + ", segment: " + segmentContext);
            }
            if (createWrapperArray && AtlasPath.isArraySegment(segmentContext.getSegment())) {
                int size = AtlasPath.indexOfSegment(segmentContext.getSegment()) + 1;
                if (LOG.isDebugEnabled()) {
                    LOG.debug("Instantiating array of size " + size + " for class '" + clz.getName() + "', segment: "
                            + segmentContext);
                }
                return Array.newInstance(clz, size);
            }
            if (LOG.isDebugEnabled()) {
                LOG.debug("Instantiating object for class '" + clz.getName() + "', segment: " + segmentContext);
            }
            return clz.newInstance();
        } catch (Exception e) {
            throw new AtlasException(
                    "Could not instantiate class: " + clz.getName() + ", segment: " + segmentContext.getSegmentPath(),
                    e);
        }
    }

    /**
     * Retrieve a child object (which may be a complex class or collection class)
     * from the given parentObject.
     *
     * @param field
     *            - provided for convenience, probably not needed here
     * @param ParentObject
     *            - the object to find the child on
     * @param segmentContext
     *            - the segment of the field's path that references the child object
     */
    public Object getObjectFromParent(Field field, Object parentObject, SegmentContext segmentContext)
            throws AtlasException {
        if (LOG.isDebugEnabled()) {
            LOG.debug(
                    "Retrieving child '" + segmentContext.getSegmentPath() + "'.\n\tparentObject: " + parentObject);
        }

        if (parentObject == null) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("Cannot find child '" + segmentContext.getSegmentPath() + "', parent is null.");
            }
            return null;
        }

        // clean up our segment from something like "@addressLine1" to "addressLine1".
        // collection segments like "orders[4]" will be cleaned to "orders"
        String cleanedSegment = AtlasPath.cleanPathSegment(segmentContext.getSegment());

        // FIXME: this doesn't work if there isn't a getter but there is a private
        // member variable

        List getters = ClassHelper.getterMethodNames(cleanedSegment);
        Method getterMethod = null;
        for (String getter : getters) {
            try {
                getterMethod = ClassHelper.detectGetterMethod(parentObject.getClass(), getter);
                break;
            } catch (NoSuchMethodException e) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("Looking for getter for '" + segmentContext.getSegmentPath() + " on this class: "
                            + parentObject.getClass().getName(), e);
                }
            }
        }

        if (getterMethod == null) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("Unable to detect getter method for: " + segmentContext.getSegment() + " from "
                        + segmentContext.getSegmentPath() + " on parent: " + parentObject);
            }
            return null;
        }

        getterMethod.setAccessible(true);
        Object childObject;
        try {
            childObject = getterMethod.invoke(parentObject);
        } catch (Exception e) {
            throw new AtlasException(e);
        }

        if (LOG.isDebugEnabled()) {
            if (childObject == null) {
                LOG.debug("Could not find child object for path: " + segmentContext.getSegmentPath());
            } else {
                LOG.debug("Found child object for path '" + segmentContext.getSegmentPath() + "': " + childObject);
            }
        }

        return childObject;
    }

    /**
     * Set the given object within the parentObject.
     *
     * @param field
     *            - provided if we need it, I don't think we will since we already
     *            have the value in hand?
     * @param segmentContext
     *            - current segment for the field's path, this will be the last
     *            segment in the path.
     * @param parentObject
     *            - the object we're setting the value in
     * @param childObject
     *            - the childObject to set
     */

    public void setObjectOnParent(Field javaField, SegmentContext segmentContext, Object parentObject,
            Object childObject) throws AtlasException {
        if (LOG.isDebugEnabled()) {
            LOG.debug("Setting object for path:'" + javaField.getPath() + "'.\n\tchildObject: " + childObject
                    + "\n\tparentObject: " + parentObject);
        }

        AtlasPath pathUtil = new AtlasPath(javaField.getPath());

        try {
            Class childClass = childObject == null ? null : childObject.getClass();
            Method targetMethod = resolveSetMethod(parentObject, segmentContext, childClass);
            Object targetObject = parentObject;

            // We already know we have a 1 paramter setter here
            if (childObject == null && conversionService.isPrimitive(targetMethod.getParameterTypes()[0])) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("Not setting null value for primitive method paramter for path:'" + javaField.getPath()
                            + "'.\n\tchildObject: " + childObject + "\n\tparentObject: " + parentObject);
                }
                return;
            }

            if (targetMethod != null) {
                targetMethod.invoke(targetObject, childObject);
                javaField.setValue(childObject);
            } else {
                try {
                    java.lang.reflect.Field field = targetObject.getClass().getField(pathUtil.getLastSegment());
                    field.setAccessible(true);
                    field.set(targetObject, childObject);
                    javaField.setValue(field.get(targetObject));
                } catch (NoSuchFieldException nsfe) {
                    throw new AtlasException("Unable to find matting setter method or field for path: "
                            + javaField.getPath() + " on parentObject: " + parentObject.getClass().getName());
                }
            }
        } catch (Exception e) {
            String parentClassName = parentObject == null ? null : parentObject.getClass().getName();
            String childClassName = childObject == null ? null : childObject.getClass().getName();
            throw new AtlasException("Unable to set value for path: " + javaField.getPath() + " parentObject: "
                    + parentClassName + " childObject: " + childClassName, e);
        }
    }

    public Class resolveClassFromParent(Field javaField, Object parentObject, SegmentContext segmentContext)
            throws AtlasException {
        if (LOG.isDebugEnabled()) {
            LOG.debug("Resolving class for path:'" + javaField.getPath() + "'.\n\tsegment: " + segmentContext
                    + "\n\tparentObject: " + parentObject);
        }

        String setterMethodName = "set" + capitalizeFirstLetter(AtlasPath.cleanPathSegment(segmentContext.getSegment()));
        try {
            Method m = ClassHelper.detectSetterMethod(parentObject.getClass(), setterMethodName, null);
            return m.getParameterTypes()[0];
        } catch (Exception e) {
            throw new AtlasException(e);
        }
    }

    protected Method resolveSetMethod(Object sourceObject, SegmentContext segmentContext, Class targetType)
            throws NoSuchMethodException {
        String setterMethodName = "set" + capitalizeFirstLetter(AtlasPath.cleanPathSegment(segmentContext.getSegment()));

        List> classTree = resolveMappableClasses(sourceObject.getClass());

        if (LOG.isDebugEnabled()) {
            LOG.debug("Found " + classTree.size() + " mappable classes for class '"
                    + sourceObject.getClass().getName() + "': " + classTree);
        }

        Method m = null;
        for (Class clazz : classTree) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("Looking for setter '" + setterMethodName + "' on this class: " + clazz.getName());
            }
            try {
                m = ClassHelper.detectSetterMethod(clazz, setterMethodName, targetType);
                if (m != null) {
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("Found setter '" + setterMethodName + "' on this class: " + clazz.getName());
                    }
                    return m;
                }
            } catch (NoSuchMethodException e) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("Did not find setter '" + setterMethodName + "' on this class: " + clazz.getName(), e);
                }
            }

            // Try the boxUnboxed version
            if (conversionService.isPrimitive(targetType) || conversionService.isBoxedPrimitive(targetType)) {
                try {
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("Looking for boxed setter '" + setterMethodName + "' on this class: "
                                + clazz.getName());
                    }
                    m = ClassHelper.detectSetterMethod(clazz, setterMethodName,
                            conversionService.boxOrUnboxPrimitive(targetType));
                    if (m != null) {
                        if (LOG.isDebugEnabled()) {
                            LOG.debug("Found setter '" + setterMethodName + "' on this class: " + clazz.getName());
                        }
                        return m;
                    }
                } catch (NoSuchMethodException e) {
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("Did not find setter '" + setterMethodName + "' on this class: " + clazz.getName(),
                                e);
                    }
                }
            }
        }

        throw new NoSuchMethodException("Unable to resolve expected setter '" + setterMethodName + "' for path: "
                + segmentContext.getSegmentPath() + ", on object: " + sourceObject);
    }

    public static String capitalizeFirstLetter(String string) {
        if (StringUtil.isEmpty(string)) {
            return string;
        }
        if (string.length() == 1) {
            return String.valueOf(string.charAt(0)).toUpperCase();
        }
        return String.valueOf(string.charAt(0)).toUpperCase() + string.substring(1);
    }

    protected List> resolveMappableClasses(Class className) {
        List> classTree = new ArrayList<>();
        classTree.add(className);
        Class superClazz = className.getSuperclass();
        while (superClazz != null) {
            if (JdkPackages.contains(superClazz.getPackage().getName())) {
                superClazz = null;
            } else {
                classTree.add(superClazz);
                superClazz = superClazz.getSuperclass();
            }
        }

        // DON'T reverse.. prefer child -> parent -> grandparent
        // List> reverseTree = classTree.subList(0, classTree.size());
        // Collections.reverse(reverseTree);
        // return reverseTree;

        return classTree;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy