edu.harvard.iq.dataverse.mydata.MyDataFinder.java Source code

Java tutorial

Introduction

Here is the source code for edu.harvard.iq.dataverse.mydata.MyDataFinder.java

Source

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package edu.harvard.iq.dataverse.mydata;

import edu.harvard.iq.dataverse.DvObject;
import edu.harvard.iq.dataverse.DvObjectServiceBean;
import edu.harvard.iq.dataverse.RoleAssigneeServiceBean;
import edu.harvard.iq.dataverse.authorization.DataverseRolePermissionHelper;
import edu.harvard.iq.dataverse.authorization.groups.GroupServiceBean;
import edu.harvard.iq.dataverse.search.SearchFields;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import javax.json.Json;
import javax.json.JsonArrayBuilder;
import javax.json.JsonObjectBuilder;
import org.apache.commons.lang.StringUtils;

/**
 * Given a user and a set of filters (dvobject type, roles, publication status):
 *  - Use postgres to identify DvObject types
 *  - Format a solr query string
 * 
 * @author rmp553
 */
//@Stateless
public class MyDataFinder {

    private static final Logger logger = Logger.getLogger(MyDataFinder.class.getCanonicalName());

    private String userIdentifier;
    MyDataFilterParams filterParams;

    // !! RMP - Excluded by default; don't have cases yet to make this true
    private boolean excludeHarvestedData = true;
    //private String searchTerm = "*";

    // --------------------
    private DataverseRolePermissionHelper rolePermissionHelper;
    private RoleAssigneeServiceBean roleAssigneeService;
    private DvObjectServiceBean dvObjectServiceBean;
    private GroupServiceBean groupService;
    //private RoleAssigneeServiceBean roleService = new RoleAssigneeServiceBean();
    //private MyDataQueryHelperServiceBean myDataQueryHelperService;
    // --------------------
    public boolean errorFound = false;
    public String errorMessage = null;
    // --------------------

    public Map<Long, Boolean> harvestedDataverseIds = new HashMap<>();

    // Populated in initial query.  DvObject ids -- regardless of Dtype,
    // are sorted into respective buckets in regard to permissions.
    // The same id may appear in multiple lists--and more than once
    //
    // ----------------------------
    // POPULATED IN STEP 1 (1st query)
    // ----------------------------
    public Map<Long, Long> childToParentIds = new HashMap<>();
    public Map<Long, Boolean> idsWithDataversePermissions = new HashMap<>(); // { role id : true }
    public Map<Long, Boolean> idsWithDatasetPermissions = new HashMap<>(); // { role id : true }
    public Map<Long, Boolean> idsWithFilePermissions = new HashMap<>(); // { role id : true }

    private List<Long> directDvObjectIds = new ArrayList<>();

    // Lists later used to format Solr Queries
    // 
    // ----------------------------
    // POPULATED IN STEP 2 (2nd query)
    // ----------------------------
    private List<Long> directDataverseIds = new ArrayList<>();
    private List<Long> directDatasetIds = new ArrayList<>();
    private List<Long> directFileIds = new ArrayList<>();

    private List<Long> datasetParentIds = new ArrayList<>(); // dataverse has dataset permissions

    private List<Long> fileParentIds = new ArrayList<>(); // dataset has file permissions      
    private List<Long> fileGrandparentFileIds = new ArrayList<>(); // dataverse has file permissions

    public MyDataFinder(DataverseRolePermissionHelper rolePermissionHelper,
            RoleAssigneeServiceBean roleAssigneeService, DvObjectServiceBean dvObjectServiceBean,
            GroupServiceBean groupService) {
        this.msgt("MyDataFinder, constructor");
        this.rolePermissionHelper = rolePermissionHelper;
        this.roleAssigneeService = roleAssigneeService;
        this.dvObjectServiceBean = dvObjectServiceBean;
        this.groupService = groupService;
        this.loadHarvestedDataverseIds();
    }

    private void loadHarvestedDataverseIds() {

        for (Long id : dvObjectServiceBean.getAllHarvestedDataverseIds()) {
            harvestedDataverseIds.put(id, true);
        }

    }

    public void setExcludeHarvestedData(boolean val) {

        this.excludeHarvestedData = val;
    }

    public boolean isHarvestedDataExcluded() {
        return excludeHarvestedData;
    }

    /**
     * Check if a dvobject id is in the Harvested Id dict
     * @param id
     * @return 
     */
    private boolean isHarvesteDataverseId(Long id) {

        if (id == null) {
            return false;
        }

        if (this.harvestedDataverseIds.containsKey(id)) {
            return true;
        }
        return false;
    }

    public void initFields() {
        // ----------------------------
        // POPULATED IN STEP 1 (1st query)
        // ----------------------------
        this.childToParentIds = new HashMap<>();
        this.idsWithDataversePermissions = new HashMap<>(); // { role id : true }
        this.idsWithDatasetPermissions = new HashMap<>(); // { role id : true }
        this.idsWithFilePermissions = new HashMap<>(); // { role id : true }

        this.directDvObjectIds = new ArrayList<>();

        // Lists later used to format Solr Queries
        // 
        // ----------------------------
        // POPULATED IN STEP 2 (2nd query)
        // ----------------------------
        this.directDataverseIds = new ArrayList<>();
        this.directDatasetIds = new ArrayList<>();
        this.directFileIds = new ArrayList<>();

        this.datasetParentIds = new ArrayList<>(); // dataverse has dataset permissions

        this.fileParentIds = new ArrayList<>(); // dataset has file permissions      
        this.fileGrandparentFileIds = new ArrayList<>(); // dataverse has file permissions

    }

    public DataverseRolePermissionHelper getRolePermissionHelper() {
        return this.rolePermissionHelper;
    }
    /*
    private ArrayList<Long> dataverseIds;
    private ArrayList<Long> primaryDatasetIds;
    private ArrayList<Long> primaryFileIds;
    private ArrayList<Long> parentIds;
    */

    /*public void runFindDataSteps(String userIdentifier){
    this.userIdentifier = userIdentifier;
    msgt("runFindDataSteps: " + userIdentifier);
    if (!runStep1RoleAssignments()){
        return;
    }
    if (!runStep2DirectAssignments()){
        return;
    }
    if (!fileGrandparentFileIds.isEmpty()){
        runStep3FilePermsAssignedAtDataverse();
    }        
    }*/

    public void runFindDataSteps(MyDataFilterParams filterParams) {

        this.filterParams = filterParams;
        this.userIdentifier = this.filterParams.getUserIdentifier();

        if (this.filterParams.hasError()) {
            this.addErrorMessage(filterParams.getErrorMessage());
            return;
        }

        if (!runStep1RoleAssignments()) {
            return;
        }
        if (!runStep2DirectAssignments()) {
            return;
        }
        if (!fileGrandparentFileIds.isEmpty()) {
            runStep3FilePermsAssignedAtDataverse();
        }

    }

    public List<String> getSolrFilterQueriesForTotalCounts() {

        return this.getSolrFilterQueries(true);
    }

    public List<String> getSolrFilterQueries() {

        return this.getSolrFilterQueries(false);
    }

    /**
     * Get the final queries for the Solr Search object
     * 
     * @return 
     */
    private List<String> getSolrFilterQueries(boolean totalCountsOnly) {
        if (this.hasError()) {
            throw new IllegalStateException(
                    "Error encountered earlier.  Before calling this method on a MyDataFinder object, first check 'hasError()'");
        }

        // init filterQueries list
        List<String> filterQueries = new ArrayList<>();

        // -----------------------------------------------------------------
        // (1) Add entityId/parentId FQ 
        //  - by entityId (dvObject id) and parentId (dvObject ownerId)
        // -----------------------------------------------------------------
        String dvObjectFQ = this.getSolrDvObjectFilterQuery();
        if (dvObjectFQ == null) {
            this.addErrorMessage(DataRetrieverAPI.MSG_NO_RESULTS_FOUND);
            return null;
        }
        filterQueries.add(dvObjectFQ);
        // -----------------------------------------------------------------
        // For total counts, don't filter by publicationStatus or DvObjectType
        // -----------------------------------------------------------------
        if (totalCountsOnly == true) {
            return filterQueries;
        }

        // -----------------------------------------------------------------
        // (2) FQ by dvObjectType
        // -----------------------------------------------------------------
        filterQueries.add(this.filterParams.getSolrFragmentForDvObjectType());
        //fq=dvObjectType:(dataverses+OR+datasets+OR+files)
        //fq=(dvObjectType:Dataset)
        //filterQueries.add("dvObjectType:(dataverses OR datasets OR files)");

        // -----------------------------------------------------------------
        // (3) FQ by Publication Status
        // -----------------------------------------------------------------
        filterQueries.add(this.filterParams.getSolrFragmentForPublicationStatus());
        //fq=publicationStatus:"Unpublished"&fq=publicationStatus:"Draft"

        return filterQueries;
    }

    public String getSolrDvObjectFilterQuery() {

        if (this.hasError()) {
            throw new IllegalStateException(
                    "Error encountered earlier.  Before calling this method on a MyDataFinder object,first check 'hasError()'");
        }

        // Build lists of Ids
        List<Long> entityIds = new ArrayList<>();
        List<Long> parentIds = new ArrayList<>();
        List<Long> datasetParentIdsForFQ = new ArrayList<>();
        List<Long> fileParentIdsForFQ = new ArrayList<>();

        if (this.filterParams.areDataversesIncluded()) {
            entityIds.addAll(this.directDataverseIds); // dv ids
        }
        if (this.filterParams.areDatasetsIncluded()) {
            entityIds.addAll(this.directDatasetIds); // dataset ids
            parentIds.addAll(this.datasetParentIds); // dv ids that are dataset parents
            datasetParentIdsForFQ.addAll(this.datasetParentIds);
        }

        if (this.filterParams.areFilesIncluded()) {
            entityIds.addAll(this.directFileIds); // file ids
            parentIds.addAll(this.fileParentIds); // dataset ids that are file parents
            fileParentIdsForFQ.addAll(this.fileParentIds);
        }

        // Remove duplicates by Creating a Set
        //
        Set<Long> distinctEntityIds = new HashSet<>(entityIds);
        Set<Long> distinctParentIds = new HashSet<>(parentIds);

        if ((distinctEntityIds.size() == 0) && (distinctParentIds.size() == 0)) {
            this.addErrorMessage(DataRetrieverAPI.MSG_NO_RESULTS_FOUND);
            return null;
        }

        msg("distinctEntityIds (1): " + distinctEntityIds.size());
        msg("distinctParentIds: " + distinctParentIds.size());

        // See if we can trim down the list of distinctEntityIds
        //  If we have the parent of a distinctEntityId in distinctParentIds,
        //  then we query it via the parent
        //        
        List<Long> finalDirectEntityIds = new ArrayList<>();
        for (Long idToCheck : distinctEntityIds) {
            if (this.childToParentIds.containsKey(idToCheck)) { // Do we have the parent in our map?

                // we are not checking the parent of dataverses, so add this explicitly
                // Similar to SEK 7/015 - all direct dataverse ids are used because child dataverses with direct assignments are being lost.
                //
                if (this.directDataverseIds.contains(idToCheck)) {
                    // Add all dataverse ids explicitly
                    finalDirectEntityIds.add(idToCheck);

                } else if (!distinctParentIds.contains(this.childToParentIds.get(idToCheck))) {
                    // Is the parent also in our list of Ids to query?
                    // No, then let's check this id directly
                    //
                    finalDirectEntityIds.add(idToCheck);
                }
            }
        }
        // Set the distinctEntityIds to the finalDirectEntityIds
        //distinctEntityIds = new HashSet<>(distinctEntityIds);
        distinctEntityIds = new HashSet<>(finalDirectEntityIds);

        msg("distinctEntityIds (2): " + distinctEntityIds.size());

        // Start up a SolrQueryFormatter for building clauses
        //
        SolrQueryFormatter sqf = new SolrQueryFormatter();

        // Build clauses
        String entityIdClause = null;
        if (distinctEntityIds.size() > 0) {
            entityIdClause = sqf.buildIdQuery(distinctEntityIds, SearchFields.ENTITY_ID, null);
        }

        String parentIdClause = null;
        if (distinctParentIds.size() > 0) {
            parentIdClause = sqf.buildIdQuery(distinctParentIds, SearchFields.PARENT_ID, "datasets OR files");
        }

        if ((entityIdClause != null) && (parentIdClause != null)) {
            return "(" + entityIdClause + " OR " + parentIdClause + ")";

        } else if (entityIdClause != null) {
            // only entityIdClause
            return entityIdClause;

        } else if (parentIdClause != null) {
            // only parentIdClause
            return parentIdClause;
        }

        // Shouldn't get here...
        return null;
    }

    public String getTestString() {

        if (this.hasError()) {
            return this.getErrorMessage();
        }

        List<String> outputList = new ArrayList<>();

        // ----------------------
        // idsWithDatasetPermissions
        // ----------------------
        List<String> idList = new ArrayList<>();
        outputList.add("<h4>dataset ids: " + this.idsWithDatasetPermissions.size() + "</h4>");
        for (Map.Entry pair : this.idsWithDatasetPermissions.entrySet()) {
            idList.add(pair.getKey().toString());
        }
        outputList.add("<pre>" + StringUtils.join(idList, ", ") + "</pre>");

        // ----------------------
        // datasetParentIds
        // ----------------------
        List<String> idList2 = new ArrayList<>();
        outputList.add("<h4>datasetParentIds ids: " + this.datasetParentIds.size() + "</h4>");
        for (Long dpId : this.datasetParentIds) {
            idList2.add(dpId.toString());
        }
        outputList.add("<pre>" + StringUtils.join(idList2, ", ") + "</pre>");

        return StringUtils.join(outputList, "<br />");
    }

    public String formatUserIdentifierAsAssigneeIdentifier(String userIdentifier) {
        if (userIdentifier == null) {
            return null;
        }
        if (userIdentifier.startsWith("@")) {
            return userIdentifier;
        }
        return "@" + userIdentifier;
    }

    /**
     * "publication_statuses" : [ name 1, name 2, etc.]
     * 
     * @return 
     */
    public JsonObjectBuilder getSelectedFilterParamsAsJSON() {

        JsonObjectBuilder jsonData = Json.createObjectBuilder();
        jsonData.add("publication_statuses", this.filterParams.getListofSelectedPublicationStatuses())
                .add("role_names", this.getListofSelectedRoles());

        return jsonData;
    }

    /**
     * "publication_statuses" : [ name 1, name 2, etc.]
     * 
     * @return 
     */
    public JsonArrayBuilder getListofSelectedRoles() {

        JsonArrayBuilder jsonArray = Json.createArrayBuilder();

        for (Long roleId : this.filterParams.getRoleIds()) {
            jsonArray.add(this.rolePermissionHelper.getRoleName(roleId));
        }
        return jsonArray;
    }

    private boolean runStep1RoleAssignments() {

        List<Object[]> results = this.roleAssigneeService.getAssigneeAndRoleIdListFor(filterParams);

        //logger.info("runStep1RoleAssignments results: " + results.toString());

        if (results == null) {
            this.addErrorMessage("Sorry, the EntityManager isn't working (still).");
            return false;
        } else if (results.isEmpty()) {
            List<String> roleNames = this.rolePermissionHelper.getRoleNamesByIdList(this.filterParams.getRoleIds());
            if ((roleNames == null) || (roleNames.isEmpty())) {
                this.addErrorMessage("Sorry, you have no assigned roles.");
            } else {
                if (roleNames.size() == 1) {
                    this.addErrorMessage(
                            "Sorry, nothing was found for this role: " + StringUtils.join(roleNames, ", "));
                } else {
                    this.addErrorMessage(
                            "Sorry, nothing was found for these roles: " + StringUtils.join(roleNames, ", "));
                }
            }
            return false;
        }

        // Iterate through assigned objects, a single object may end up in 
        // multiple "buckets"
        for (Object[] ra : results) {
            Long dvId = (Long) ra[0];
            Long roleId = (Long) ra[1];

            //----------------------------------
            // Is this is a harvested Dataverse?
            // If so, skip it.
            //----------------------------------
            if ((this.isHarvestedDataExcluded()) && (this.isHarvesteDataverseId(dvId))) {
                continue;
            }

            //----------------------------------
            // Put dvId in 1 or more buckets, depending pn if role
            // applies to a Dataverse, Dataset, and/or File
            //----------------------------------
            if (this.rolePermissionHelper.hasDataversePermissions(roleId)) {
                this.idsWithDataversePermissions.put(dvId, true);
            }
            if (this.rolePermissionHelper.hasDatasetPermissions(roleId)) {
                this.idsWithDatasetPermissions.put(dvId, true);
            }
            if (this.rolePermissionHelper.hasFilePermissions(roleId)) {
                this.idsWithFilePermissions.put(dvId, true);
            }
            directDvObjectIds.add(dvId);
        }
        return true;
    }

    private boolean runStep2DirectAssignments() {

        if (this.hasError()) {
            throw new IllegalStateException(
                    "Error encountered earlier.  Before calling this method on a MyData object,first check 'hasError()'");
        }
        //msgt("runStep2DirectAssignments");

        List<Object[]> results = this.dvObjectServiceBean.getDvObjectInfoForMyData(directDvObjectIds);
        msgt("runStep2DirectAssignments number of results: " + results.size());
        //List<RoleAssignment> results = this.roleAssigneeService.getAssignmentsFor(this.userIdentifier);
        if (results.isEmpty()) {
            this.addErrorMessage("Sorry, you have no assigned Dataverses, Datasets, or Files.");
            return false;
        }

        Integer dvIdAsInteger;
        Long dvId;
        String dtype;
        Long parentId;

        // -----------------------------------------------
        // Iterate through assigned objects
        // -----------------------------------------------
        for (Object[] ra : results) {
            dvIdAsInteger = (Integer) ra[0]; // ?? Why?
            dvId = new Long(dvIdAsInteger);
            dtype = (String) ra[1];
            parentId = (Long) ra[2];

            // -----------------------------------------------
            // If this object is harvested, then skip it...
            // -----------------------------------------------
            if (this.isHarvestedDataExcluded()) {
                if ((this.isHarvesteDataverseId(dvId)) || (this.isHarvesteDataverseId(parentId))) {
                    continue;
                }
            }

            this.childToParentIds.put(dvId, parentId);

            switch (dtype) {
            case (DvObject.DATAVERSE_DTYPE_STRING):
                //if (this.idsWithDataversePermissions.containsKey(dvId)){
                this.directDataverseIds.add(dvId); // Direct dataverse (no indirect dataverses)
                //}
                if (this.idsWithDatasetPermissions.containsKey(dvId)) {
                    this.datasetParentIds.add(dvId); // Parent to dataset
                }
                if (this.idsWithFilePermissions.containsKey(dvId)) {
                    this.fileGrandparentFileIds.add(dvId); // Grandparent to file
                    // Also show the Dataset--even though the permissions don't apply directly
                    //  e.g. The Permissions flows:
                    //      from the DV -> through the DS -> to the file
                    this.datasetParentIds.add(dvId); // Parent to dataset
                }
                break;
            case (DvObject.DATASET_DTYPE_STRING):
                //if (this.idsWithDatasetPermissions.containsKey(dvId)){
                this.directDatasetIds.add(dvId); // Direct dataset
                //}
                if (this.idsWithFilePermissions.containsKey(dvId)) {
                    this.fileParentIds.add(dvId); // Parent to file
                }
                break;
            case (DvObject.DATAFILE_DTYPE_STRING):
                if (this.idsWithFilePermissions.containsKey(dvId)) {
                    this.directFileIds.add(dvId); // Direct file
                }
                break;
            } // end switch
        }

        // Direct ids no longer needed
        //
        this.directDvObjectIds = null;

        return true;
    }

    private boolean runStep3FilePermsAssignedAtDataverse() {
        msgt("runStep3FilePermsAssignedAtDataverse");
        if ((this.fileGrandparentFileIds == null) || (this.fileGrandparentFileIds.isEmpty())) {
            return true;
        }

        List<Object[]> results = this.dvObjectServiceBean
                .getDvObjectInfoByParentIdForMyData(this.fileGrandparentFileIds);
        msg("runStep3FilePermsAssignedAtDataverse results count: " + results.size());
        /*  SEK 07/09 Ticket 2329
        Removed failure for empty results - if there are none let it go
        */
        if (results.isEmpty()) {
            return true; // RMP, shouldn't throw an error if no results           
        }

        Integer dvIdAsInteger;
        Long dvId;
        String dtype;
        Long parentId;

        // Iterate through object list
        //
        for (Object[] ra : results) {
            dvIdAsInteger = (Integer) ra[0]; // ?? Why?
            dvId = new Long(dvIdAsInteger);
            dtype = (String) ra[1];
            parentId = (Long) ra[2];

            this.childToParentIds.put(dvId, parentId);

            // Should ALWAYS be a Dataset!
            if (dtype.equals(DvObject.DATASET_DTYPE_STRING)) {
                this.fileParentIds.add(dvId);
            }
        }

        return true;
    }
    /*
    private void postStep2Cleanup(){
    // Clear step1 lookups
    idsWithDataversePermissions = null;
    idsWithDatasetPermissions = null;
    idsWithFilePermissions = null;
    directDvObjectIds = null;   // Direct ids no longer needed
    }*/

    public boolean hasError() {
        return this.errorFound;
    }

    public String getErrorMessage() {
        return this.errorMessage;
    }

    private void addErrorMessage(String s) {
        this.errorFound = true;
        this.errorMessage = s;
    }

    private void msg(String s) {
        //logger.fine(s);
    }

    private void msgt(String s) {
        msg("-------------------------------");
        msg(s);
        msg("-------------------------------");
    }

} // end: MyDataFinder