org.openvpms.report.AbstractExpressionEvaluator.java Source code

Java tutorial

Introduction

Here is the source code for org.openvpms.report.AbstractExpressionEvaluator.java

Source

/*
 * Version: 1.0
 *
 * The contents of this file are subject to the OpenVPMS License Version
 * 1.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.openvpms.org/license/
 *
 * Software distributed under the License is distributed on an 'AS IS' basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * Copyright 2015 (C) OpenVPMS Ltd. All Rights Reserved.
 */

package org.openvpms.report;

import org.apache.commons.jxpath.Functions;
import org.apache.commons.jxpath.JXPathContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openvpms.component.business.domain.im.archetype.descriptor.NodeDescriptor;
import org.openvpms.component.business.domain.im.common.EntityRelationship;
import org.openvpms.component.business.domain.im.common.IMObject;
import org.openvpms.component.business.domain.im.common.IMObjectReference;
import org.openvpms.component.business.domain.im.common.Participation;
import org.openvpms.component.business.domain.im.datatypes.quantity.Money;
import org.openvpms.component.business.service.archetype.IArchetypeService;
import org.openvpms.component.business.service.archetype.helper.IMObjectVariables;
import org.openvpms.component.business.service.archetype.helper.LookupHelper;
import org.openvpms.component.business.service.archetype.helper.PropertyResolver;
import org.openvpms.component.business.service.archetype.helper.PropertyResolverException;
import org.openvpms.component.business.service.lookup.ILookupService;
import org.openvpms.component.system.common.jxpath.JXPathHelper;
import org.openvpms.component.system.common.util.PropertySet;
import org.openvpms.component.system.common.util.PropertyState;

import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Collection;
import java.util.Date;

/**
 * Abstract implementation of the {@link ExpressionEvaluator} interface.
 *
 * @author Tim Anderson
 */
public abstract class AbstractExpressionEvaluator<T> implements ExpressionEvaluator {

    /**
     * The object.
     */
    private final T object;

    /**
     * Additional report fields. May be {@code null}.
     */
    private final PropertySet fields;

    /**
     * The archetype service.
     */
    private final IArchetypeService service;

    /**
     * The lookup service.
     */
    private final ILookupService lookups;

    /**
     * The JXPath extension functions.
     */
    private final Functions functions;

    /**
     * The JXPath context.
     */
    private JXPathContext context;

    /**
     * The logger.
     */
    private static final Log log = LogFactory.getLog(AbstractExpressionEvaluator.class);

    /**
     * Constructs an {@link AbstractExpressionEvaluator}.
     *
     * @param object    the object
     * @param fields    additional report fields. These override any in the report. May be {@code null}
     * @param service   the archetype service
     * @param lookups   the lookup service
     * @param functions the JXPath extension functions
     */
    public AbstractExpressionEvaluator(T object, PropertySet fields, IArchetypeService service,
            ILookupService lookups, Functions functions) {
        this.object = object;
        this.fields = fields;
        this.service = service;
        this.lookups = lookups;
        this.functions = functions;
    }

    /**
     * Returns the value of an expression.
     * If the expression is of the form [expr] it will be evaluated using {@link #evaluate(String)} else it will be
     * evaluated using {@link #getNodeValue(String)}.
     *
     * @param expression the expression
     * @return the result of the expression
     */
    public Object getValue(String expression) {
        Object result;
        try {
            if (expression.startsWith("[") && expression.endsWith("]")) {
                String eval = expression.substring(1, expression.length() - 1);
                result = evaluate(eval);
            } else {
                if (fields != null && fields.exists(expression)) {
                    result = getFieldValue(expression);
                } else {
                    result = getNodeValue(expression);
                }
            }
        } catch (Exception exception) {
            log.warn("Failed to evaluate: " + expression, exception);
            // TODO localise
            result = "Expression Error";
        }
        return result;
    }

    /**
     * Returns the formatted value of an expression.
     *
     * @param expression the expression
     * @return the result of the expression
     */
    @Override
    public String getFormattedValue(String expression) {
        Object value = getValue(expression);
        if (value instanceof Date) {
            Date date = (Date) value;
            return DateFormat.getDateInstance(DateFormat.MEDIUM).format(date);
        } else if (value instanceof Money) {
            return NumberFormat.getCurrencyInstance().format(value);
        } else if (value instanceof BigDecimal) {
            DecimalFormat format = new DecimalFormat("#,##0.00;-#,##0.00");
            return format.format(value);
        } else if (value instanceof IMObject) {
            return getValue((IMObject) value);
        } else if (value instanceof IMObjectReference) {
            return getValue((IMObjectReference) value);
        } else if (value != null) {
            return value.toString();
        }
        return null;
    }

    /**
     * Evaluates an expression.
     *
     * @param expression the expression to evaluate
     * @return the value of the expression
     */
    protected Object evaluate(String expression) {
        if (context == null) {
            context = JXPathHelper.newContext(object, functions);
            if (fields != null) {
                IMObjectVariables variables = new IMObjectVariables(service, lookups);
                for (String name : fields.getNames()) {
                    Object value = fields.get(name);
                    if (value != null) {
                        variables.add(name, value);
                    }
                }
                context.setVariables(variables);
            }
        }
        return context.getValue(expression);
    }

    /**
     * Returns a node value.
     *
     * @param name the node name
     * @return the node value
     */
    protected abstract Object getNodeValue(String name);

    /**
     * Returns a field value.
     *
     * @param name the field name
     * @return the field value
     */
    protected Object getFieldValue(String name) {
        return getValue(fields.resolve(name));
    }

    /**
     * Returns the object.
     *
     * @return the object
     */
    protected T getObject() {
        return object;
    }

    /**
     * Returns the archetype service.
     *
     * @return the archetype service
     */
    protected IArchetypeService getService() {
        return service;
    }

    /**
     * Helper to return a value for an object, for display purposes.
     * If the object is a:
     * <ul>
     * <li>Participation, returns the name/description of the participating
     * Entity</li>
     * <li>EntityRelationship, returns the name/description of the target
     * entity</li>
     * <li>otherwise, returns the object's name, or its description if the name
     * is null</li>
     * <ul>
     *
     * @param object the object. May be {@code null}
     * @return a value for the object
     */
    protected String getValue(IMObject object) {
        String value = null;
        if (object instanceof Participation) {
            value = getValue(((Participation) object).getEntity());
        } else if (object instanceof EntityRelationship) {
            value = getValue(((EntityRelationship) object).getTarget());
        } else if (object != null) {
            value = object.getName();
            if (value == null) {
                value = object.getDescription();
            }
        }
        if (value == null) {
            value = "";
        }
        return value;
    }

    /**
     * Helper to return a value for an object, for display purposes.
     *
     * @param ref the object reference. May be {@code null}
     * @return a value for the object
     */
    protected String getValue(IMObjectReference ref) {
        IMObject object = null;
        if (ref != null) {
            object = service.get(ref);
        }
        return getValue(object);
    }

    /**
     * Helper to return a the value of a node, handling collection nodes.
     * If the node doesn't exist, a localised message indicating this will be returned.
     *
     * @param name     the node name
     * @param resolver the node resolver
     * @return the node value
     */
    protected Object getValue(String name, PropertyResolver resolver) {
        Object result;
        try {
            PropertyState state = resolver.resolve(name);
            result = getValue(state);
        } catch (PropertyResolverException exception) {
            return exception.getLocalizedMessage();
        }
        return result;
    }

    /**
     * Returns the value of a {@link PropertyState}, handling collection nodes.
     *
     * @param state the state
     * @return the value
     */
    @SuppressWarnings("unchecked")
    private Object getValue(PropertyState state) {
        Object result = null;
        NodeDescriptor descriptor = state.getNode();
        Object value;
        if (descriptor != null && descriptor.isLookup()) {
            value = LookupHelper.getName(service, lookups, descriptor, state.getParent());
        } else {
            value = state.getValue();
        }
        if (value != null) {
            if (state.getNode() != null && state.getNode().isCollection()) {
                if (value instanceof Collection) {
                    Collection<IMObject> values = (Collection<IMObject>) value;
                    StringBuilder descriptions = new StringBuilder();
                    for (IMObject object : values) {
                        descriptions.append(getValue(object));
                        descriptions.append('\n');
                    }
                    result = descriptions.toString();
                } else if (value instanceof IMObject) {
                    // single value collection.
                    IMObject object = (IMObject) value;
                    result = getValue(object);
                } else {
                    result = value;
                }
            } else {
                result = value;
            }
        }
        return result;
    }
}