org.roda.core.plugins.misc.RodaFolderAIP.java Source code

Java tutorial

Introduction

Here is the source code for org.roda.core.plugins.misc.RodaFolderAIP.java

Source

/**
 * The contents of this file are subject to the license and copyright
 * detailed in the LICENSE file at the root of the source
 * tree and available online at
 *
 * https://github.com/keeps/roda
 */
package org.roda.core.plugins.misc;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Stream;

import org.apache.commons.io.FilenameUtils;
import org.roda.core.storage.fs.FSUtils;
import org.roda_project.commons_ip.model.AIP;
import org.roda_project.commons_ip.model.IPContentType;
import org.roda_project.commons_ip.model.IPDescriptiveMetadata;
import org.roda_project.commons_ip.model.IPFile;
import org.roda_project.commons_ip.model.IPMetadata;
import org.roda_project.commons_ip.model.IPRepresentation;
import org.roda_project.commons_ip.model.MetadataType;
import org.roda_project.commons_ip.model.ParseException;
import org.roda_project.commons_ip.model.RepresentationContentType;
import org.roda_project.commons_ip.model.RepresentationStatus;
import org.roda_project.commons_ip.model.impl.AIPWrap;
import org.roda_project.commons_ip.model.impl.BasicAIP;
import org.roda_project.commons_ip.utils.IPException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * {@link AIP} implementation that can read a RODA AIP from a folder.
 *
 * @author Rui Castro (rui.castro@gmail.com)
 */
final class RodaFolderAIP extends AIPWrap {
    /**
     * Logger.
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(RodaFolderAIP.class);

    /**
     * Constant string "id".
     */
    private static final String ID = "id";
    /**
     * Constant string "type".
     */
    private static final String TYPE = "type";
    /**
     * Constant string "version".
     */
    private static final String VERSION = "version";
    /**
     * Constant string "representations".
     */
    private static final String REPRESENTATIONS = "representations";
    /**
     * Constant string "metadata".
     */
    private static final String METADATA = "metadata";
    /**
     * Constant string "descriptive".
     */
    private static final String DESCRIPTIVE = "descriptive";
    /**
     * Constant string "descriptiveMetadata".
     */
    private static final String DESCRIPTIVE_METADATA = "descriptiveMetadata";
    /**
     * Constant string "preservation".
     */
    private static final String PRESERVATION = "preservation";
    /**
     * Constant string "other".
     */
    private static final String OTHER = "other";
    /**
     * Constant string "schemas".
     */
    private static final String SCHEMAS = "schemas";
    /**
     * Constant string "documentation".
     */
    private static final String DOCUMENTATION = "documentation";
    /**
     * Constant string "submission".
     */
    private static final String SUBMISSION = "submission";

    /**
     * Constructor.
     *
     * @param aip
     *          the {@link AIP} to wrap.
     * @param path
     *          AIP path.
     */
    private RodaFolderAIP(final AIP aip, final Path path) {
        super(aip);
        setBasePath(path);
    }

    /**
     * Reads a {@link RodaFolderAIP} from the given folder.
     *
     * @param source
     *          the source folder.
     * @return a {@link RodaFolderAIP}.
     * @throws ParseException
     *           if some error occurs.
     */
    static AIP parse(final Path source) throws ParseException {
        return new RodaFolderAIP(new BasicAIP(), source).read();
    }

    /**
     * Read the AIP.
     *
     * @return the {@link AIP}.
     * @throws ParseException
     *           if some error occurred.
     */
    private AIP read() throws ParseException {
        try {

            final ObjectMapper mapper = new ObjectMapper();
            final JsonNode json = mapper.readTree(getBasePath().resolve("aip.json").toFile());

            this.setId(getBasePath().getFileName().toString());
            if (json.has("parentId")) {
                this.setAncestors(Collections.singletonList(json.get("parentId").asText()));
            }
            this.setContentType(new IPContentType(json.get(TYPE).asText()));
            this.setState(json.get("state").asText());

            final Path mdPath = getBasePath().resolve(METADATA);

            readJsonDescriptiveMDs(this, json);
            findAndAddPreservationMDs(this::addPreservationMetadata);
            findAndAddOtherMDs(mdPath.resolve(OTHER), this::addOtherMetadata);
            findIPFiles(getBasePath().resolve(SCHEMAS)).forEach(this::addSchema);
            findIPFiles(getBasePath().resolve(DOCUMENTATION)).forEach(this::addDocumentation);
            findIPFiles(getBasePath().resolve(SUBMISSION)).forEach(this::addSubmission);

            readRepresentations(this, json);

            return this;
        } catch (final IOException | IPException e) {
            LOGGER.debug("Error reading aip.json", e);
            throw new ParseException("Error reading aip.json", e);
        }
    }

    /**
     * Read {@link IPRepresentation} and update the {@link AIP}.
     *
     * @param aip
     *          the {@link AIP}.
     * @param json
     *          the JSON node.
     * @throws IPException
     *           if some error occurs.
     * @throws IOException
     *           if some I/O error occurs.
     */
    private void readRepresentations(final AIP aip, final JsonNode json) throws IPException, IOException {
        if (json.has(REPRESENTATIONS)) {
            final Iterator<JsonNode> iterator = json.get(REPRESENTATIONS).elements();
            while (iterator.hasNext()) {
                aip.addRepresentation(readRepresentation(iterator.next()));
            }
        }
    }

    /**
     * Read {@link IPRepresentation}.
     *
     * @param json
     *          the JSON node.
     * @return the {@link IPRepresentation}.
     * @throws IOException
     *           if some I/O error occurs.
     * @throws IPException
     *           if some error occured.
     */
    private IPRepresentation readRepresentation(final JsonNode json) throws IOException, IPException {
        final IPRepresentation rep = new IPRepresentation(json.get(ID).asText());
        rep.setStatus(json.get("original").asBoolean() ? RepresentationStatus.getORIGINAL()
                : RepresentationStatus.getOTHER());
        rep.setContentType(new RepresentationContentType(json.get(TYPE).asText()));

        final Path repPath = getBasePath().resolve(REPRESENTATIONS).resolve(rep.getRepresentationID());
        final Path mdPath = repPath.resolve(METADATA);

        findIPFiles(repPath.resolve("data")).forEach(rep::addFile);
        findDescriptiveMDs(mdPath.resolve(DESCRIPTIVE)).forEach(rep::addDescriptiveMetadata);
        findPreservationMDs(mdPath.resolve(PRESERVATION)).forEach(rep::addPreservationMetadata);
        findAndAddOtherMDs(mdPath.resolve(OTHER), rep::addOtherMetadata);
        // TODO: ask for example of representation agents
        findIPFiles(repPath.resolve(SCHEMAS)).forEach(rep::addDocumentation);
        findIPFiles(repPath.resolve(DOCUMENTATION)).forEach(rep::addDocumentation);

        return rep;
    }

    /**
     * Read {@link IPDescriptiveMetadata} records from <code>aip.json</code>
     * "descriptiveMetadata" field and update the {@link AIP}.
     *
     * @param aip
     *          the {@link AIP}.
     * @param json
     *          the JSON node.
     * @throws IPException
     *           if some error occurs.
     */
    private void readJsonDescriptiveMDs(final AIP aip, final JsonNode json) throws IPException {
        if (json.has(DESCRIPTIVE_METADATA)) {
            final Iterator<JsonNode> iterator = json.get(DESCRIPTIVE_METADATA).elements();
            while (iterator.hasNext()) {
                aip.addDescriptiveMetadata(readJsonDescriptionMD(iterator.next()));
            }
        }
    }

    /**
     * Read {@link IPDescriptiveMetadata} from a {@link JsonNode}.
     *
     * @param json
     *          the JSON node.
     * @return the {@link IPDescriptiveMetadata}.
     */
    private IPDescriptiveMetadata readJsonDescriptionMD(final JsonNode json) {
        final String id = json.get(ID).asText();
        final String version = json.has(VERSION) ? json.get(VERSION).asText() : null;
        return new IPDescriptiveMetadata(id,
                new IPFile(getBasePath().resolve(METADATA).resolve(DESCRIPTIVE).resolve(id)),
                new MetadataType(json.get(TYPE).asText()), version);
    }

    /**
     * Find preservation metadata files and adds them to {@link AIP}.
     *
     * @param mdSetter
     *          the {@link IPMetadataSetter}.
     * @throws IOException
     *           if some I/O error occurs.
     * @throws IPException
     *           if some other error occurs.
     */
    private void findAndAddPreservationMDs(final IPMetadataSetter mdSetter) throws IOException, IPException {
        for (IPMetadata md : findPreservationMDs(getBasePath().resolve(METADATA).resolve(PRESERVATION))) {
            mdSetter.addMetadata(md);
        }
    }

    /**
     * Find other metadata files and for each one calls
     * {@link IPMetadataSetter#addMetadata(IPMetadata)}.
     *
     * @param omdPath
     *          the {@link Path} to other metadata files.
     * @param mdSetter
     *          the {@link IPMetadataSetter}.
     * @throws IOException
     *           if some I/O error occurs.
     * @throws IPException
     *           if some other error occurs.
     */
    private void findAndAddOtherMDs(final Path omdPath, final IPMetadataSetter mdSetter)
            throws IOException, IPException {
        if (FSUtils.isDirectory(omdPath)) {
            final Iterator<Path> paths = Files.list(omdPath).iterator();
            while (paths.hasNext()) {
                final Path mdPath = paths.next();
                if (FSUtils.isDirectory(mdPath)) {
                    for (IPMetadata md : findMDs(mdPath,
                            MetadataType.OTHER().setOtherType(mdPath.getFileName().toString()))) {
                        mdSetter.addMetadata(md);
                    }
                }
                if (FSUtils.isFile(mdPath)) {
                    for (IPMetadata md : findMDs(mdPath, MetadataType.OTHER())) {
                        mdSetter.addMetadata(md);
                    }
                }
            }
        }
    }

    /**
     * Find {@link IPDescriptiveMetadata}s in the given {@link Path}.
     *
     * @param dmdPath
     *          the {@link Path}.
     * @return the {@link List <IPDescriptiveMetadata>}.
     * @throws IOException
     *           if some I/O error occurs.
     */
    private List<IPDescriptiveMetadata> findDescriptiveMDs(final Path dmdPath) throws IOException {
        final List<IPDescriptiveMetadata> list = new ArrayList<>();
        if (FSUtils.isDirectory(dmdPath)) {
            try (Stream<Path> paths = Files.walk(dmdPath)) {
                paths.forEach(filePath -> {
                    if (FSUtils.isFile(filePath)) {
                        list.add(new IPDescriptiveMetadata(
                                FilenameUtils.removeExtension(filePath.getFileName().toString()),
                                new IPFile(filePath), MetadataType.OTHER(), null));
                    }
                });
            }
        }
        return list;
    }

    /**
     * Find {@link IPMetadata}s in the given {@link Path}.
     *
     * @param mdPath
     *          the {@link Path}.
     * @return the {@link List<IPMetadata>}.
     * @throws IOException
     *           if some I/O error occurs.
     */
    private List<IPMetadata> findPreservationMDs(final Path mdPath) throws IOException {
        return findMDs(mdPath, new MetadataType(MetadataType.MetadataTypeEnum.PREMIS));
    }

    /**
     * Find {@link IPMetadata}s in the given {@link Path}.
     *
     * @param mdPath
     *          the {@link Path}.
     * @param mdType
     *          the {@link MetadataType}.
     * @return the {@link List<IPMetadata>}.
     * @throws IOException
     *           if some I/O error occurs.
     */
    private List<IPMetadata> findMDs(final Path mdPath, final MetadataType mdType) throws IOException {
        final List<IPMetadata> list = new ArrayList<>();
        if (FSUtils.isDirectory(mdPath)) {
            try (Stream<Path> paths = Files.walk(mdPath)) {
                paths.forEach(filePath -> {
                    if (FSUtils.isFile(filePath)) {
                        list.add(new IPMetadata(FilenameUtils.removeExtension(filePath.getFileName().toString()),
                                new IPFile(filePath), mdType));
                    }
                });
            }
        }
        return list;
    }

    /**
     * Find {@link IPFile}s in the given {@link Path}.
     *
     * @param filesPath
     *          the {@link Path}.
     * @return the {@link List<IPFile>}.
     * @throws IOException
     *           if some I/O error occurs.
     */
    private List<IPFile> findIPFiles(final Path filesPath) throws IOException {
        final List<IPFile> list = new ArrayList<>();
        if (FSUtils.isDirectory(filesPath)) {
            try (Stream<Path> paths = Files.walk(filesPath)) {
                paths.forEach(filePath -> {
                    if (FSUtils.isFile(filePath)) {
                        list.add(new IPFile(filePath));
                    }
                });
            }
        }
        return list;
    }

    /**
     * {@link IPMetadata} setter interface. Implementations of this interface
     * should add the {@link IPMetadata} records to themselves.
     */
    @FunctionalInterface
    interface IPMetadataSetter {
        /**
         * Add the specified metadata.
         *
         * @param md
         *          the {@link IPMetadata}.
         * @throws IPException
         *           if some error occurred.
         */
        void addMetadata(IPMetadata md) throws IPException;
    }

}