org.opentestsystem.authoring.testitembank.service.impl.ZipXMLServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.opentestsystem.authoring.testitembank.service.impl.ZipXMLServiceImpl.java

Source

/*******************************************************************************
 * Educational Online Test Delivery System 
 * Copyright (c) 2014 American Institutes for Research
 *   
 * Distributed under the AIR Open Source License, Version 1.0 
 * See accompanying file AIR-License-1_0.txt or at
 * http://www.smarterapp.org/documents/American_Institutes_for_Research_Open_Source_Software_License.pdf
 ******************************************************************************/
package org.opentestsystem.authoring.testitembank.service.impl;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.XML;
import org.opentestsystem.authoring.testitembank.apipzip.domain.ApipItemMetadata;
import org.opentestsystem.authoring.testitembank.apipzip.domain.ApipManifest;
import org.opentestsystem.authoring.testitembank.apipzip.domain.ApipManifestDependency;
import org.opentestsystem.authoring.testitembank.apipzip.domain.ApipManifestResource;
import org.opentestsystem.authoring.testitembank.apipzip.domain.ZipMetadata;
import org.opentestsystem.authoring.testitembank.apipzip.domain.ZipSmarterAppItemMetadata;
import org.opentestsystem.authoring.testitembank.domain.Item;
import org.opentestsystem.authoring.testitembank.exception.TestItemBankException;
import org.opentestsystem.authoring.testitembank.service.ZipXMLService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;

public class ZipXMLServiceImpl implements ZipXMLService {

    private static final Logger LOGGER = LoggerFactory.getLogger(ZipXMLServiceImpl.class);

    private static final String SMARTER_APP_METADATA_KEY = "smarterAppMetadata";

    private static final String METADATA_KEY = "metadata";
    private static final String LANGUAGE_KEY = "Language";

    private static final int PRETTY_PRINT_INDENT_FACTOR = 4;

    private static JAXBContext jaxbContext;

    private static final String SMARTER_ENGLISH_LANG = "eng";
    private static final String SMARTER_SPANISH_LANG = "spa";
    private static final String ITEM_ENGLISH_LANG = "ENU";
    private static final String ITEM_SPANISH_LANG = "ESN";
    private static final String ITEM_ENGLISH_BRAILLE_LANG = "ENU-Braille";
    private static final String NOT_BRAILLABLE_TYPE = "Not Braillable";

    private static ObjectMapper STATIC_JSON_MAPPER = new ObjectMapper();

    static {
        try {
            jaxbContext = JAXBContext.newInstance(
                    new Class[] { ApipManifest.class, ApipManifestResource.class, ApipManifestDependency.class });
        } catch (final JAXBException e) {
            throw new TestItemBankException("invalid.jaxb.context", e);
        }
        STATIC_JSON_MAPPER.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        STATIC_JSON_MAPPER.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
    }

    private static class APIP_RESOURCE_IDENTIFIER_FILTER implements Predicate<ApipManifestResource> {
        private final String targetIdentifier;

        private APIP_RESOURCE_IDENTIFIER_FILTER(final String targetIdentifier) {
            this.targetIdentifier = targetIdentifier;
        }

        public static APIP_RESOURCE_IDENTIFIER_FILTER getInstance(final String targetIdentifier) {
            return new APIP_RESOURCE_IDENTIFIER_FILTER(targetIdentifier);
        }

        @Override
        public boolean apply(final ApipManifestResource apipManifestResource) {
            return this.targetIdentifier.equals(apipManifestResource.getIdentifier());
        }
    };

    @Override
    public void writeToManifestToOutputStream(final ApipManifest manifest, final OutputStream outputStream)
            throws JAXBException {
        final Marshaller marshaller = jaxbContext.createMarshaller();
        marshaller.marshal(manifest, outputStream);
    }

    @Override
    public Item buildItem(final ApipManifestResource resource, final ApipItemMetadata apipMetadata,
            final String gridfsId, final String tenantId, final String itemBank) {

        Map<String, Object> itemMetadata;
        if (apipMetadata == null) {
            itemMetadata = new HashMap<String, Object>();
        } else {
            itemMetadata = Maps.newHashMap(apipMetadata.getMetadata());
        }

        Item item = new Item();
        try {
            item.setIdentifier(resource.getIdentifier());
            item.setOriginalZipGridId(gridfsId);
            if (apipMetadata != null) {
                ZipMetadata zipMetadataFile = STATIC_JSON_MAPPER.readValue(apipMetadata.getMetadataJson(),
                        ZipMetadata.class);

                ZipSmarterAppItemMetadata smarterAppMetadata = zipMetadataFile.getMetadata()
                        .getSmarterAppMetadata();
                item.setTenantId(tenantId);
                item.setItemBank(itemBank);
                item.setInteractionType(smarterAppMetadata.getInteractionType());
                item.setVersion(smarterAppMetadata.getVersion());
                item.setIntendedGrade(smarterAppMetadata.getIntendedGrade());
                item.setSubject(smarterAppMetadata.getSubject());
                item.setStatus(smarterAppMetadata.getStatus());
                item.setStimulusName(smarterAppMetadata.getStimulusName());
                item.setAssociatedStimulus(smarterAppMetadata.getAssociatedStimulus());
                item.setAssociatedTutorial(smarterAppMetadata.getAssociatedTutorial());
                item.setAssociatedWordlist(smarterAppMetadata.getAssociatedWordlist());
                //item.setLanguages(smarterAppMetadata.getLanguages());

                String brailleType = smarterAppMetadata.getBrailleType();

                // Convert languages into format TDS expects; add Braille, if needed
                List<String> itemLanguages = null;
                List<String> smarterLanguages = smarterAppMetadata.getLanguages();
                if (smarterLanguages != null) {
                    itemLanguages = new ArrayList<String>();
                    for (String lang : smarterLanguages) {
                        if (SMARTER_ENGLISH_LANG.equalsIgnoreCase(lang)) {
                            itemLanguages.add(ITEM_ENGLISH_LANG);
                            if (!StringUtils.isEmpty(brailleType)
                                    && !NOT_BRAILLABLE_TYPE.equalsIgnoreCase(brailleType))
                                itemLanguages.add(ITEM_ENGLISH_BRAILLE_LANG);

                        } else if (SMARTER_SPANISH_LANG.equalsIgnoreCase(lang)) {
                            itemLanguages.add(ITEM_SPANISH_LANG);

                        } else {
                            itemLanguages.add(lang);
                        }
                    }
                }
                item.setLanguages(itemLanguages);

                item.setItemEnemies(smarterAppMetadata.getEnemyItems());

                item.setItemScoreDimensionList(smarterAppMetadata.getItemScoreDimensionList());

                item.setItemStandardPublicationList(smarterAppMetadata.getItemStandardPublicationList());

                // Convert itemMetadata languages into format TDS expects
                if (itemMetadata.containsKey(LANGUAGE_KEY)) {
                    itemMetadata.put(LANGUAGE_KEY, itemLanguages);
                }
                item.setAllIncludedMetatdata(itemMetadata);
            }
        } catch (IOException e) {
            LOGGER.error("unable to parse metdata into item format for key fields", e);
        } catch (Exception ee) {
            LOGGER.error("error rebuilding item metadata ", ee);
        }
        return item;
    }

    @Override
    @SuppressWarnings("unchecked")
    public ApipItemMetadata extractMetadataFromXML(final InputStream inputstream) {
        final ApipItemMetadata apipMetadata = new ApipItemMetadata();

        Map<String, Object> metadata = new HashMap<String, Object>();
        String jsonPrettyPrintString = "";
        try {
            // List<String> lines = Files.readAllLines(metadataFile.toPath(), StandardCharsets.UTF_8);
            StringWriter writer = new StringWriter();
            IOUtils.copy(inputstream, writer, StandardCharsets.UTF_8);
            String xml = writer.toString();

            JSONObject xmlJSONObj = XML.toJSONObject(xml);
            jsonPrettyPrintString = xmlJSONObj.toString(PRETTY_PRINT_INDENT_FACTOR);

            HashMap<String, Object> mapFromJson = STATIC_JSON_MAPPER.readValue(jsonPrettyPrintString,
                    new TypeReference<HashMap<String, Object>>() {
                    });

            HashMap<String, Object> mp = convertValueToString(mapFromJson);

            HashMap<String, Object> metadataTags = (HashMap<String, Object>) mp.get(METADATA_KEY);
            HashMap<String, Object> smarterAppMetadataTags = (HashMap<String, Object>) metadataTags
                    .get(SMARTER_APP_METADATA_KEY);

            populateMap(metadata, smarterAppMetadataTags, Arrays.asList(new String[] {}));
            populateMap(metadata, metadataTags, Arrays.asList(new String[] { SMARTER_APP_METADATA_KEY }));

        } catch (JSONException | IOException je) {
            System.out.println(je.toString());
        }
        apipMetadata.setMetadata(metadata);
        apipMetadata.setMetadataJson(jsonPrettyPrintString);
        return apipMetadata;
    }

    private HashMap<String, Object> convertValueToString(HashMap<String, Object> objMap) {
        HashMap<String, Object> newMap = new HashMap<String, Object>();
        for (Entry<String, Object> mapEntry : objMap.entrySet()) {
            newMap.put(mapEntry.getKey(), convertToString(mapEntry.getValue()));
        }
        return newMap;
    }

    @SuppressWarnings("unchecked")
    private Object convertToString(Object objectToConvert) {
        Object newVal = null;
        if (objectToConvert instanceof List) {
            List<Object> newList = new ArrayList<Object>();
            List<Object> origList = (List<Object>) objectToConvert;
            for (Object listObj : origList) {
                newList.add(convertToString(listObj));
            }
            newVal = newList;
        } else if (objectToConvert instanceof Map) {
            Map<String, Object> origMap = (Map<String, Object>) objectToConvert;
            HashMap<String, Object> newMap = new HashMap<String, Object>();
            for (Entry<String, Object> mapEntry : origMap.entrySet()) {
                newMap.put(mapEntry.getKey(), convertToString(mapEntry.getValue()));
            }
            newVal = newMap;
        } else {
            newVal = objectToConvert.toString();
        }
        return newVal;
    }

    @SuppressWarnings("unchecked")
    private void populateMap(Map<String, Object> searchableMetadata, HashMap<String, Object> importedMetadata,
            List<String> excludedTags) {
        for (Entry<String, Object> entries : importedMetadata.entrySet()) {
            if (!excludedTags.contains(entries.getKey())) {
                Object currentObjects = searchableMetadata.get(entries.getKey());
                if (currentObjects == null) {
                    searchableMetadata.put(entries.getKey(), entries.getValue());
                } else {
                    List<Object> objectList;
                    if (currentObjects instanceof List) {
                        objectList = (List<Object>) currentObjects;
                    } else {
                        objectList = new ArrayList<Object>();
                        objectList.add(currentObjects);
                    }
                    if (entries.getValue() instanceof List) {
                        objectList.addAll((List<Object>) entries.getValue());
                    } else {
                        objectList.add(entries.getValue());
                    }
                }
            }
        }
    }

    @Override
    public ApipManifest extractManifestFromXML(final InputStream inputstream) {
        ApipManifest manifest = null;
        try (InputStream input = inputstream) {
            final Unmarshaller um = jaxbContext.createUnmarshaller();
            final JAXBElement<ApipManifest> myJAXBObject = um.unmarshal(getDocument(input), ApipManifest.class);
            manifest = myJAXBObject.getValue();
        } catch (final Exception e) {
            throw new TestItemBankException("apip.zip.extractor.failure.mainfest", e);
        }
        return manifest;
    }

    private Document getDocument(final InputStream input) throws Exception {
        final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        final DocumentBuilder builder = factory.newDocumentBuilder();
        return builder.parse(input);
    }

    @Override
    public ApipManifest consolidateManifests(final String targetIdentifier, final List<ApipManifest> manifests) {
        final ApipManifest consolidatedManifest = new ApipManifest();
        consolidatedManifest.setIdentifier(targetIdentifier);
        consolidatedManifest.setResources(new ArrayList<ApipManifestResource>());

        for (final ApipManifest manifest : manifests) {
            for (final ApipManifestResource resource : manifest.getResources()) {
                final boolean containsIdentifier = Iterables.tryFind(consolidatedManifest.getResources(),
                        APIP_RESOURCE_IDENTIFIER_FILTER.getInstance(resource.getIdentifier())).isPresent();
                if (!containsIdentifier) {
                    consolidatedManifest.getResources().add(resource);
                }
            }
        }

        Collections.sort(consolidatedManifest.getResources(), ApipManifestResource.BY_IDENTIFIER_ORDERING);
        return consolidatedManifest;
    }

}