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

global.namespace.archive.io.delta.Delta Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2013-2018 Schlichtherle IT Services.
 * All rights reserved. Use is subject to license terms.
 */
package global.namespace.archive.io.delta;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import global.namespace.archive.io.api.ArchiveFileInput;
import global.namespace.archive.io.api.ArchiveFileOutput;
import global.namespace.archive.io.delta.dto.DeltaDTO;
import global.namespace.archive.io.delta.dto.EntryNameAndDigestValueDTO;
import global.namespace.archive.io.delta.dto.EntryNameAndTwoDigestValuesDTO;
import global.namespace.archive.io.delta.model.DeltaModel;
import global.namespace.archive.io.delta.model.EntryNameAndDigestValue;
import global.namespace.archive.io.delta.model.EntryNameAndTwoDigestValues;
import global.namespace.fun.io.api.Codec;
import global.namespace.fun.io.api.Sink;
import global.namespace.fun.io.api.Source;
import global.namespace.fun.io.jackson.Jackson;

import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

import static java.util.Collections.emptyList;

/**
 * Diffs and patches archive files.
 *
 * @author Christian Schlichtherle
 */
public class Delta {

    private Delta() { }

    private static final String META_INF_DELTA_JSON = "META-INF/delta.json";

    /**
     * Returns a builder for comparing a first archive file to a second archive file and generating a delta archive
     * file.
     */
    public static ArchiveFileDiffBuilder diff() { return new ArchiveFileDiffBuilder(); }

    /** Returns a builder for patching the first archive file to a second archive file using a delta archive file. */
    public static ArchiveFilePatchBuilder patch() { return new ArchiveFilePatchBuilder(); }

    static  void encodeModel(ArchiveFileOutput output, DeltaModel model) throws Exception {
        encodeModel(output.sink(META_INF_DELTA_JSON), model);
    }

    static  DeltaModel decodeModel(ArchiveFileInput input) throws Exception {
        return decodeModel(input.source(META_INF_DELTA_JSON).orElseThrow(() ->
                new InvalidDeltaArchiveFileException(new MissingArchiveEntryException(META_INF_DELTA_JSON))));
    }

    /** Encodes the given delta model to the given sink. */
    static void encodeModel(Sink sink, DeltaModel model) throws Exception { encodeDTO(sink, marshal(model)); }

    /** Decodes a delta model from the given source. */
    static DeltaModel decodeModel(Source source) throws Exception { return unmarshal(decodeDTO(source)); }

    private static void encodeDTO(Sink sink, DeltaDTO dto) throws Exception { jsonCodec().encoder(sink).encode(dto); }

    private static DeltaDTO decodeDTO(Source source) throws Exception {
        return jsonCodec().decoder(source).decode(DeltaDTO.class);
    }

    private static Codec jsonCodec() {
        final ObjectMapper mapper = new ObjectMapper();
        mapper.setSerializationInclusion(JsonInclude.Include.NON_DEFAULT);
        return Jackson.jsonCodec(mapper);
    }

    private static DeltaDTO marshal(final DeltaModel v) {
        if (null == v) {
            return null;
        } else {
            final DeltaDTO dto = new DeltaDTO();
            dto.algorithm = v.digestAlgorithmName();
            dto.numBytes = v.digestByteLength().orElse(0);
            dto.changed = marshal2(v.changedEntries());
            dto.unchanged = marshal(v.unchangedEntries());
            dto.added = marshal(v.addedEntries());
            dto.removed = marshal(v.removedEntries());
            return dto;
        }
    }

    private static DeltaModel unmarshal(final DeltaDTO v) throws Exception {
        if (null == v) {
            return null;
        } else {
            return DeltaModel
                    .builder()
                    .messageDigest(MessageDigest.getInstance(v.algorithm))
                    .changedEntries(unmarshal2(v.changed))
                    .unchangedEntries(unmarshal(v.unchanged))
                    .addedEntries(unmarshal(v.added))
                    .removedEntries(unmarshal(v.removed))
                    .build();
        }
    }

    private static EntryNameAndTwoDigestValuesDTO[] marshal2(final Collection v) {
        if (null == v || v.isEmpty()) {
            return null;
        } else {
            return v.stream().map(entry -> {
                final EntryNameAndTwoDigestValuesDTO dto = new EntryNameAndTwoDigestValuesDTO();
                dto.name = entry.entryName();
                dto.first = entry.firstDigestValue();
                dto.second = entry.secondDigestValue();
                return dto;
            }).toArray(EntryNameAndTwoDigestValuesDTO[]::new);
        }
    }

    private static List unmarshal2(EntryNameAndTwoDigestValuesDTO[] v) {
        return null == v ? emptyList() : Arrays.stream(v)
                .map(dto -> new EntryNameAndTwoDigestValues(dto.name, dto.first, dto.second))
                .collect(Collectors.toList());
    }

    private static EntryNameAndDigestValueDTO[] marshal(final Collection v) {
        if (null == v || v.isEmpty()) {
            return null;
        } else {
            return v.stream().map(entry -> {
                final EntryNameAndDigestValueDTO dto = new EntryNameAndDigestValueDTO();
                dto.name = entry.entryName();
                dto.digest = entry.digestValue();
                return dto;
            }).toArray(EntryNameAndDigestValueDTO[]::new);
        }
    }

    private static List unmarshal(EntryNameAndDigestValueDTO[] v) {
        return null == v ? emptyList() : Arrays.stream(v)
                .map(dto -> new EntryNameAndDigestValue(dto.name, dto.digest))
                .collect(Collectors.toList());
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy