package org.openlca.app.validation; import org.openlca.app.M; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.runtime.IProgressMonitor; import org.openlca.app.db.Database; import org.openlca.app.util.Labels; import org.openlca.core.database.Daos; import org.openlca.core.database.FlowDao; import org.openlca.core.database.IDatabase; import org.openlca.core.database.ParameterDao; import org.openlca.core.database.ProcessDao; import org.openlca.core.database.ProductSystemDao; import org.openlca.core.database.UnitGroupDao; import org.openlca.core.database.references.ExchangeReferenceSearch; import org.openlca.core.database.references.FlowPropertyFactorReferenceSearch; import org.openlca.core.database.references.IReferenceSearch; import org.openlca.core.database.references.IReferenceSearch.Reference; import org.openlca.core.model.AbstractEntity; import org.openlca.core.model.Exchange; import org.openlca.core.model.FlowPropertyFactor; import org.openlca.core.model.ModelType; import org.openlca.core.model.descriptors.CategorizedDescriptor; public class DatabaseValidation { private IProgressMonitor monitor; private static final List<Class<? extends AbstractEntity>> nesting = new ArrayList<>(); static { nesting.add(FlowPropertyFactor.class); nesting.add(Exchange.class); } public static DatabaseValidation with(IProgressMonitor monitor) { DatabaseValidation e = new DatabaseValidation(); e.monitor = monitor; return e; } public List<ModelStatus> evaluateAll() { IDatabase db = Database.get(); if (db == null) return Collections.emptyList(); Map<ModelType, Set<Long>> toEval = new HashMap<>(); if (monitor != null) monitor.beginTask(M.Preparing, IProgressMonitor.UNKNOWN); for (ModelType type : ModelType.values()) { if (monitor != null && monitor.isCanceled()) continue; if (!type.isCategorized()) continue; Set<Long> ids = getAll(type); if (ids.isEmpty()) continue; toEval.put(type, ids); } if (monitor != null && !monitor.isCanceled()) monitor.beginTask(M.ValidatingDatabase, toEval.size() * 3); List<ModelStatus> result = new ArrayList<>(); for (ModelType type : toEval.keySet()) { if (monitor != null && monitor.isCanceled()) continue; if (monitor != null) monitor.subTask(Labels.modelType(type)); result.addAll(eval(type, toEval.get(type))); } if (monitor != null) monitor.done(); return result; } private Set<Long> getAll(ModelType type) { Set<Long> ids = new HashSet<>(); for (CategorizedDescriptor d : loadDescriptors(type)) ids.add(d.getId()); return ids; } private List<? extends CategorizedDescriptor> loadDescriptors(ModelType type) { IDatabase db = Database.get(); if (type == ModelType.PARAMETER) return new ParameterDao(db).getGlobalDescriptors(); return Daos.createCategorizedDao(db, type).getDescriptors(); } public ModelStatus evaluate(ModelType type, long id) { IDatabase db = Database.get(); if (db == null) return null; if (monitor != null && !monitor.isCanceled()) monitor.beginTask("Validating model", IProgressMonitor.UNKNOWN); ModelStatus result = eval(type, Collections.singleton(id)).get(0); if (monitor != null) monitor.done(); return result; } public List<ModelStatus> evaluate(ModelType type) { IDatabase db = Database.get(); if (db == null) return Collections.emptyList(); if (monitor != null && !monitor.isCanceled()) monitor.beginTask("Validating model", IProgressMonitor.UNKNOWN); Set<Long> ids = getAll(type); List<ModelStatus> result = eval(type, ids); if (monitor != null) monitor.done(); return result; } private List<ModelStatus> eval(ModelType type, Set<Long> ids) { List<ModelStatus> result = new ArrayList<>(); if (monitor != null && monitor.isCanceled()) return result; List<Reference> references = findReferences(type, ids); if (monitor != null && monitor.isCanceled()) return result; if (monitor != null) monitor.worked(2); List<Reference> notExisting = checkExistence(references); if (monitor != null && monitor.isCanceled()) return result; Map<Long, Boolean> referenceSet = checkReferenceSet(type, ids); if (monitor != null && monitor.isCanceled()) return result; for (Long id : ids) { if (monitor != null && monitor.isCanceled()) continue; boolean validReferenceSet = referenceSet == null || referenceSet.get(id); ModelStatus status = new ModelStatus(type, id, filter(notExisting, id), validReferenceSet); result.add(status); } if (monitor != null && !monitor.isCanceled()) monitor.worked(1); return result; } private Map<Long, Boolean> checkReferenceSet(ModelType type, Set<Long> ids) { switch (type) { case PRODUCT_SYSTEM: return new ProductSystemDao(Database.get()).hasReferenceProcess(ids); case PROCESS: return new ProcessDao(Database.get()).hasQuantitativeReference(ids); case FLOW: return new FlowDao(Database.get()).hasReferenceFactor(ids); case UNIT_GROUP: return new UnitGroupDao(Database.get()).hasReferenceUnit(ids); default: return null; } } private List<Reference> evalNested(List<Reference> references) { List<Reference> result = new ArrayList<>(); result.addAll(references); Map<Class<? extends AbstractEntity>, List<Reference>> nested = new HashMap<>(); for (Reference ref : references) { if (monitor != null && monitor.isCanceled()) continue; if (!nesting.contains(ref.getType())) continue; List<Reference> ids = nested.get(ref.getType()); if (ids == null) nested.put(ref.getType(), ids = new ArrayList<>()); ids.add(ref); } for (Class<? extends AbstractEntity> nestingType : nesting) { if (monitor != null && monitor.isCanceled()) continue; if (!nested.containsKey(nestingType)) continue; List<Reference> nestedRefs = findReferences(nestingType, nested.get(nestingType)); result.addAll(nestedRefs); } return result; } private List<Reference> filter(List<Reference> references, long ownerId) { List<Reference> filtered = new ArrayList<>(); for (Reference ref : references) if (ref.ownerId == ownerId) filtered.add(ref); return filtered; } private List<Reference> checkExistence(List<Reference> references) { IDatabase db = Database.get(); List<Reference> notExisting = new ArrayList<>(); Map<Class<? extends AbstractEntity>, Map<Long, List<Reference>>> byType = splitByType(references); for (Class<? extends AbstractEntity> type : byType.keySet()) { if (monitor != null && monitor.isCanceled()) continue; Map<Long, List<Reference>> refMap = byType.get(type); Collection<List<Reference>> values = refMap.values(); Set<Long> ids = toIdSet(values); Map<Long, Boolean> map = Daos.createBaseDao(db, type).contains(ids); for (Long id : map.keySet()) if (map.get(id) == false) notExisting.addAll(refMap.get(id)); } for (Reference ref : references) if (!ref.optional && ref.id == 0l) notExisting.add(ref); return notExisting; } private Map<Class<? extends AbstractEntity>, Map<Long, List<Reference>>> splitByType(List<Reference> references) { Map<Class<? extends AbstractEntity>, Map<Long, List<Reference>>> byType = new HashMap<>(); for (Reference reference : references) { Map<Long, List<Reference>> forType = byType.get(reference.getType()); if (forType == null) byType.put(reference.getType(), forType = new HashMap<>()); add(reference, forType); } return byType; } private void add(Reference reference, Map<Long, List<Reference>> map) { List<Reference> list = map.get(reference.id); if (list == null) map.put(reference.id, list = new ArrayList<>()); list.add(reference); } private Set<Long> toIdSet(Collection<List<Reference>> references) { Set<Long> ids = new HashSet<>(); for (List<Reference> referenceList : references) for (Reference reference : referenceList) if (reference.id != 0l) ids.add(reference.id); return ids; } private List<Reference> findReferences(ModelType type, Set<Long> ids) { IDatabase db = Database.get(); List<Reference> refs = IReferenceSearch.FACTORY.createFor(type, db, true).findReferences(ids); return evalNested(refs); } private List<Reference> findReferences(Class<? extends AbstractEntity> type, List<Reference> refs) { List<Reference> nestedRefs = null; Map<Long, Class<? extends AbstractEntity>> ownerTypes = new HashMap<>(); Map<Long, Long> ownerIds = new HashMap<>(); Set<Long> ids = new HashSet<>(); for (Reference ref : refs) { ids.add(ref.id); ownerIds.put(ref.id, ref.ownerId); ownerTypes.put(ref.id, ref.getOwnerType()); } if (type == FlowPropertyFactor.class) nestedRefs = new FlowPropertyFactorReferenceSearch(Database.get(), ownerTypes, ownerIds).findReferences(ids); else if (type == Exchange.class) nestedRefs = new ExchangeReferenceSearch(Database.get(), ownerTypes, ownerIds).findReferences(ids); return evalNested(nestedRefs); } }