<commit_before>/*
 * Copyright (c) 2004-2019, University of Oslo
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 * Neither the name of the HISP project nor the names of its contributors may
 * be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.hisp.dhis.android.core.program.programindicatorengine;


import org.apache.commons.jexl2.JexlException;
import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore;
import org.hisp.dhis.android.core.common.AggregationType;
import org.hisp.dhis.android.core.common.ValueType;
import org.hisp.dhis.android.core.constant.Constant;
import org.hisp.dhis.android.core.dataelement.DataElement;
import org.hisp.dhis.android.core.enrollment.Enrollment;
import org.hisp.dhis.android.core.enrollment.internal.EnrollmentStore;
import org.hisp.dhis.android.core.event.Event;
import org.hisp.dhis.android.core.event.internal.EventStore;
import org.hisp.dhis.android.core.program.ProgramIndicator;
import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttribute;
import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttributeValue;
import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttributeValueStore;
import org.hisp.dhis.android.core.trackedentity.TrackedEntityDataValue;
import org.hisp.dhis.android.core.trackedentity.TrackedEntityDataValueStore;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.inject.Inject;

import dagger.Reusable;

/**
 * @author <NAME>
 */

@SuppressWarnings({
        "PMD.ExcessiveImports",
        "PMD.ExcessiveMethodLength",
        "PMD.NPathComplexity",
        "PMD.CyclomaticComplexity",
        "PMD.ModifiedCyclomaticComplexity",
        "PMD.StdCyclomaticComplexity",
        "PMD.GodClass"
})
@Reusable
public class ProgramIndicatorEngine {
    private static final String NULL_REPLACEMENT = "null";

    private static final String SEPARATOR_ID = "\\.";
    private static final String KEY_DATAELEMENT = "#";
    private static final String KEY_ATTRIBUTE = "A";
    private static final String KEY_PROGRAM_VARIABLE = "V";
    private static final String KEY_CONSTANT = "C";
    private static final String INCIDENT_DATE = "incident_date";
    private static final String ENROLLMENT_DATE = "enrollment_date";
    private static final String ENROLLMENT_STATUS = "enrollment_status";
    private static final String EVENT_DATE = "event_date";
    private static final String EVENT_COUNT = "event_count";
    private static final String DUE_DATE = "due_date";
    private static final String CURRENT_DATE = "current_date";
    private static final String VAR_VALUE_COUNT = "value_count";
    private static final String VAR_ZERO_POS_VALUE_COUNT = "zero_pos_value_count";
    private static final String EXPRESSION_REGEXP = "(" + KEY_DATAELEMENT + "|" + KEY_ATTRIBUTE + "|" +
            KEY_PROGRAM_VARIABLE + "|" + KEY_CONSTANT +
            ")\\{(\\w+|" + INCIDENT_DATE + "|" + ENROLLMENT_DATE + "|" + ENROLLMENT_STATUS + "|" +
            EVENT_DATE + "|" + EVENT_COUNT + "|" + DUE_DATE + "|" + CURRENT_DATE + ")" +
            SEPARATOR_ID + "?(\\w*)\\}";
    private static final Pattern EXPRESSION_PATTERN = Pattern.compile(EXPRESSION_REGEXP);
    private static final Pattern VALUECOUNT_PATTERN =
            Pattern.compile("V\\{(" + VAR_VALUE_COUNT + "|" + VAR_ZERO_POS_VALUE_COUNT + ")\\}");

    private final IdentifiableObjectStore<ProgramIndicator> programIndicatorStore;
    private final TrackedEntityDataValueStore trackedEntityDataValueStore;
    private final EnrollmentStore enrollmentStore;
    private final EventStore eventStore;
    private final IdentifiableObjectStore<DataElement> dataElementStore;
    private final IdentifiableObjectStore<TrackedEntityAttribute> trackedEntityAttributeStore;
    private final IdentifiableObjectStore<Constant> constantStore;
    private final TrackedEntityAttributeValueStore trackedEntityAttributeValueStore;

    @Inject
    ProgramIndicatorEngine(IdentifiableObjectStore<ProgramIndicator> programIndicatorStore,
                           TrackedEntityDataValueStore trackedEntityDataValueStore,
                           EnrollmentStore enrollmentStore,
                           EventStore eventStore,
                           IdentifiableObjectStore<DataElement> dataElementStore,
                           IdentifiableObjectStore<TrackedEntityAttribute> trackedEntityAttributeStore,
                           IdentifiableObjectStore<Constant> constantStore,
                           TrackedEntityAttributeValueStore trackedEntityAttributeValueStore) {
        this.programIndicatorStore = programIndicatorStore;
        this.trackedEntityDataValueStore = trackedEntityDataValueStore;
        this.enrollmentStore = enrollmentStore;
        this.eventStore = eventStore;
        this.dataElementStore = dataElementStore;
        this.trackedEntityAttributeStore = trackedEntityAttributeStore;
        this.constantStore = constantStore;
        this.trackedEntityAttributeValueStore = trackedEntityAttributeValueStore;
    }

    /**
     * Calculate an program indicator value based on program instance and an
     * indicator defined for a TrackedEntityInstance
     *
     * @param enrollment  Enrollment uid
     * @param event Event uid
     * @param programIndicatorUid ProgramIndicator
     * @return Indicator value
     */
    public String getProgramIndicatorValue(String enrollment, String event, String programIndicatorUid) {
        if (enrollment == null && event == null) {
            return null;
        }

        String value = getValue(enrollment, event, programIndicatorUid);

        if (MathUtils.isNumeric(value)) {
            return TextUtils.fromDouble(Double.valueOf(value));
        } else {
            return value;
        }
    }

    // -------------------------------------------------------------------------
    // Supportive methods
    // -------------------------------------------------------------------------

    private String getValue(String enrollment, String event, String indicatorUid) {
        String expression = parseIndicatorExpression(enrollment, event, indicatorUid);
        String value;
        try {
            value = ExpressionUtils.evaluateToString(expression, null);
        } catch (JexlException e) {
            value = null;
        } catch (IllegalStateException e){
            value = null;
        }
        return value;
    }

    String parseIndicatorExpression(String enrollment, String event, String indicatorUid) {
        StringBuffer buffer = new StringBuffer();

        ProgramIndicator programIndicator = this.programIndicatorStore.selectByUid(indicatorUid);
        String expression = programIndicator.expression();

        Matcher matcher = EXPRESSION_PATTERN.matcher(expression);

        int valueCount = 0;
        int zeroPosValueCount = 0;

        Map<String, List<Event>> cachedEvents = new HashMap<>();
        Enrollment cachedEnrollment = null;
        Map<String, TrackedEntityAttributeValue> attributeToAttributeValues = new HashMap<>();

        Date currentDate = new Date();
<commit_msg>[ANDROSDK-859] Modfiy program indicator engine to return null when the expression in a program indicator is null<commit_after>/*
 * Copyright (c) 2004-2019, University of Oslo
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 * Neither the name of the HISP project nor the names of its contributors may
 * be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.hisp.dhis.android.core.program.programindicatorengine;


import org.apache.commons.jexl2.JexlException;
import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore;
import org.hisp.dhis.android.core.common.AggregationType;
import org.hisp.dhis.android.core.common.ValueType;
import org.hisp.dhis.android.core.constant.Constant;
import org.hisp.dhis.android.core.dataelement.DataElement;
import org.hisp.dhis.android.core.enrollment.Enrollment;
import org.hisp.dhis.android.core.enrollment.internal.EnrollmentStore;
import org.hisp.dhis.android.core.event.Event;
import org.hisp.dhis.android.core.event.internal.EventStore;
import org.hisp.dhis.android.core.program.ProgramIndicator;
import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttribute;
import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttributeValue;
import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttributeValueStore;
import org.hisp.dhis.android.core.trackedentity.TrackedEntityDataValue;
import org.hisp.dhis.android.core.trackedentity.TrackedEntityDataValueStore;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.inject.Inject;

import dagger.Reusable;

/**
 * @author <NAME>
 */

@SuppressWarnings({
        "PMD.ExcessiveImports",
        "PMD.ExcessiveMethodLength",
        "PMD.NPathComplexity",
        "PMD.CyclomaticComplexity",
        "PMD.ModifiedCyclomaticComplexity",
        "PMD.StdCyclomaticComplexity",
        "PMD.GodClass"
})
@Reusable
public class ProgramIndicatorEngine {
    private static final String NULL_REPLACEMENT = "null";

    private static final String SEPARATOR_ID = "\\.";
    private static final String KEY_DATAELEMENT = "#";
    private static final String KEY_ATTRIBUTE = "A";
    private static final String KEY_PROGRAM_VARIABLE = "V";
    private static final String KEY_CONSTANT = "C";
    private static final String INCIDENT_DATE = "incident_date";
    private static final String ENROLLMENT_DATE = "enrollment_date";
    private static final String ENROLLMENT_STATUS = "enrollment_status";
    private static final String EVENT_DATE = "event_date";
    private static final String EVENT_COUNT = "event_count";
    private static final String DUE_DATE = "due_date";
    private static final String CURRENT_DATE = "current_date";
    private static final String VAR_VALUE_COUNT = "value_count";
    private static final String VAR_ZERO_POS_VALUE_COUNT = "zero_pos_value_count";
    private static final String EXPRESSION_REGEXP = "(" + KEY_DATAELEMENT + "|" + KEY_ATTRIBUTE + "|" +
            KEY_PROGRAM_VARIABLE + "|" + KEY_CONSTANT +
            ")\\{(\\w+|" + INCIDENT_DATE + "|" + ENROLLMENT_DATE + "|" + ENROLLMENT_STATUS + "|" +
            EVENT_DATE + "|" + EVENT_COUNT + "|" + DUE_DATE + "|" + CURRENT_DATE + ")" +
            SEPARATOR_ID + "?(\\w*)\\}";
    private static final Pattern EXPRESSION_PATTERN = Pattern.compile(EXPRESSION_REGEXP);
    private static final Pattern VALUECOUNT_PATTERN =
            Pattern.compile("V\\{(" + VAR_VALUE_COUNT + "|" + VAR_ZERO_POS_VALUE_COUNT + ")\\}");

    private final IdentifiableObjectStore<ProgramIndicator> programIndicatorStore;
    private final TrackedEntityDataValueStore trackedEntityDataValueStore;
    private final EnrollmentStore enrollmentStore;
    private final EventStore eventStore;
    private final IdentifiableObjectStore<DataElement> dataElementStore;
    private final IdentifiableObjectStore<TrackedEntityAttribute> trackedEntityAttributeStore;
    private final IdentifiableObjectStore<Constant> constantStore;
    private final TrackedEntityAttributeValueStore trackedEntityAttributeValueStore;

    @Inject
    ProgramIndicatorEngine(IdentifiableObjectStore<ProgramIndicator> programIndicatorStore,
                           TrackedEntityDataValueStore trackedEntityDataValueStore,
                           EnrollmentStore enrollmentStore,
                           EventStore eventStore,
                           IdentifiableObjectStore<DataElement> dataElementStore,
                           IdentifiableObjectStore<TrackedEntityAttribute> trackedEntityAttributeStore,
                           IdentifiableObjectStore<Constant> constantStore,
                           TrackedEntityAttributeValueStore trackedEntityAttributeValueStore) {
        this.programIndicatorStore = programIndicatorStore;
        this.trackedEntityDataValueStore = trackedEntityDataValueStore;
        this.enrollmentStore = enrollmentStore;
        this.eventStore = eventStore;
        this.dataElementStore = dataElementStore;
        this.trackedEntityAttributeStore = trackedEntityAttributeStore;
        this.constantStore = constantStore;
        this.trackedEntityAttributeValueStore = trackedEntityAttributeValueStore;
    }

    /**
     * Calculate an program indicator value based on program instance and an
     * indicator defined for a TrackedEntityInstance
     *
     * @param enrollment  Enrollment uid
     * @param event Event uid
     * @param programIndicatorUid ProgramIndicator
     * @return Indicator value
     */
    public String getProgramIndicatorValue(String enrollment, String event, String programIndicatorUid) {
        if (enrollment == null && event == null) {
            return null;
        }

        String value = getValue(enrollment, event, programIndicatorUid);

        if (MathUtils.isNumeric(value)) {
            return TextUtils.fromDouble(Double.valueOf(value));
        } else {
            return value;
        }
    }

    // -------------------------------------------------------------------------
    // Supportive methods
    // -------------------------------------------------------------------------

    private String getValue(String enrollment, String event, String indicatorUid) {
        String expression = parseIndicatorExpression(enrollment, event, indicatorUid);
        String value;
        try {
            value = ExpressionUtils.evaluateToString(expression, null);
        } catch (JexlException e) {
            value = null;
        } catch (IllegalStateException e){
            value = null;
        }
        return value;
    }

    String parseIndicatorExpression(String enrollment, String event, String indicatorUid) {

        ProgramIndicator programIndicator = this.programIndicatorStore.selectByUid(indicatorUid);
        String expression = programIndicator.expression();

        if (expression == null) {
            return null;
        }

        Matcher matcher = EXPRESSION_PATTERN.matcher(expression);

        int valueCount = 0;
        int zeroPosValueCount = 0;

        Map<String, List<Event>> cachedEvents = new HashMap<>();
        Enrollment cachedEnrollment = null;
        Map<String, TrackedEntityAttributeValue> attributeToAttributeValues = new HashMap<>();

        StringBuffer buffer = new StringBuffer();
        Date currentDate = new Date();

{'fraction_non_alphanumeric': 0.09251481502790403, 'fraction_numerical': 0.0014383522236925377, 'mean_word_length': 3.372830188679245, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4829337', 'n_tokens_mistral': 4604, 'n_tokens_neox': 4459, 'n_words': 1241}