<commit_before>/*
 * Copyright (c) 2015, Apptentive, Inc. All Rights Reserved.
 * Please refer to the LICENSE file for the terms and conditions
 * under which redistribution and use of this file is permitted.
 */

package com.apptentive.android.sdk.module.engagement.logic;

import android.content.Context;

import com.apptentive.android.sdk.Apptentive;
import com.apptentive.android.sdk.Log;
import com.apptentive.android.sdk.util.Util;

import org.json.JSONException;
import org.json.JSONObject;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * @author <NAME>
 */
public class ConditionalClause implements Clause {
	String query;
	List<ConditionalTest> conditionalTests;

	public ConditionalClause(String query, Object inputValue) {
		this.query = query;
		conditionalTests = new ArrayList<ConditionalTest>();

		Log.v("    + ConditionalClause for query: \"%s\"", query);
		if (inputValue instanceof JSONObject && !isComplexType((JSONObject) inputValue)) {
			conditionalTests = getConditions((JSONObject) inputValue);
		} else {
			conditionalTests.add(new ConditionalTest(ConditionalOperator.$eq, parseValue(inputValue)));
		}
	}

	private List<ConditionalTest> getConditions(JSONObject conditionObject) {
		List<ConditionalTest> conditionalTests = new ArrayList<ConditionalTest>();
		Iterator<String> operators = conditionObject.keys();
		while (operators.hasNext()) {
			String operator = operators.next();
			Object value = null;
			if (!conditionObject.isNull(operator)) {
				value = parseValue(conditionObject.opt(operator));
			}
			conditionalTests.add(new ConditionalTest(ConditionalOperator.parse(operator), value));
		}
		return conditionalTests;
	}

	/**
	 * Constructs complex types values from the JSONObjects that represent them. Turns all Numbers into BigDecimal for easier comparison.
	 *
	 * @param value
	 * @return null if value is a JSONObject, but not a complex type.
	 */
	private static Object parseValue(Object value) {
		if (value == null) {
			return null;
		}
		if (value instanceof Double) {
			return new BigDecimal((Double) value);
		} else if (value instanceof Long) {
			return new BigDecimal((Long) value);
		} else if (value instanceof Integer) {
			return new BigDecimal((Integer) value);
		} else if (value instanceof Float) {
			return new BigDecimal((Float) value);
		} else if (value instanceof Short) {
			return new BigDecimal((Short) value);
		} else if (value instanceof JSONObject) {
			JSONObject jsonObject = (JSONObject) value;
			String typeName = jsonObject.optString("_type");
			if (typeName != null) {
				try {
					if (Apptentive.Version.TYPE.equals(typeName)) {
						return new Apptentive.Version(jsonObject.toString());
					} else if (Apptentive.DateTime.TYPE.equals(typeName)) {
						return new Apptentive.DateTime(jsonObject.toString());
					} else {
						throw new RuntimeException(String.format("Error parsing complex parameter with unrecognized name: \"%s\"", typeName));
					}
				} catch (JSONException e) {
					throw new RuntimeException(String.format("Error parsing complex parameter with name: \"%s\", and value: \"%s\"" + typeName, value), e);
				}
			} else {
				throw new RuntimeException("Error: Complex type parameter missing \"_type\".");
			}
		}
		// All other values, such as Boolean and String should be returned unaltered.
		return value;
	}

	private boolean isComplexType(JSONObject jsonObject) {
		return jsonObject != null && !jsonObject.isNull("_type");
	}

	/**
	 * The test in this conditional clause are implicitly ANDed together, so return false if any of them is false, and continue the loop for each test that is true;
	 *
	 * @param context
	 * @return
	 */
	@Override
	public boolean evaluate(Context context) {
		Log.v("    - %s", query);
		Comparable field = FieldManager.getValue(context, query);
		Log.v("      - => %s", Util.classToString(field));
		for (ConditionalTest test : conditionalTests) {
			switch (test.operator) {
				case $exists: {
					Log.v("      - %s %s?", test.operator, Util.classToString(test.parameter));
					if (test.parameter == null) {
						return false;
					}
					if (!(test.parameter instanceof Boolean)) {
						throw new IllegalArgumentException(String.format("%s operator passed parameter of type %s", test.operator.name(), test.parameter.getClass().getSimpleName()));
					}
					boolean exists = FieldManager.exists(context, query);
					boolean parameter = (Boolean) test.parameter;
					if (exists == parameter) {
						continue;
					} else {
						return false;
					}
				}
				case $eq:
					Log.v("      - %s %s?", test.operator, Util.classToString(test.parameter));
					if (field == null && test.parameter == null) {
						continue;<commit_msg>Per new rules, return false instead of throwing an exception when `$exists` parameter is not type `boolean`.<commit_after>/*
 * Copyright (c) 2015, Apptentive, Inc. All Rights Reserved.
 * Please refer to the LICENSE file for the terms and conditions
 * under which redistribution and use of this file is permitted.
 */

package com.apptentive.android.sdk.module.engagement.logic;

import android.content.Context;

import com.apptentive.android.sdk.Apptentive;
import com.apptentive.android.sdk.Log;
import com.apptentive.android.sdk.util.Util;

import org.json.JSONException;
import org.json.JSONObject;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * @author <NAME>
 */
public class ConditionalClause implements Clause {
	String query;
	List<ConditionalTest> conditionalTests;

	public ConditionalClause(String query, Object inputValue) {
		this.query = query;
		conditionalTests = new ArrayList<ConditionalTest>();

		Log.v("    + ConditionalClause for query: \"%s\"", query);
		if (inputValue instanceof JSONObject && !isComplexType((JSONObject) inputValue)) {
			conditionalTests = getConditions((JSONObject) inputValue);
		} else {
			conditionalTests.add(new ConditionalTest(ConditionalOperator.$eq, parseValue(inputValue)));
		}
	}

	private List<ConditionalTest> getConditions(JSONObject conditionObject) {
		List<ConditionalTest> conditionalTests = new ArrayList<ConditionalTest>();
		Iterator<String> operators = conditionObject.keys();
		while (operators.hasNext()) {
			String operator = operators.next();
			Object value = null;
			if (!conditionObject.isNull(operator)) {
				value = parseValue(conditionObject.opt(operator));
			}
			conditionalTests.add(new ConditionalTest(ConditionalOperator.parse(operator), value));
		}
		return conditionalTests;
	}

	/**
	 * Constructs complex types values from the JSONObjects that represent them. Turns all Numbers into BigDecimal for easier comparison.
	 *
	 * @param value
	 * @return null if value is a JSONObject, but not a complex type.
	 */
	private static Object parseValue(Object value) {
		if (value == null) {
			return null;
		}
		if (value instanceof Double) {
			return new BigDecimal((Double) value);
		} else if (value instanceof Long) {
			return new BigDecimal((Long) value);
		} else if (value instanceof Integer) {
			return new BigDecimal((Integer) value);
		} else if (value instanceof Float) {
			return new BigDecimal((Float) value);
		} else if (value instanceof Short) {
			return new BigDecimal((Short) value);
		} else if (value instanceof JSONObject) {
			JSONObject jsonObject = (JSONObject) value;
			String typeName = jsonObject.optString("_type");
			if (typeName != null) {
				try {
					if (Apptentive.Version.TYPE.equals(typeName)) {
						return new Apptentive.Version(jsonObject.toString());
					} else if (Apptentive.DateTime.TYPE.equals(typeName)) {
						return new Apptentive.DateTime(jsonObject.toString());
					} else {
						throw new RuntimeException(String.format("Error parsing complex parameter with unrecognized name: \"%s\"", typeName));
					}
				} catch (JSONException e) {
					throw new RuntimeException(String.format("Error parsing complex parameter with name: \"%s\", and value: \"%s\"" + typeName, value), e);
				}
			} else {
				throw new RuntimeException("Error: Complex type parameter missing \"_type\".");
			}
		}
		// All other values, such as Boolean and String should be returned unaltered.
		return value;
	}

	private boolean isComplexType(JSONObject jsonObject) {
		return jsonObject != null && !jsonObject.isNull("_type");
	}

	/**
	 * The test in this conditional clause are implicitly ANDed together, so return false if any of them is false, and continue the loop for each test that is true;
	 *
	 * @param context
	 * @return
	 */
	@Override
	public boolean evaluate(Context context) {
		Log.v("    - %s", query);
		Comparable field = FieldManager.getValue(context, query);
		Log.v("      - => %s", Util.classToString(field));
		for (ConditionalTest test : conditionalTests) {
			switch (test.operator) {
				case $exists: {
					Log.v("      - %s %s?", test.operator, Util.classToString(test.parameter));
					if (test.parameter == null) {
						return false;
					}
					if (!(test.parameter instanceof Boolean)) {
						return false;
					}
					boolean exists = FieldManager.exists(context, query);
					boolean parameter = (Boolean) test.parameter;
					if (exists == parameter) {
						continue;
					} else {
						return false;
					}
				}
				case $eq:
					Log.v("      - %s %s?", test.operator, Util.classToString(test.parameter));
					if (field == null && test.parameter == null) {
						continue;
{'fraction_non_alphanumeric': 0.11740506329113924, 'fraction_numerical': 0.0008438818565400844, 'mean_word_length': 3.8595592004100463, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 19, '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': '1120723', 'n_tokens_mistral': 3134, 'n_tokens_neox': 2667, 'n_words': 810}