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

edu.stanford.protege.webprotege.revision.ProjectChangesManager Maven / Gradle / Ivy

The newest version!
package edu.stanford.protege.webprotege.revision;

import com.google.common.base.Stopwatch;
import com.google.common.collect.*;
import edu.stanford.protege.webprotege.axiom.AxiomIRISubjectProvider;
import edu.stanford.protege.webprotege.change.OntologyChange;
import edu.stanford.protege.webprotege.change.ProjectChange;
import edu.stanford.protege.webprotege.diff.DiffElement;
import edu.stanford.protege.webprotege.diff.DiffElementRenderer;
import edu.stanford.protege.webprotege.diff.Revision2DiffElementsTranslator;
import edu.stanford.protege.webprotege.inject.ProjectSingleton;
import edu.stanford.protege.webprotege.common.Page;
import edu.stanford.protege.webprotege.common.PageRequest;
import edu.stanford.protege.webprotege.common.ProjectId;
import edu.stanford.protege.webprotege.renderer.RenderingManager;
import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import javax.inject.Inject;
import javax.inject.Provider;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

/**
 * Matthew Horridge
 * Stanford Center for Biomedical Informatics Research
 * 27/05/15
 */
@ProjectSingleton
public class ProjectChangesManager {

    private static final Logger logger = LoggerFactory.getLogger(ProjectChangesManager.class);

    public static final int DEFAULT_CHANGE_LIMIT = 50;

    private final ProjectId projectId;

    private final RevisionManager revisionManager;

    private final RenderingManager browserTextProvider;

    private final Comparator changeRecordComparator;

    private final Provider revision2DiffElementsTranslatorProvider;

    private final Table, ImmutableList> cache = HashBasedTable.create();

    @Inject
    public ProjectChangesManager(ProjectId projectId,
                                 @Nonnull RevisionManager revisionManager,
                                 @Nonnull RenderingManager browserTextProvider,
                                 @Nonnull Comparator changeRecordComparator,
                                 @Nonnull Provider revision2DiffElementsTranslatorProvider) {
        this.projectId = projectId;
        this.revisionManager = revisionManager;
        this.browserTextProvider = browserTextProvider;
        this.changeRecordComparator = changeRecordComparator;
        this.revision2DiffElementsTranslatorProvider = revision2DiffElementsTranslatorProvider;
    }

    private static Multimap, OntologyChange> getChangesBySubject(Revision revision) {
        Multimap, OntologyChange> results = HashMultimap.create();
        revision.getChanges().forEach(record -> results.put(getSubject(record), record));
        return results;
    }

    private static Optional getSubject(OntologyChange change) {
        if (change.isAxiomChange()) {
            var axiom = change.getAxiomOrThrow();
            return getSubject(axiom);
        }
        else {
            return Optional.empty();
        }
    }

    private static Optional getSubject(OWLAxiom axiom) {
        AxiomIRISubjectProvider subjectProvider = new AxiomIRISubjectProvider(IRI::compareTo);
        return subjectProvider.getSubject(axiom);
    }

    public Page getProjectChanges(Optional subject,
                                                 PageRequest pageRequest) {
        ImmutableList revisions = revisionManager.getRevisions();
        if (subject.isPresent()) {
            // We need to scan revisions to find the ones containing a particular subject
            // We ignore the page request here.
            // This needs reworking really, but the number of changes per entity is usually small
            // so this works for now.
            ImmutableList.Builder changes = ImmutableList.builder();
            for (Revision revision : revisions) {
                getProjectChangesForRevision(revision, subject, changes);
            }
            ImmutableList theChanges = changes.build();
            return Page.create(1, 1, theChanges, theChanges.size());
        }
        else {
            // Pages are in reverse order
            ImmutableList.Builder changes = ImmutableList.builder();
            revisions.reverse().stream()
                    .skip(pageRequest.getSkip())
                    .limit(pageRequest.getPageSize())
                    .forEach(revision -> getProjectChangesForRevision(revision, subject, changes));
            ImmutableList changeList = changes.build();
            int pageCount = (revisions.size() / pageRequest.getPageSize()) + 1;
            return Page.create(pageRequest.getPageNumber(),
                              pageCount,
                              changeList, changeList.size());
        }
    }

    public ImmutableList getProjectChangesForSubjectInRevision(OWLEntity subject, Revision revision) {
        ImmutableList.Builder resultBuilder = ImmutableList.builder();
        getProjectChangesForRevision(revision, Optional.of(subject), resultBuilder);
        return resultBuilder.build();
    }

    private void getProjectChangesForRevision(Revision revision,
                                              Optional subject,
                                              ImmutableList.Builder changesBuilder) {
        if(!cache.containsRow(revision.getRevisionNumber())) {
            logger.debug("{} Building cache for revision {}", projectId, revision.getRevisionNumber().getValue());
            var stopwatch = Stopwatch.createStarted();
            var changeRecordsBySubject = getChangesBySubject(revision);
            changeRecordsBySubject.asMap().forEach((subj, records) -> {
                cache.put(revision.getRevisionNumber(), subj, ImmutableList.copyOf(records));
            });
            logger.debug("{} Cached revision {} in {} ms", projectId, revision.getRevisionNumber().getValue(), stopwatch.elapsed(TimeUnit.MILLISECONDS));
        }
        List limitedRecords = new ArrayList<>();
        final int totalChanges;
        if (subject.isPresent()) {
            List records = cache.get(revision.getRevisionNumber(), subject.map(OWLEntity::getIRI));
            if (records == null) {
                // Nothing in this revision that changes the subject
                return;
            }
            totalChanges = records.size();
            limitedRecords.addAll(records);
        }
        else {
            totalChanges = revision.getSize();
            revision.getChanges().stream()
                    .limit(DEFAULT_CHANGE_LIMIT)
                    .forEach(limitedRecords::add);
        }


        Revision2DiffElementsTranslator translator = revision2DiffElementsTranslatorProvider.get();
        List> axiomDiffElements = translator.getDiffElementsFromRevision(limitedRecords);
        sortDiff(axiomDiffElements);
        List> renderedDiffElements = renderDiffElements(axiomDiffElements);
        int pageElements = renderedDiffElements.size();
        int pageCount;
        if (pageElements == 0) {
            pageCount = 1;
        }
        else {
            pageCount = totalChanges / pageElements + (totalChanges % pageElements);
        }
        Page> page = Page.create(
                1,
                pageCount,
                renderedDiffElements,
                totalChanges
        );
        ProjectChange projectChange = ProjectChange.get(
                revision.getRevisionNumber(),
                revision.getUserId(),
                revision.getTimestamp(),
                revision.getHighLevelDescription(),
                totalChanges,
                page);
        changesBuilder.add(projectChange);
    }

    private List> renderDiffElements(List> axiomDiffElements) {

        List> diffElements = new ArrayList<>();
        DiffElementRenderer renderer = new DiffElementRenderer<>(browserTextProvider);
        for (DiffElement axiomDiffElement : axiomDiffElements) {
            diffElements.add(renderer.render(axiomDiffElement));
        }
        return diffElements;
    }


    private void sortDiff(List> diffElements) {
        Comparator> c =
                Comparator
                        .comparing((Function, OntologyChange>)
                                           DiffElement::getLineElement, changeRecordComparator)
                        .thenComparing(DiffElement::getDiffOperation)
                        .thenComparing(DiffElement::getSourceDocument);
        diffElements.sort(c);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy