package gov.loc.repository.bagit.creator; import java.io.IOException; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Collection; import java.util.Map; import java.util.ResourceBundle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import gov.loc.repository.bagit.annotation.Incubating; import gov.loc.repository.bagit.domain.Bag; import gov.loc.repository.bagit.domain.Manifest; import gov.loc.repository.bagit.domain.Version; import gov.loc.repository.bagit.hash.Hasher; import gov.loc.repository.bagit.hash.SupportedAlgorithm; import gov.loc.repository.bagit.writer.BagitFileWriter; import gov.loc.repository.bagit.writer.ManifestWriter; /** * Responsible for creating a bag in place. */ public final class BagCreator { private static final Logger logger = LoggerFactory.getLogger(BagCreator.class); private static final ResourceBundle messages = ResourceBundle.getBundle("MessageBundle"); private BagCreator(){} @SuppressWarnings("CPD-START") /** * Creates a basic(only required elements) bag in place for version 0.97. * This method moves and creates files, thus if an error is thrown during operation it may leave the filesystem * in an unknown state of transition. Thus this is <b>not thread safe</b> * * @param root the directory that will become the base of the bag and where to start searching for content * @param algorithms an collection of {@link SupportedAlgorithm} implementations * @param includeHidden to include hidden files when generating the bagit files, like the manifests * @throws NoSuchAlgorithmException if {@link MessageDigest} can't find the algorithm * @throws IOException if there is a problem writing or moving file(s) * @return a {@link Bag} object representing the newly created bagit bag */ public static Bag bagInPlace(final Path root, final Collection<SupportedAlgorithm> algorithms, final boolean includeHidden) throws NoSuchAlgorithmException, IOException{ final Bag bag = new Bag(new Version(0, 97)); bag.setRootDir(root); logger.info(messages.getString("creating_bag"), bag.getVersion(), root); final Path dataDir = root.resolve("data"); Files.createDirectory(dataDir); try(final DirectoryStream<Path> directoryStream = Files.newDirectoryStream(root)){ for(final Path path : directoryStream){ if(!path.equals(dataDir) && !Files.isHidden(path) || includeHidden){ Files.move(path, dataDir.resolve(path.getFileName())); } } } logger.info(messages.getString("creating_payload_manifests")); final Map<Manifest, MessageDigest> payloadFilesMap = Hasher.createManifestToMessageDigestMap(algorithms); final CreatePayloadManifestsVistor payloadVisitor = new CreatePayloadManifestsVistor(payloadFilesMap, includeHidden); Files.walkFileTree(dataDir, payloadVisitor); bag.getPayLoadManifests().addAll(payloadFilesMap.keySet()); BagitFileWriter.writeBagitFile(bag.getVersion(), bag.getFileEncoding(), root); ManifestWriter.writePayloadManifests(bag.getPayLoadManifests(), root, root, bag.getFileEncoding()); logger.info(messages.getString("creating_tag_manifests")); final Map<Manifest, MessageDigest> tagFilesMap = Hasher.createManifestToMessageDigestMap(algorithms); final CreateTagManifestsVistor tagVistor = new CreateTagManifestsVistor(tagFilesMap, includeHidden); Files.walkFileTree(root, tagVistor); bag.getTagManifests().addAll(tagFilesMap.keySet()); ManifestWriter.writeTagManifests(bag.getTagManifests(), root, root, bag.getFileEncoding()); return bag; } @SuppressWarnings("CPD-END") /** * Creates a basic(only required elements) .bagit bag in place. * This creates files and directories, thus if an error is thrown during operation it may leave the filesystem * in an unknown state of transition. Thus this is <b>not thread safe</b> * * @param root the directory that will become the base of the bag and where to start searching for content * @param algorithms an collection of {@link SupportedAlgorithm} implementations * @param includeHidden to include hidden files when generating the bagit files, like the manifests * @return a {@link Bag} object representing the newly created bagit bag * @throws NoSuchAlgorithmException if {@link MessageDigest} can't find the algorithm * @throws IOException if there is a problem writing files or .bagit directory */ @Incubating public static Bag createDotBagit(final Path root, final Collection<SupportedAlgorithm> algorithms, final boolean includeHidden) throws NoSuchAlgorithmException, IOException{ final Bag bag = new Bag(new Version(2, 0)); bag.setRootDir(root); logger.info(messages.getString("creating_bag"), bag.getVersion(), root); final Path dotbagitDir = root.resolve(".bagit"); Files.createDirectories(dotbagitDir); logger.info(messages.getString("creating_payload_manifests")); final Map<Manifest, MessageDigest> map = Hasher.createManifestToMessageDigestMap(algorithms); final CreatePayloadManifestsVistor visitor = new CreatePayloadManifestsVistor(map, includeHidden); Files.walkFileTree(root, visitor); bag.getPayLoadManifests().addAll(map.keySet()); BagitFileWriter.writeBagitFile(bag.getVersion(), bag.getFileEncoding(), dotbagitDir); ManifestWriter.writePayloadManifests(bag.getPayLoadManifests(), dotbagitDir, root, bag.getFileEncoding()); logger.info(messages.getString("creating_tag_manifests")); final Map<Manifest, MessageDigest> tagFilesMap = Hasher.createManifestToMessageDigestMap(algorithms); final CreateTagManifestsVistor tagVistor = new CreateTagManifestsVistor(tagFilesMap, includeHidden); Files.walkFileTree(dotbagitDir, tagVistor); bag.getTagManifests().addAll(tagFilesMap.keySet()); ManifestWriter.writeTagManifests(bag.getTagManifests(), dotbagitDir, root, bag.getFileEncoding()); return bag; } }