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

com.servicerocket.confluence.randombits.metadata.type.EvaluatedNumber Maven / Gradle / Ivy

There is a newer version: 2.5.12
Show newest version
package com.servicerocket.confluence.randombits.metadata.type;

import com.atlassian.confluence.core.ContentEntityObject;
import org.apache.commons.lang.StringUtils;
import com.servicerocket.confluence.randombits.metadata.MetadataManager;
import com.servicerocket.confluence.randombits.metadata.MetadataStorage;
import com.servicerocket.confluence.randombits.metadata.expression.RoundFunction;
import com.servicerocket.confluence.randombits.metadata.expression.TableFunctionParser;
import org.randombits.math.eval.Constant;
import org.randombits.math.eval.ParseException;
import org.randombits.math.eval.Parser;
import com.servicerocket.confluence.randombits.storage.EmptyStorage;
import com.servicerocket.confluence.randombits.storage.Storage;
import com.servicerocket.confluence.randombits.storage.StorageException;

import java.util.Map;

/**
 * A mathematical expression that is calculated based on the Metadata from a particular
 * data path of a {@link ContentEntityObject}.
 */
public class EvaluatedNumber extends Number implements Comparable {

    private final ContentEntityObject content;

    private final String dataPath;

    private final String expression;

    private Number value;

    private final MetadataManager metadataManager;

    public EvaluatedNumber( MetadataManager metadataManager, ContentEntityObject content, String dataPath, String expression ) {
        this.content = content;
        this.dataPath = dataPath;
        this.expression = expression;
        this.metadataManager = metadataManager;
    }

    public ContentEntityObject getContent() {
        return content;
    }

    public String getDataPath() {
        return dataPath;
    }

    public String getExpression() {
        return expression;
    }

    private Number getValue() {
        if ( value == null ) {
            evaluate();
        }
        return value;
    }

    @Override
    public int intValue() {
        return getValue().intValue();
    }

    @Override
    public long longValue() {
        return getValue().longValue();
    }

    @Override
    public float floatValue() {
        return getValue().floatValue();
    }

    @Override
    public double doubleValue() {
        return getValue().doubleValue();
    }

    /**
     * Resets the expression value. It will be recalculated next
     * time the number value is requested, or when {@link #evaluate()}
     * is called;
     */
    public void reset() {
        value = null;
    }

    /**
     * Evaluates the expression in this instance.
     */
    public void evaluate() {
        String[] names = null;
        Storage data = getData();

        if ( data != null ) {
            names = walkPath( data, dataPath );
        }

        value = recalculateValue( data, expression );

        if ( data != null ) {
            unwalkPath( data, names );
        }
    }

    private Storage getData() {
        if ( content != null )
            return metadataManager.loadReadableData( content );
        return new EmptyStorage();
    }


    private static void unwalkPath( Storage storage, String[] names ) {
        if ( names != null ) {
            for (String name : names) {
                storage.closeBox();
            }
        }
    }

    private static String[] walkPath( Storage storage, String path ) {
        if ( StringUtils.isNotBlank( path ) ) {
            String[] names = path.split( "\\" + Storage.SEPARATOR );
            if ( names.length > 0 ) {
                for (String name : names) {
                    storage.openBox(name);
                }

                return names;
            }
        }
        return null;
    }


    private static Number recalculateValue( Storage data, String expression ) {
        Parser parser = new Parser();
        parser.add( new RoundFunction() );
        parser.add( new TableFunctionParser( data, TableFunctionParser.Type.SUM, MetadataStorage.ROW_COUNT_FIELD ) );
        parser.add( new TableFunctionParser( data, TableFunctionParser.Type.AVERAGE, MetadataStorage.ROW_COUNT_FIELD ) );

        String expr = processFields( expression, parser, data );

        try {
            return parser.parse( expr ).getVal();
        } catch ( ParseException e ) {
            return Double.NaN;
        }
    }

    /**
     * Parses the expression and adds field references as variables for
     * evaluation.
     *
     * @param expr   The expression.
     * @param parser The expression parser.
     * @param fields The fields to process.
     * @return The modified expression.
     */
    private static String processFields( String expr, Parser parser, Storage fields ) {
        StringBuffer out = new StringBuffer();
        String field, alias;
        Map aliases = new java.util.HashMap();
        int aliasCount = 0;

        int start, end = -1;

        for ( start = expr.indexOf( "${" ); start >= 0; start = expr.indexOf( "${", end ) ) {
            // Append the last chunk of text
            out.append( expr.substring( end + 1, start ) );

            // Deal with the field/variable
            end = expr.indexOf( "}", start );
            field = expr.substring( start + 2, end );
            alias = aliases.get( field );
            if ( alias == null ) {
                alias = "x" + aliasCount;
                aliases.put( field, alias );
                aliasCount++;
                try {
                    Number numValue = null;

                    Object value = fields.getObject( field, null );

                    if ( value instanceof String ) {
                        try {
                            numValue = new Double( (String) value );
                        } catch ( NumberFormatException e ) {
                            // Do nothing...
                        }
                    }

                    if ( value instanceof Number )
                        numValue = (Number) value;

                    double doubleValue = numValue != null ? numValue.doubleValue() : 0.0;
                    parser.add( new Constant( alias, doubleValue ) );
                    // ctx.addMessage("Aliased '" + field + "' as '" + alias +
                    // "': " + value);
                } catch ( StorageException e ) {
                    // ignore the bad value.
                }
            }

            out.append( alias );
        }

        out.append( expr.substring( end + 1 ) );

        // ctx.addMessage("Processed expression: " + out);
        return out.toString();
    }

    @Override
    public int hashCode() {
        return getValue().hashCode() + 11;
    }

    @Override
    public boolean equals( Object o ) {
        if ( o instanceof Number ) {
            return getValue().equals( o );
        }
        return false;
    }

    @Override
    public String toString() {
        return getValue().toString();
    }

    public int compareTo( EvaluatedNumber evaluatedNumber ) {
        double self = getValue().doubleValue();
        double other = evaluatedNumber.doubleValue();

        return self < other ? -1 : self > other ? 1 : 0;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy