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

org.eclipse.lyo.trs.client.util.ProviderUtil Maven / Gradle / Ivy

/*
 * Copyright (c) 2020 Contributors to the Eclipse Foundation
 *
 * See the NOTICE file(s) distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License 1.0
 * which is available at http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

package org.eclipse.lyo.trs.client.util;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.vocabulary.RDF;
import org.eclipse.lyo.core.trs.Base;
import org.eclipse.lyo.core.trs.ChangeEvent;
import org.eclipse.lyo.core.trs.ChangeLog;
import org.eclipse.lyo.core.trs.Page;
import org.eclipse.lyo.core.trs.TrackedResourceSet;
import org.eclipse.lyo.oslc4j.core.exception.LyoModelException;
import org.eclipse.lyo.oslc4j.core.model.AbstractResource;
import org.eclipse.lyo.oslc4j.provider.jena.JenaModelHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ProviderUtil {
    private final static Logger log = LoggerFactory.getLogger(ProviderUtil.class);

    public static boolean isNotEmptySingletonArray(Object[] changeLogs) {
        return changeLogs != null && changeLogs.length == 1;
    }

    public static boolean isNotEmpty(Object[] deletions) {
        return deletions != null && deletions.length > 0;
    }

    public static boolean isNilUri(URI currentPageUri) {
        return currentPageUri == null || currentPageUri.toString().equals(RDF.nil.getURI());
    }

    /**
     * returns true if the change log pojo contains the change event with the
     * given uri and false otherwise
     */
    public static boolean changeLogContainsEvent(URI syncPointUri, ChangeLog changeLog) {
        for (ChangeEvent changeEvent : changeLog.getChange()) {
            if (changeEvent.getAbout().equals(syncPointUri)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 1. create an ordered list of change events from the list of change logs
     * given as an argument 2. Cut the list at the last processed change event
     * 3. Optimize the changes list by removing all redundant events for the
     * same resource
     *
     * @param changeLogs the list of change logs containing the change events to be
     *                   processed
     *
     * @return the optimized ordered list of change events
     */
    public static List optimizedChangesList(List changeLogs,
            URI lastProcessedChangeEventUri) {
        Collections.reverse(changeLogs);

        ChangeLog firstChangeLog = changeLogs.get(0);
        List firstChangelogEvents = firstChangeLog.getChange();

        // sort the events, needed for the cut-off later
        firstChangelogEvents.sort(Comparator.comparing(ChangeEvent::getOrder));
        firstChangeLog.setChange(firstChangelogEvents);

        // TODO Andrew@2018-02-28: just delete this line, getter after setter is some superstition
//        firstChangelogEvents = firstChangeLog.getChange();

        // if the last processed event is in the CL, it must be in the first
        // see 'fetchUpdatedChangeLogs' for details why
        int indexOfSync = -1;
        for (ChangeEvent changeEvent : firstChangelogEvents) {
            if (changeEvent.getAbout().equals(lastProcessedChangeEventUri)) {
                indexOfSync = firstChangelogEvents.indexOf(changeEvent);
                break;
            }
        }

        firstChangelogEvents = firstChangelogEvents.subList(indexOfSync + 1,
                firstChangelogEvents.size());
        firstChangeLog.setChange(firstChangelogEvents);

        List changesToProcess = new ArrayList<>();
        // merge all changelogs, events after the cutoff from the first log are there
        for (ChangeLog changeLog : changeLogs) {
            changesToProcess.addAll(changeLog.getChange());
        }
        changesToProcess.sort(Comparator.comparing(ChangeEvent::getOrder));

        // NB! Andrew@2018-02-27: this is not going to work for getting all changes via MQTT embedding
        // TODO Andrew@2019-01-15: refactor to support MQTT
        // TODO Andrew@2018-02-27: output warning for the events we missed if compress eliminated anything

        // replace all change events for a single resource with the latest event only
        List compressedChanges = compressChanges(changesToProcess);
        return compressedChanges;
    }

    public static List baseChangeEventsOptimizationSafe(
            List compressedChangesList, List baseMembers) {
        // do it once to improve performance actually
        final Set compressedUris = compressedChangesList.stream()
                .map(AbstractResource::getAbout)
                .collect(Collectors.toSet());
        List filteredBase = new ArrayList<>(baseMembers.size());

        for (URI baseMember : baseMembers) {
            if (!compressedUris.contains(baseMember)) {
                filteredBase.add(baseMember);
            } else {
                log.debug("Removing {} from the base because it has been updated since in the " +
                        "changelog", baseMember);
            }
        }

        return filteredBase;
    }

    /**
     * takes an ordered list of change events to be processed and compressed the
     * list by removing multiple change events for the same resource and keeping
     * only the latest event for that resource
     *
     * @return an ordered optimized list of change events
     */
    private static List compressChanges(List changesToProcess) {
        Map resToChangeEventMap = new HashMap<>();

        for (ChangeEvent changeToProcess : changesToProcess) {
            resToChangeEventMap.put(changeToProcess.getChanged(), changeToProcess);
        }

        // TODO Andrew@2018-02-27: create compressed list in one go
        // Algorithm:
        // - use a hashset to keep track of the resource uris
        // - walk the changesToProcess in reverse
        // - reverse the returned list
        List reducedChangesList;
        if (!resToChangeEventMap.isEmpty()) {
            reducedChangesList = new ArrayList<>(resToChangeEventMap.values());
            reducedChangesList.sort(Comparator.comparing(ChangeEvent::getOrder));
        } else {
            reducedChangesList = new ArrayList<>();
        }
        return reducedChangesList;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy