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

org.openlca.git.actions.GitStoreReader Maven / Gradle / Ivy

There is a newer version: 2.2.1
Show newest version
package org.openlca.git.actions;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.openlca.core.model.ModelType;
import org.openlca.git.RepositoryInfo;
import org.openlca.git.actions.ConflictResolver.ConflictResolution;
import org.openlca.git.actions.ConflictResolver.ConflictResolutionType;
import org.openlca.git.model.Change;
import org.openlca.git.model.Commit;
import org.openlca.git.model.ModelRef;
import org.openlca.git.model.Reference;
import org.openlca.git.repo.OlcaRepository;
import org.openlca.git.util.GitUtil;
import org.openlca.git.util.ModelRefMap;
import org.openlca.jsonld.Json;
import org.openlca.jsonld.JsonStoreReader;
import org.openlca.util.Strings;

import com.google.gson.Gson;
import com.google.gson.JsonObject;

class GitStoreReader implements JsonStoreReader {

	private static final Gson gson = new Gson();
	private final OlcaRepository repo;
	private final Commit localCommit;
	private final Commit remoteCommit;
	private final Categories categories;
	private final ModelRefMap changes;
	private final ConflictResolver conflictResolver;
	private final byte[] repoInfo;
	final Set resolvedConflicts = new HashSet<>();

	GitStoreReader(OlcaRepository repo, Commit remoteCommit, List remoteChanges) {
		this(repo, null, remoteCommit, remoteChanges, null);
	}

	GitStoreReader(OlcaRepository repo, Commit localCommit, Commit remoteCommit, List changes,
			ConflictResolver conflictResolver) {
		this.repo = repo;
		this.categories = Categories.of(repo, remoteCommit.id);
		this.localCommit = localCommit;
		this.remoteCommit = remoteCommit;
		this.conflictResolver = conflictResolver != null ? conflictResolver : ConflictResolver.NULL;
		this.changes = new ModelRefMap();
		this.repoInfo = repo.datasets.getRepositoryInfo(remoteCommit);
		changes.forEach(ref -> this.changes.put(ref, ref));
	}

	@Override
	public byte[] getBytes(String path) {
		if (RepositoryInfo.FILE_NAME.equals(path))
			return repoInfo;
		var binDir = GitUtil.findBinDir(path);
		if (binDir == null && GitUtil.isDatasetPath(path))
			return categories.getForPath(path);
		var type = ModelType.valueOf(path.substring(0, path.indexOf("/")));
		if (binDir != null) {
			var refId = GitUtil.getRefId(binDir);
			var filepath = path.substring(binDir.length() + 1);
			var ref = repo.references.get(type, refId, remoteCommit.id);
			return repo.datasets.getBinary(ref, filepath);
		}
		var refId = GitUtil.getRefId(path);
		var ref = changes.get(type, refId);
		return repo.datasets.getBytes(ref);
	}

	@Override
	public JsonObject get(ModelType type, String refId) {
		if (type == ModelType.CATEGORY)
			return categories.getForRefId(refId);
		if (!hasChanged(type, refId))
			return null;
		var ref = changes.get(type, refId);
		if (ref == null)
			return null;
		if (conflictResolver.peekConflictResolution(ref) == ConflictResolutionType.IS_EQUAL)
			return null;
		var data = repo.datasets.get(ref);
		var remote = parse(data);
		if (!conflictResolver.isConflict(ref))
			return remote;
		var resolution = conflictResolver.resolveConflict(ref, remote);
		if (resolution == null)
			throw new ConflictException(type, refId);
		if (resolution.type == ConflictResolutionType.IS_EQUAL)
			return null;
		if (resolution.type == ConflictResolutionType.OVERWRITE) {
			resolveOverwrite(ref);
			return remote;
		}
		if (resolution.type == ConflictResolutionType.KEEP && localCommit != null) {
			resolveKeep(ref);
			return null;
		}
		return resolveMerge(ref, resolution);
	}

	private void resolveOverwrite(Reference ref) {
		// commit writer will use remote commit version when no conflict
		// resolution change is provided, only in case of a move, the
		// deletion needs to be applied to the repo otherwise the dataset will
		// appear in both categories
		var localRef = repo.references.get(ref.type, ref.refId, localCommit.id);
		if (localRef != null && !ref.path.equals(localRef.path)) {
			resolvedConflicts.add(Change.delete(localRef));
		}
	}

	private void resolveKeep(Reference ref) {
		var localRef = repo.references.get(ref.type, ref.refId, localCommit.id);
		if (localRef == null) {
			resolvedConflicts.add(Change.delete(ref));
		} else if (!ref.path.equals(localRef.path)) {
			resolvedConflicts.addAll(Change.move(ref, localRef));
		} else {
			resolvedConflicts.add(Change.modify(localRef));
		}
	}

	private JsonObject resolveMerge(Reference ref, ConflictResolution resolution) {
		var localRef = repo.references.get(ref.type, ref.refId, localCommit.id);
		var category = Json.getString(resolution.data, "category");
		var mergedPath = GitUtil.toDatasetPath(localRef.type, category, localRef.refId);
		var mergedRef = new ModelRef(mergedPath);
		if (!mergedPath.equals(localRef.path)) {
			resolvedConflicts.addAll(Change.move(localRef, mergedRef));
		} else if (!mergedPath.equals(ref.path)) {
			resolvedConflicts.addAll(Change.move(ref, mergedRef));
		} else {
			resolvedConflicts.add(Change.modify(mergedRef));
		}
		return resolution.data;
	}

	private boolean hasChanged(ModelType type, String refId) {
		return changes.contains(type, refId);
	}

	@Override
	public List getBinFiles(ModelType type, String refId) {
		if (type == ModelType.CATEGORY)
			return Collections.emptyList();
		var ref = changes.get(type, refId);
		if (ref == null)
			return Collections.emptyList();
		return repo.references.getBinaries(ref).stream()
				.map(binary -> ref.getBinariesPath() + "/" + binary)
				.toList();
	}

	@Override
	public List getFiles(String dir) {
		// TODO relevant for upgrades
		return new ArrayList<>();
	}

	@Override
	public List getRefIds(ModelType type) {
		return getChanges(type).stream()
				.map(r -> r.refId)
				.toList();
	}

	List getChanges(ModelType type) {
		if (type == ModelType.CATEGORY)
			return new ArrayList<>();
		return changes.get(type).stream()
				.map(this::replaceEqualOrKeptWithNull)
				.collect(Collectors.toList());
	}

	int size() {
		return changes.size();
	}

	private Reference replaceEqualOrKeptWithNull(Reference ref) {
		// performance improvement: JsonImport will load model from
		// database. If ref will not be imported and conflict
		// resolver can determine resolution without json data we
		// can skip that. Returning null values to still display progress
		// in ImportData
		var resolution = conflictResolver.peekConflictResolution(ref);
		if (resolution == ConflictResolutionType.IS_EQUAL)
			return null;
		if (resolution == ConflictResolutionType.KEEP && localCommit != null) {
			resolveKeep(ref);
			return null;
		}
		return ref;
	}

	private JsonObject parse(String data) {
		if (Strings.nullOrEmpty(data))
			return null;
		return gson.fromJson(data, JsonObject.class);
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy