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

org.openlca.git.util.MetaDataParser Maven / Gradle / Ivy

The newest version!
package org.openlca.git.util;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;

import org.openlca.git.util.FieldDefinition.Type;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;

public class MetaDataParser {

	private static final Logger log = LoggerFactory.getLogger(MetaDataParser.class);
	private final JsonParser parser;
	private final List defs;
	private final Map values = new HashMap<>();
	private final Map candidates = new HashMap<>();
	private final Set metCondition = new HashSet<>();
	private boolean skipOnArraysOrObjects;

	private MetaDataParser(InputStream json, List defs) throws IOException {
		this.parser = new JsonFactory().createParser(json);
		this.defs = new ArrayList<>(defs);
	}

	public static Map parse(InputStream json, String... fields) {
		var defs = Stream.of(fields).map(field -> FieldDefinition.firstOf(field)).toList();
		return parse(json, defs);
	}

	public static Map parse(InputStream json, FieldDefinition... defs) {
		return parse(json, defs != null ? Arrays.asList(defs) : null);		
	}

	public static Map parse(InputStream json, List defs) {
		if (defs == null || defs.size() == 0)
			return new HashMap<>();
		try {
			var instance = new MetaDataParser(json, defs);
			instance.parse();
			return instance.values;
		} catch (IOException e) {
			log.error("Error parsing dataset", e);
			return new HashMap<>();
		}
	}
	
	public static Map parseTop(InputStream json, String... fields) {
		if (fields == null || fields.length == 0)
			return new HashMap<>();
		try {
			var defs = Stream.of(fields).map(field -> FieldDefinition.firstOf(field)).toList();
			var instance = new MetaDataParser(json, defs);
			instance.skipOnArraysOrObjects = true;
			instance.parse();
			return instance.values;
		} catch (IOException e) {
			log.error("Error parsing dataset", e);
			return new HashMap<>();
		}
	}

	private void parse() throws IOException {
		var fields = new ArrayList();
		String current = null;
		while (!parser.isClosed() && !defs.isEmpty()) {
			var token = parser.nextToken();
			if (JsonToken.START_ARRAY.equals(token)) {
				if (skipOnArraysOrObjects)
					return;
			} else if (JsonToken.END_ARRAY.equals(token)) {
				for (var def : new ArrayList<>(defs)) {
					var c = join(fields, current);
					if (def.type == Type.ALL && startMatches(def.fields, c)
							&& (!def.isConditional() || startMatches(def.conditionFields, c))) {
						values.put(def.name, values.get(def.name));
						defs.remove(def);
					}
				}
			} else if (JsonToken.START_OBJECT.equals(token)) {
				if (current != null) {
					if (skipOnArraysOrObjects)
						return;
					fields.add(current);
				}
				for (var def : defs) {
					if (def.isConditional() && startMatches(def.conditionFields, fields)) {
						metCondition.remove(def.name);
						candidates.remove(def.name);
					}
				}
			} else if (JsonToken.END_OBJECT.equals(token)) {
				if (!fields.isEmpty()) {
					current = fields.remove(fields.size() - 1);
				}
			} else if (JsonToken.FIELD_NAME.equals(token)) {
				current = parser.getCurrentName();
			} else if (current != null) {
				handleValue(join(fields, current));
			}
		}
	}

	private void handleValue(List fields) throws IOException {
		String value = null;
		for (var def : new ArrayList<>(defs)) {
			if (isExactMatch(def.fields, fields)) {
				value = value != null ? value : parser.getValueAsString();
				handleValue(def, value);
			} else if (def.isConditional() && isExactMatch(def.conditionFields, fields)) {
				value = value != null ? value : parser.getValueAsString();
				if (def.meetsCondition.apply(value)) {
					metCondition.add(def.name);
					var candidate = candidates.get(def.name);
					if (candidate != null) {
						if (def.type == Type.FIRST) {
							values.put(def.name, candidate);
							defs.remove(def);
						} else {
							values.put(def.name, join(toList(values.get(def.name)), candidate));
						}
					}
				}
			}
		}
	}

	private void handleValue(FieldDefinition def, String v) throws IOException {
		var value = def.converter.apply(v);
		var conditionWasMet = !def.isConditional() || metCondition.contains(def.name);
		switch (def.type) {
			case FIRST -> {
				if (!conditionWasMet) {
					candidates.put(def.name, value);
				} else {
					values.put(def.name, value);
					defs.remove(def);
				}
			}
			case ALL -> {
				if (!conditionWasMet) {
					candidates.put(def.name, value);
				} else {
					values.put(def.name, join(toList(values.get(def.name)), value));
				}
			}
		}
	}

	private static boolean isExactMatch(List fields, List otherFields) {
		return isMatch(fields, otherFields, true);
	}

	private static boolean startMatches(List fields, List otherFields) {
		return isMatch(fields, otherFields, false);
	}

	private static boolean isMatch(List fields, List otherFields, boolean exact) {
		if (exact && fields.size() != otherFields.size())
			return false;
		if (fields.size() < otherFields.size())
			return false;
		for (var i = 0; i < otherFields.size(); i++)
			if (!fields.get(i).equals(otherFields.get(i)))
				return false;
		return true;
	}

	private static  List join(List current, T newElement) {
		var joined = new ArrayList(current);
		joined.add(newElement);
		return joined;
	}

	@SuppressWarnings("unchecked")
	private static List toList(Object value) {
		if (value == null)
			return new ArrayList<>();
		return (List) value;
	}

}