swh:1:snp:ff2a11cd2e44dd19ec3814028ef2ce6605664e63
Raw File
Tip revision: 98e2633b4a5f373161dbfc9d3d193098f1cd0c49 authored by Eric Fischer on 07 June 2018, 00:47:20 UTC
Use weak pointers for container and parser to avoid reference loops
Tip revision: 98e2633
evaluator.cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <map>
#include "mvt.hpp"
#include "evaluator.hpp"

int compare(mvt_value one, std::shared_ptr<json_object> two, bool &fail) {
	if (one.type == mvt_string) {
		if (two->type != JSON_STRING) {
			fail = true;
			return false;  // string vs non-string
		}

		return strcmp(one.string_value.c_str(), two->string.c_str());
	}

	if (one.type == mvt_double || one.type == mvt_float || one.type == mvt_int || one.type == mvt_uint || one.type == mvt_sint) {
		if (two->type != JSON_NUMBER) {
			fail = true;
			return false;  // number vs non-number
		}

		double v;
		if (one.type == mvt_double) {
			v = one.numeric_value.double_value;
		} else if (one.type == mvt_float) {
			v = one.numeric_value.float_value;
		} else if (one.type == mvt_int) {
			v = one.numeric_value.int_value;
		} else if (one.type == mvt_uint) {
			v = one.numeric_value.uint_value;
		} else if (one.type == mvt_sint) {
			v = one.numeric_value.sint_value;
		} else {
			fprintf(stderr, "Internal error: bad mvt type %d\n", one.type);
			exit(EXIT_FAILURE);
		}

		if (v < two->number) {
			return -1;
		} else if (v > two->number) {
			return 1;
		} else {
			return 0;
		}
	}

	if (one.type == mvt_bool) {
		if (two->type != JSON_TRUE && two->type != JSON_FALSE) {
			fail = true;
			return false;  // bool vs non-bool
		}

		bool b = two->type != JSON_FALSE;
		return one.numeric_value.bool_value > b;
	}

	if (one.type == mvt_null) {
		if (two->type != JSON_NULL) {
			fail = true;
			return false;  // null vs non-null
		}

		return 0;  // null equals null
	}

	fprintf(stderr, "Internal error: bad mvt type %d\n", one.type);
	exit(EXIT_FAILURE);
}

bool eval(std::map<std::string, mvt_value> const &feature, std::shared_ptr<json_object> f) {
	if (f == NULL || f->type != JSON_ARRAY) {
		fprintf(stderr, "Filter is not an array: %s\n", json_stringify(f).c_str());
		exit(EXIT_FAILURE);
	}

	if (f->array.size() < 1) {
		fprintf(stderr, "Array too small in filter: %s\n", json_stringify(f).c_str());
		exit(EXIT_FAILURE);
	}

	if (f->array[0]->type != JSON_STRING) {
		fprintf(stderr, "Filter operation is not a string: %s\n", json_stringify(f).c_str());
		exit(EXIT_FAILURE);
	}

	if (f->array[0]->string == "has" ||
	    f->array[0]->string == "!has") {
		if (f->array.size() != 2) {
			fprintf(stderr, "Wrong number of array elements in filter: %s\n", json_stringify(f).c_str());
			exit(EXIT_FAILURE);
		}

		if (f->array[0]->string == "has") {
			if (f->array[1]->type != JSON_STRING) {
				fprintf(stderr, "\"has\" key is not a string: %s\n", json_stringify(f).c_str());
				exit(EXIT_FAILURE);
			}
			return feature.count(std::string(f->array[1]->string)) != 0;
		}

		if (f->array[0]->string == "!has") {
			if (f->array[1]->type != JSON_STRING) {
				fprintf(stderr, "\"!has\" key is not a string: %s\n", json_stringify(f).c_str());
				exit(EXIT_FAILURE);
			}
			return feature.count(std::string(f->array[1]->string)) == 0;
		}
	}

	if (f->array[0]->string == "==" ||
	    f->array[0]->string == "!=" ||
	    f->array[0]->string == ">" ||
	    f->array[0]->string == ">=" ||
	    f->array[0]->string == "<" ||
	    f->array[0]->string == "<=") {
		if (f->array.size() != 3) {
			fprintf(stderr, "Wrong number of array elements in filter: %s\n", json_stringify(f).c_str());
			exit(EXIT_FAILURE);
		}
		if (f->array[1]->type != JSON_STRING) {
			fprintf(stderr, "\"!has\" key is not a string: %s\n", json_stringify(f).c_str());
			exit(EXIT_FAILURE);
		}

		auto ff = feature.find(std::string(f->array[1]->string));
		if (ff == feature.end()) {
			static bool warned = false;
			if (!warned) {
				std::string s = json_stringify(f);
				fprintf(stderr, "Warning: attribute not found for comparison: %s\n", s.c_str());
				warned = true;
			}
			if (f->array[0]->string == "!=") {
				return true;  //  attributes that aren't found are not equal
			}
			return false;  // not found: comparison is false
		}

		bool fail = false;
		int cmp = compare(ff->second, f->array[2], fail);

		if (fail) {
			static bool warned = false;
			if (!warned) {
				std::string s = json_stringify(f);
				fprintf(stderr, "Warning: mismatched type in comparison: %s\n", s.c_str());
				warned = true;
			}
			if (f->array[0]->string == "!=") {
				return true;  // mismatched types are not equal
			}
			return false;
		}

		if (f->array[0]->string == "==") {
			return cmp == 0;
		}
		if (f->array[0]->string == "!=") {
			return cmp != 0;
		}
		if (f->array[0]->string == ">") {
			return cmp > 0;
		}
		if (f->array[0]->string == ">=") {
			return cmp >= 0;
		}
		if (f->array[0]->string == "<") {
			return cmp < 0;
		}
		if (f->array[0]->string == "<=") {
			return cmp <= 0;
		}

		fprintf(stderr, "Internal error: can't happen: %s\n", json_stringify(f).c_str());
		exit(EXIT_FAILURE);
	}

	if (f->array[0]->string == "all" ||
	    f->array[0]->string == "any" ||
	    f->array[0]->string == "none") {
		bool v;

		if (f->array[0]->string == "all") {
			v = true;
		} else {
			v = false;
		}

		for (size_t i = 1; i < f->array.size(); i++) {
			bool out = eval(feature, f->array[i]);

			if (f->array[0]->string == "all") {
				v = v && out;
				if (!v) {
					break;
				}
			} else {
				v = v || out;
				if (v) {
					break;
				}
			}
		}

		if (f->array[0]->string == "none") {
			return !v;
		} else {
			return v;
		}
	}

	if (f->array[0]->string == "in" ||
	    f->array[0]->string == "!in") {
		if (f->array.size() < 2) {
			fprintf(stderr, "Array too small in filter: %s\n", json_stringify(f).c_str());
			exit(EXIT_FAILURE);
		}

		if (f->array[1]->type != JSON_STRING) {
			fprintf(stderr, "\"!has\" key is not a string: %s\n", json_stringify(f).c_str());
			exit(EXIT_FAILURE);
		}

		auto ff = feature.find(std::string(f->array[1]->string));
		if (ff == feature.end()) {
			static bool warned = false;
			if (!warned) {
				std::string s = json_stringify(f);
				fprintf(stderr, "Warning: attribute not found for comparison: %s\n", s.c_str());
				warned = true;
			}
			if (f->array[0]->string == "!in") {
				return true;  // attributes that aren't found are not in
			}
			return false;  // not found: comparison is false
		}

		bool found = false;
		for (size_t i = 2; i < f->array.size(); i++) {
			bool fail = false;
			int cmp = compare(ff->second, f->array[i], fail);

			if (fail) {
				static bool warned = false;
				if (!warned) {
					std::string s = json_stringify(f);
					fprintf(stderr, "Warning: mismatched type in comparison: %s\n", s.c_str());
					warned = true;
				}
				cmp = 1;
			}

			if (cmp == 0) {
				found = true;
				break;
			}
		}

		if (f->array[0]->string == "in") {
			return found;
		} else {
			return !found;
		}
	}

	fprintf(stderr, "Unknown filter %s\n", json_stringify(f).c_str());
	exit(EXIT_FAILURE);
}

bool evaluate(std::map<std::string, mvt_value> const &feature, std::string const &layer, std::shared_ptr<json_object> filter) {
	if (filter == NULL || filter->type != JSON_HASH) {
		fprintf(stderr, "Error: filter is not a hash: %s\n", json_stringify(filter).c_str());
		exit(EXIT_FAILURE);
	}

	bool ok = true;
	std::shared_ptr<json_object> f;

	f = json_hash_get(filter, layer.c_str());
	if (ok && f != NULL) {
		ok = eval(feature, f);
	}

	f = json_hash_get(filter, "*");
	if (ok && f != NULL) {
		ok = eval(feature, f);
	}

	return ok;
}

std::shared_ptr<json_object> read_filter(const char *fname) {
	FILE *fp = fopen(fname, "r");
	if (fp == NULL) {
		perror(fname);
		exit(EXIT_FAILURE);
	}

	std::shared_ptr<json_pull> jp = json_begin_file(fp);
	std::shared_ptr<json_object> filter = json_read_tree(jp);
	if (filter == NULL) {
		fprintf(stderr, "%s: %s\n", fname, jp->error.c_str());
		exit(EXIT_FAILURE);
	}
	json_disconnect(filter);
	json_end(jp);
	fclose(fp);
	return filter;
}

std::shared_ptr<json_object> parse_filter(const char *s) {
	std::shared_ptr<json_pull> jp = json_begin_string(s);
	std::shared_ptr<json_object> filter = json_read_tree(jp);
	if (filter == NULL) {
		fprintf(stderr, "Could not parse filter %s\n", s);
		fprintf(stderr, "%s\n", jp->error.c_str());
		exit(EXIT_FAILURE);
	}
	json_disconnect(filter);
	json_end(jp);
	return filter;
}
back to top