/* DigiDoc4J library * * This software is released under either the GNU Library General Public * License (see LICENSE.LGPL). * * Note that the only valid version of the LGPL license as far as this * project is concerned is the original GNU Library General Public License * Version 2.1, February 1999 */ package org.digidoc4j; import java.io.File; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang.StringUtils; import org.digidoc4j.exceptions.InvalidDataFileException; import org.digidoc4j.exceptions.NotSupportedException; import org.digidoc4j.impl.CustomContainerBuilder; import org.digidoc4j.impl.bdoc.BDocContainerBuilder; import org.digidoc4j.impl.ddoc.DDocContainerBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Class for creating and opening containers. * <p> * Here's an example of creating a new container: * </p> * <p><code> * {@link Container} container = {@link ContainerBuilder}. <br/> *    {@link ContainerBuilder#aContainer(String) aContainer("BDOC")}. <br/> *    {@link ContainerBuilder#withConfiguration(Configuration) withConfiguration(configuration)}. // Configuration settings <br/> *    {@link ContainerBuilder#withDataFile(String, String) withDataFile("testFiles/legal_contract_1.txt", "text/plain")}. // Adding a document from a hard drive <br/> *    {@link ContainerBuilder#withDataFile(InputStream, String, String) withDataFile(inputStream, "legal_contract_2.txt", "text/plain")}. // Adding a document from a stream <br/> *    {@link ContainerBuilder#build() build()}; <br/> * </code></p> * <p> * Use {@link ContainerBuilder#aContainer() ContainerBuilder.aContainer()} to create a new container builder, populate the builder with data and * finally call {@link ContainerBuilder#build()} to create the container with the populated data. * Use {@link ContainerBuilder#fromExistingFile(String)} or {@link ContainerBuilder#fromStream(InputStream)} to open an existing container. * </p> */ public abstract class ContainerBuilder { private static final Logger logger = LoggerFactory.getLogger(ContainerBuilder.class); public static final String BDOC_CONTAINER_TYPE = "BDOC"; public static final String DDOC_CONTAINER_TYPE = "DDOC"; protected static Map<String, Class<? extends Container>> containerImplementations = new HashMap<>(); protected Configuration configuration; protected List<ContainerDataFile> dataFiles = new ArrayList<>(); protected String containerFilePath; protected InputStream containerInputStream; /** * Create a new BDoc container builder. * * @return builder for creating or opening a container. */ public static ContainerBuilder aContainer() { return aContainer(BDOC_CONTAINER_TYPE); } /** * Create a new container builder based on a container type. * * @param containerType a type of container to be created, e.g. "BDOC" or "DDOC". * * @return builder for creating a container. */ public static ContainerBuilder aContainer(String containerType) { if (isCustomContainerType(containerType)) { return new CustomContainerBuilder(containerType); } switch (containerType) { case BDOC_CONTAINER_TYPE: return new BDocContainerBuilder(); case DDOC_CONTAINER_TYPE: return new DDocContainerBuilder(); } throw new NotSupportedException("Container type is not supported: " + containerType); } /** * Builds a new container or opens existing container from the parameters given to the builder. * * @return fresh container. */ public Container build() { if (shouldOpenContainerFromFile()) { return openContainerFromFile(); } else if (shouldOpenContainerFromStream()) { return openContainerFromStream(); } Container container = createNewContainer(); addDataFilesToContainer(container); return container; } /** * Specify configuration for the container. * * @param configuration configuration to use for creating the container. * @return builder for creating or opening a container. */ public ContainerBuilder withConfiguration(Configuration configuration) { this.configuration = configuration; return this; } /** * Add a data file to the container. * * @param filePath data file location on the disk. * @param mimeType MIME type of the data file, for example 'text/plain' or 'application/msword' * @return builder for creating or opening a container. * @throws InvalidDataFileException */ public ContainerBuilder withDataFile(String filePath, String mimeType) throws InvalidDataFileException { dataFiles.add(new ContainerDataFile(filePath, mimeType)); return this; } /** * Add a data file from a stream to the container. * * @param inputStream stream of a data file to be added to the container. * @param fileName name of the data file to be added. * @param mimeType MIME type of the data file, for example 'text/plain' or 'application/msword' * @return builder for creating or opening a container. * @throws InvalidDataFileException */ public ContainerBuilder withDataFile(InputStream inputStream, String fileName, String mimeType) throws InvalidDataFileException { dataFiles.add(new ContainerDataFile(inputStream, fileName, mimeType)); return this; } /** * Add a data file to the container. * * @param file data file to be added to the container. * @param mimeType MIME type of the data file, for example 'text/plain' or 'application/msword' * @return builder for creating or opening a container. * @throws InvalidDataFileException */ public ContainerBuilder withDataFile(File file, String mimeType) throws InvalidDataFileException { dataFiles.add(new ContainerDataFile(file.getPath(), mimeType)); return this; } /** * Add a data file to the container. * * @param dataFile data file to be added to the container. * @return builder for creating or opening a container. */ public ContainerBuilder withDataFile(DataFile dataFile) { dataFiles.add(new ContainerDataFile(dataFile)); return this; } /** * Open container from an existing file. * * @param filePath absolute path to the container file. * @return builder for creating or opening a container. */ public ContainerBuilder fromExistingFile(String filePath) { this.containerFilePath = filePath; return this; } /** * Open container from a stream. * * @param containerInputStream stream of the container file to be opened. * @return builder for creating or opening a container. */ public ContainerBuilder fromStream(InputStream containerInputStream) { this.containerInputStream = containerInputStream; return this; } /** * Set a custom container implementation class to be used for the container type. * * @param containerType container type name used when handling such containers. * @param containerClass container implementation for handling such container types. * @param <T> container class extending the Container interface. * @see Container */ public static <T extends Container> void setContainerImplementation(String containerType, Class<T> containerClass) { logger.info("Using " + containerClass.getName() + "for container type " + containerType); containerImplementations.put(containerType, containerClass); } /** * Clear the list of custom container implementations and types * and continue using the default container types and implementations. */ public static void removeCustomContainerImplementations() { logger.info("Removing custom container implementations"); containerImplementations.clear(); } protected abstract Container createNewContainer(); protected abstract Container openContainerFromFile(); protected abstract Container openContainerFromStream(); protected void addDataFilesToContainer(Container container) { for (ContainerDataFile file : dataFiles) { if (file.isStream) { container.addDataFile(file.inputStream, file.filePath, file.mimeType); } else if (file.isDataFile()) { container.addDataFile(file.dataFile); } else { container.addDataFile(file.filePath, file.mimeType); } } } protected boolean shouldOpenContainerFromFile() { return StringUtils.isNotBlank(containerFilePath); } protected boolean shouldOpenContainerFromStream() { return containerInputStream != null; } public abstract ContainerBuilder usingTempDirectory(String temporaryDirectoryPath); private static boolean isCustomContainerType(String containerType) { return containerImplementations.containsKey(containerType); } private class ContainerDataFile { String filePath; String mimeType; InputStream inputStream; DataFile dataFile; boolean isStream; public ContainerDataFile(String filePath, String mimeType) { this.filePath = filePath; this.mimeType = mimeType; isStream = false; validateDataFile(); } public ContainerDataFile(InputStream inputStream, String filePath, String mimeType) { this.filePath = filePath; this.mimeType = mimeType; this.inputStream = inputStream; isStream = true; validateDataFile(); } public ContainerDataFile(DataFile dataFile) { this.dataFile = dataFile; isStream = false; } public boolean isDataFile() { return dataFile != null; } private void validateDataFile() { if(StringUtils.isBlank(filePath)) { throw new InvalidDataFileException("File name/path cannot be empty"); } if(StringUtils.isBlank(mimeType)) { throw new InvalidDataFileException("Mime type cannot be empty"); } } } }