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

it.uniroma2.art.sheet2rdf.utils.StatusHandler Maven / Gradle / Ivy

The newest version!
package it.uniroma2.art.sheet2rdf.utils;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import it.uniroma2.art.sheet2rdf.core.MappingStruct;
import it.uniroma2.art.sheet2rdf.exception.InvalidWizardStatusException;
import it.uniroma2.art.sheet2rdf.header.AdvancedGraphApplication;
import it.uniroma2.art.sheet2rdf.header.GraphApplication;
import it.uniroma2.art.sheet2rdf.header.HeaderNameStruct;
import it.uniroma2.art.sheet2rdf.header.NodeConversion;
import it.uniroma2.art.sheet2rdf.header.NodeMemoization;
import it.uniroma2.art.sheet2rdf.header.NodeSanitization;
import it.uniroma2.art.sheet2rdf.header.SimpleGraphApplication;
import it.uniroma2.art.sheet2rdf.header.SimpleHeader;
import it.uniroma2.art.sheet2rdf.header.SubjectHeader;
import org.eclipse.rdf4j.repository.RepositoryConnection;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;


/**
 * History of status json file:
 *
 * version 1.0.0:
 * - introduced in s2rdf 4.0.2, is retro-compatible with older versions;
 *
 * version 4.2.0:
 * - introduced in s2rdf 4.2.0, so aligned scheme version with s2rdf version, is retro-compatible with the previous;
 * - status file is a json object with "scheme" attributes and, on the same level, all other attrs of the MappingStruct:
 *  {
 *      scheme: "...",
 *      subject: {...},
 *      headers: [...],
 *      headerFsMap: {...},
 *  }
 *
 * version 5.0.0:
 * - introduced multiple sheets management (but able to handle both single sheet and multiple sheets status)
 * - isolated the mapping structure in a dedicated "status" object, instead of having it to the same level of scheme
 * Single sheet
 *  {
 *      scheme: "...",
 *      status: {
 *          subject: {...},
 *          ...
 *      }
 *  }
 * Multiple sheets
 *  {
 *      scheme: "...",
 *      listStatus: [{
 *          sheetName: "...",
 *          status: {
 *              subject: {...},
 *              ...
 *          }
 *      }, {
 *          ...
 *      }]
 *  }
 *
 * version 6.0:
 *   - Attributes "memoize" and "memoizeId" of NodeConversion are now wrapped in "memoization" object
 *   - NodeConversion has a new sanitization object
 *  Before:
 *  {
 *      "nodeId" : "...",
 *      "converter" : { ... },
 *      "memoize" : ...,
 *      "memoizeId" : ...
 *  }
 *  After:
 *  {
 *      "nodeId" : "...",
 *      "converter" : { ... },
 *      "memoization": {
 *          "enabled": ...,
 *          "id": ...,
 *          "ignoreCase": ...
 *       },
 *      "sanitization": { ... },
 *  }
 */
public class StatusHandler {

    private static final String STATUS_SCHEME_VERSION_PROP = "scheme";
    private static final String SHEET_NAME_PROP = "sheetName";
    private static final String LIST_STATUS_PROP = "listStatus";
    private static final String STATUS_PROP = "status";

    private static final VersionNumber STATUS_SCHEME_1_0_0 = new VersionNumber("1.0.0");
    private static final VersionNumber STATUS_SCHEME_4_2_0 = new VersionNumber("4.2.0");
    private static final VersionNumber STATUS_SCHEME_5_0_0 = new VersionNumber("5.0.0");
    private static final VersionNumber STATUS_SCHEME_6_0_0 = new VersionNumber("6.0.0");
    private static final VersionNumber STATUS_SCHEME_CURR_VERSION = STATUS_SCHEME_6_0_0;

    private static final JsonNodeFactory jsonFactory = JsonNodeFactory.instance;
    private static final ObjectMapper mapper = new ObjectMapper();

    /**
     *
     * @param mappingStructMap
     * @param outputFile
     * @throws IOException
     */
    public static void exportGlobalStatus(Map mappingStructMap, File outputFile) throws IOException {
        ObjectNode jsonDataObject = jsonFactory.objectNode();

        ObjectWriter writer = mapper.writer(new DefaultPrettyPrinter());

        //write scheme version
        jsonDataObject.set(STATUS_SCHEME_VERSION_PROP, jsonFactory.textNode(STATUS_SCHEME_CURR_VERSION.toString()));

        //generate and write the list of MappingStruct status for each sheet
        ArrayNode statusListJson = jsonFactory.arrayNode();
        for (Map.Entry entry : mappingStructMap.entrySet()) {
            ObjectNode statusPairJson = jsonFactory.objectNode();
            ObjectNode msStatusJson = mappingStructToJson(entry.getValue());
            statusPairJson.set(SHEET_NAME_PROP, jsonFactory.textNode(entry.getKey()));
            statusPairJson.set(STATUS_PROP, msStatusJson);
            statusListJson.add(statusPairJson);
        }
        jsonDataObject.set(LIST_STATUS_PROP, statusListJson);

        writer.writeValue(outputFile, jsonDataObject);
    }

    public static void exportSheetStatus(MappingStruct mappingStruct, File outputFile) throws IOException {
        ObjectNode jsonDataObject = jsonFactory.objectNode();

        ObjectWriter writer = mapper.writer(new DefaultPrettyPrinter());

        //write scheme version
        jsonDataObject.set(STATUS_SCHEME_VERSION_PROP, jsonFactory.textNode(STATUS_SCHEME_CURR_VERSION.toString()));
        jsonDataObject.set(STATUS_PROP, mappingStructToJson(mappingStruct));

        writer.writeValue(outputFile, jsonDataObject);
    }

    private static ObjectNode mappingStructToJson(MappingStruct mappingStruct) {
        ObjectNode jsonDataObject = jsonFactory.objectNode();
        //serialize subjHeader
        jsonDataObject.set(JsonConstants.MAPPING_STRUCT_SUBJECT, mappingStruct.getSubjectHeaderAsJson());
        //serialize headers
        ArrayNode headersJson = jsonFactory.arrayNode();
        for (SimpleHeader h: mappingStruct.getHeaders()) {
            headersJson.add(mappingStruct.getSimpleHeaderAsJson(h));
        }
        jsonDataObject.set(JsonConstants.MAPPING_STRUCT_HEADERS, headersJson);
        //rule sanitization
        jsonDataObject.set(JsonConstants.MAPPING_STRUCT_RULE_SANITIZATION, mapper.valueToTree(mappingStruct.getRuleSanitization()));
        //serialize headerName-FeatureStructureName mapping
        jsonDataObject.set(JsonConstants.MAPPING_STRUCT_HEADER_FS_MAP, mapper.valueToTree(mappingStruct.getHeaderFsMap()));
        return jsonDataObject;
    }


    private static VersionNumber getStatusSchemeVersion(JsonNode jsonData) {
        VersionNumber statusSchemeVer = new VersionNumber("0.0.0"); //(default version, in case not specified in json status)
        JsonNode statusSchemeVerNode = jsonData.get(STATUS_SCHEME_VERSION_PROP);
        if (statusSchemeVerNode != null) { // scheme attr found in json
            statusSchemeVer = new VersionNumber(statusSchemeVerNode.asText());
        }
        return statusSchemeVer;
    }

    public static void restoreGlobalStatus(File inputJsonFile, Map mappingStructMap, RepositoryConnection connection) throws IOException, InvalidWizardStatusException {
        ObjectNode jsonData = (ObjectNode) mapper.readTree(inputJsonFile);
        updateMultiSheetJsonToLatest(jsonData);
        JsonNode listStatusJsonNode = jsonData.get(LIST_STATUS_PROP);
        if (listStatusJsonNode == null) {
            throw new InvalidWizardStatusException("The provided file does is not a valid multi-sheets status export");
        }
        ArrayNode listStatusArrayJson = (ArrayNode) listStatusJsonNode;
        for (int i = 0; i < listStatusArrayJson.size(); i++) {
            ObjectNode statusPairJson = (ObjectNode) listStatusArrayJson.get(i); //get node sheetName+status
            String sheetName = statusPairJson.get(SHEET_NAME_PROP).asText();
            MappingStruct ms = mappingStructMap.get(sheetName);
            if (ms != null) {
                // a mapping struct for the given sheet name has been found, it means that the spreadsheet file
                // contains a sheet with the given name (for which has been created a dedicated MappingStruct)
                statusJsonNodeToMappingStruct((ObjectNode) statusPairJson.get(STATUS_PROP), ms, connection);
            } //otherwise, ignore the status of such sheet
        }

    }

    public static void restoreSheetStatus(File inputJsonFile, MappingStruct mappingStruct, RepositoryConnection connection) throws IOException, InvalidWizardStatusException {
        ObjectNode json = (ObjectNode) mapper.readTree(inputJsonFile);
        updateSingleSheetJsonToLatest(json);
        JsonNode statusNode = json.get(STATUS_PROP);
        //"status" node expected to be in root object
        if (statusNode == null) {
            throw new InvalidWizardStatusException("The provided file is not a valid sheet status export");
        }
        statusJsonNodeToMappingStruct((ObjectNode) statusNode, mappingStruct, connection);
    }

    private static void statusJsonNodeToMappingStruct(ObjectNode statusJson, MappingStruct mappingStruct, RepositoryConnection connection) throws InvalidWizardStatusException {
        //recreate header-fs
        JsonNode headerFsMapNode = statusJson.get(JsonConstants.MAPPING_STRUCT_HEADER_FS_MAP);
        Map headerFsMap = mapper.convertValue(headerFsMapNode, new TypeReference>(){});

        //check if the map is compliant with the spreadsheet headers
        Set headersIds = headerFsMap.keySet();
        for (String headerId: headersIds) {
            SimpleHeader h = mappingStruct.getHeaderFromId(headerId);
            if (h == null) {
                throw new InvalidWizardStatusException(
                        "The provided status file contains reference to header(s) not existing in the current spreadsheet file");
            }
        }

        mappingStruct.setHeaderFsMap(headerFsMap);

        NodeSanitization ruleSanitization = new NodeSanitization();
        JsonNode ruleSanitizationNode = statusJson.get(JsonConstants.MAPPING_STRUCT_RULE_SANITIZATION);
        if (ruleSanitizationNode != null) { //v < 6.0.0 didn't have rule sanitization
            ruleSanitization = mapper.convertValue(ruleSanitizationNode, NodeSanitization.class);
        }
        mappingStruct.setRuleSanitization(ruleSanitization);

        /* RESTORE SUBJECT HEADER: "subject" */
        {
            SubjectHeader subjHeader = new SubjectHeader();

            JsonNode subjectNode = statusJson.get(JsonConstants.MAPPING_STRUCT_SUBJECT);
            //- "id"
            String id = subjectNode.get(JsonConstants.SUBJECT_HEADER_ID).asText();
            subjHeader.setId(id);
            //-node conversion: "node"
            JsonNode nodeJson = subjectNode.get(JsonConstants.SUBJECT_HEADER_NODE);
            NodeConversion nc = mapper.convertValue(nodeJson, NodeConversion.class);
            subjHeader.setNodeConversion(nc);
            //-graph application:
            // 	"graph"
            JsonNode graphJson = subjectNode.get(JsonConstants.SUBJECT_HEADER_GRAPH);
            SimpleGraphApplication graph = subjHeader.getGraphApplication();
            graph.fromJson(graphJson);
            //	"additionalGraphs"
            JsonNode additionalGraphsArrayJson = subjectNode.get(JsonConstants.SUBJECT_HEADER_ADDITIONAL_GRAPHS);
            List additionalGraphApplications = new ArrayList<>();
            if (additionalGraphsArrayJson != null && !additionalGraphsArrayJson.isNull()) {
                for (JsonNode addGraphJson : additionalGraphsArrayJson) {
                    SimpleGraphApplication ga = new SimpleGraphApplication();
                    ga.fromJson(addGraphJson);
                    additionalGraphApplications.add(ga);
                }
            }
            subjHeader.setAdditionalGraphApplications(additionalGraphApplications);

            mappingStruct.setSubjectHeader(subjHeader);
        }

        /* RESTORE HEADERS: "headers" */
        ArrayNode headersArrayNode = (ArrayNode) statusJson.get(JsonConstants.MAPPING_STRUCT_HEADERS);
        for (int i = 0; i < headersArrayNode.size(); i++) {
            JsonNode headerJson = headersArrayNode.get(i);
            String id = headerJson.get(JsonConstants.SIMPLE_HEADER_ID).asText();
            SimpleHeader header = mappingStruct.getHeaderFromId(id);

            //- name struct (restored simply from the full name)
            String headerName = headerJson.get(JsonConstants.SIMPLE_HEADER_NAME_STRUCT).get(JsonConstants.SIMPLE_HEADER_FULL_NAME).asText();
            header.setHeaderName(new HeaderNameStruct(headerName, connection, mappingStruct.getPrefixMapping()));
            //- "ignore"
            boolean ignore = headerJson.get(JsonConstants.SIMPLE_HEADER_IGNORE).asBoolean();
            header.setIgnore(ignore);
            //- node conversions: "nodes"
            JsonNode nodesArrayJson = headerJson.get(JsonConstants.SIMPLE_HEADER_NODES);
            List nodeConversions = new ArrayList<>();
            for (JsonNode nodeJson: nodesArrayJson) {
                NodeConversion nc = mapper.convertValue(nodeJson, NodeConversion.class);
                nodeConversions.add(nc);
            }
            header.setNodeConversions(nodeConversions);
            //- graph application: "graphs"
            JsonNode graphsArrayJson = headerJson.get(JsonConstants.SIMPLE_HEADER_GRAPH);
            List graphApplications = new ArrayList<>();
            for (JsonNode graphJson: graphsArrayJson) {
                GraphApplication ga;
                if (graphJson.get("property") != null) {
                    ga = new SimpleGraphApplication();
                } else {
                    ga = new AdvancedGraphApplication();
                }
                ga.fromJson(graphJson);
                graphApplications.add(ga);
            }
            header.setGraphApplications(graphApplications);
        }
    }


    /* STATUS UPDATE ROUTINES */

    private static void updateMultiSheetJsonToLatest(ObjectNode json) throws InvalidWizardStatusException {
        VersionNumber statusSchemeVer = getStatusSchemeVersion(json);
        if (statusSchemeVer.compareTo(STATUS_SCHEME_5_0_0) < 0) {
            //versions previous to 5.0.0 don't support multisheets management, so cannot restore global status
            throw new InvalidWizardStatusException("The provided file does is not a valid multi-sheets status export");
        }
        if (statusSchemeVer.compareTo(STATUS_SCHEME_6_0_0) < 0) {
            JsonNode listStatusJsonNode = json.get(LIST_STATUS_PROP);
            if (listStatusJsonNode == null) {
                throw new InvalidWizardStatusException("The provided file does is not a valid multi-sheets status export");
            }
            ArrayNode listStatusArrayJson = (ArrayNode) listStatusJsonNode;
            for (int i = 0; i < listStatusArrayJson.size(); i++) {
                JsonNode statusPairJson = listStatusArrayJson.get(i); //get node sheetName+status
                JsonNode statusNode = statusPairJson.get(STATUS_PROP);
                adaptStatusNodeTo6(statusNode);
            }
        }
    }

    private static void updateSingleSheetJsonToLatest(ObjectNode json) throws InvalidWizardStatusException {
        VersionNumber statusSchemeVer = getStatusSchemeVersion(json);
        if (statusSchemeVer.compareTo(STATUS_SCHEME_5_0_0) < 0) {
            adaptTo5(json);
        }
        if (statusSchemeVer.compareTo(STATUS_SCHEME_6_0_0) < 0) {
            adaptTo6(json);
        }
    }

    /**
     * Adapt json status structure to the structure of v 5.0.0.
     * Before 5.0.0 "subject", "headers" and "headerFsMap" were root attributes. Move them under a "status" object
     * @param json
     */
    private static void adaptTo5(ObjectNode json) throws InvalidWizardStatusException {
        JsonNode subjectNode = json.get(JsonConstants.MAPPING_STRUCT_SUBJECT);
        JsonNode headersNode = json.get(JsonConstants.MAPPING_STRUCT_HEADERS);
        JsonNode headerFsMapNode = json.get(JsonConstants.MAPPING_STRUCT_HEADER_FS_MAP);

        //v5.0.0. expects "subject", "headers" and "headerFsMap" to be in root object
        if (subjectNode == null || headersNode == null || headerFsMapNode == null) {
            throw new InvalidWizardStatusException("The provided file does is not a valid multi-sheets status export");
        }

        json.remove(JsonConstants.MAPPING_STRUCT_SUBJECT);
        json.remove(JsonConstants.MAPPING_STRUCT_HEADERS);
        json.remove(JsonConstants.MAPPING_STRUCT_HEADER_FS_MAP);

        ObjectNode statusNode = jsonFactory.objectNode();
        statusNode.set(JsonConstants.MAPPING_STRUCT_SUBJECT, subjectNode);
        statusNode.set(JsonConstants.MAPPING_STRUCT_HEADERS, headersNode);
        statusNode.set(JsonConstants.MAPPING_STRUCT_HEADER_FS_MAP, headerFsMapNode);
        json.set(STATUS_PROP, statusNode);

        json.set(STATUS_SCHEME_VERSION_PROP, jsonFactory.textNode(STATUS_SCHEME_5_0_0.toString()));
    }

    private static void adaptTo6(ObjectNode json) throws InvalidWizardStatusException {
        JsonNode statusNode = json.get(STATUS_PROP);
        //v6.0 expects "status" node to be in root object
        if (statusNode == null) {
            throw new InvalidWizardStatusException("The provided file is not a valid sheet status export");
        }
        adaptStatusNodeTo6(statusNode);
        json.set(STATUS_SCHEME_VERSION_PROP, jsonFactory.textNode(STATUS_SCHEME_6_0_0.toString()));
    }

    /**
     * Adapt json "status" object to the structure of v6.0
     * v6.0 changed the structure of NodeConversion, so update "node" in "subject" and "nodes" in "headers"
     * @param statusNode
     */
    private static void adaptStatusNodeTo6(JsonNode statusNode) {
        JsonNode subjectNode = statusNode.get(JsonConstants.MAPPING_STRUCT_SUBJECT);
        JsonNode nodeNode = subjectNode.get(JsonConstants.SUBJECT_HEADER_NODE);
        adaptNodeNodeTo6((ObjectNode) nodeNode);
        ArrayNode headersNode = (ArrayNode) statusNode.get(JsonConstants.MAPPING_STRUCT_HEADERS);
        for (int i = 0; i < headersNode.size(); i++) {
            JsonNode headerNode = headersNode.get(i);
            ArrayNode nodesNode = (ArrayNode) headerNode.get(JsonConstants.SIMPLE_HEADER_NODES);
            for (int j = 0; j < nodesNode.size(); j++) {
                adaptNodeNodeTo6((ObjectNode) nodesNode.get(j));
            }
        }
    }

    /**
     * Move the "memoize" and "memoizeId" attributes into a "memoization" object in the node.
     * Create a default "sanitization" object
     * @param nodeNode
     */
    private static void adaptNodeNodeTo6(ObjectNode nodeNode) {
        boolean memoize = nodeNode.get("memoize").asBoolean();
        String memoizeId = null;

        JsonNode memoizeIdNode = nodeNode.get("memoizeId");
        if (memoizeIdNode != null) {
            memoizeId = memoizeIdNode.asText();
        }
        nodeNode.remove("memoize");
        nodeNode.remove("memoizeId");

        NodeMemoization memoization = new NodeMemoization(memoize);
        memoization.setId(memoizeId);

        nodeNode.set("memoization", mapper.valueToTree(memoization));
        nodeNode.set("sanitization", mapper.valueToTree(new NodeSanitization()));
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy