svnserver.repository.git.GitDeltaConsumer.java Source code

Java tutorial

Introduction

Here is the source code for svnserver.repository.git.GitDeltaConsumer.java

Source

/**
 * This file is part of git-as-svn. It is subject to the license terms
 * in the LICENSE file found in the top-level directory of this distribution
 * and at http://www.gnu.org/licenses/gpl-2.0.html. No part of git-as-svn,
 * including this file, may be copied, modified, propagated, or distributed
 * except according to the terms contained in the LICENSE file.
 */
package svnserver.repository.git;

import com.google.common.io.ByteStreams;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNProperty;
import org.tmatesoft.svn.core.io.diff.SVNDeltaProcessor;
import org.tmatesoft.svn.core.io.diff.SVNDiffWindow;
import svnserver.TemporaryOutputStream;
import svnserver.auth.User;
import svnserver.repository.VcsDeltaConsumer;
import svnserver.repository.git.filter.GitFilter;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;

/**
 * Delta consumer for applying svn diff on git blob.
 *
 * @author a.navrotskiy
 */
public class GitDeltaConsumer implements VcsDeltaConsumer {
    @NotNull
    private static final Logger log = LoggerFactory.getLogger(GitDeltaConsumer.class);
    @NotNull
    private final Map<String, String> props;
    @NotNull
    private final GitWriter writer;
    @NotNull
    private final GitEntry entry;
    @Nullable
    private final User user;
    @Nullable
    private SVNDeltaProcessor window;
    @Nullable
    private final GitObject<ObjectId> originalId;
    @Nullable
    private final String originalMd5;
    @Nullable
    private GitObject<ObjectId> objectId;
    @Nullable
    private final GitFilter oldFilter;
    @Nullable
    private GitFilter newFilter;

    @NotNull
    private TemporaryOutputStream temporaryStream;
    @Nullable
    private String md5;

    public GitDeltaConsumer(@NotNull GitWriter writer, @NotNull GitEntry entry, @Nullable GitFile file,
            @Nullable User user) throws IOException, SVNException {
        this.writer = writer;
        this.entry = entry;
        this.user = user;
        if (file != null) {
            this.originalMd5 = file.getMd5();
            this.originalId = file.getObjectId();
            this.props = new HashMap<>(file.getProperties());
            this.oldFilter = file.getFilter();
        } else {
            this.originalMd5 = null;
            this.originalId = null;
            this.props = new HashMap<>();
            this.oldFilter = null;
        }
        this.newFilter = null;
        this.objectId = originalId;
        this.temporaryStream = new TemporaryOutputStream();
    }

    @NotNull
    @Override
    public Map<String, String> getProperties() {
        return props;
    }

    @Nullable
    public GitObject<ObjectId> getOriginalId() {
        return originalId;
    }

    @Nullable
    public GitObject<ObjectId> getObjectId() throws IOException, SVNException {
        if ((originalId != null) && originalId.equals(objectId) && (newFilter == null)) {
            this.newFilter = oldFilter;
            this.objectId = originalId;
            if (oldFilter == null) {
                throw new IllegalStateException("Original object ID defined, but original Filter is not defined");
            }
            migrateFilter(writer.getRepository().getFilter(
                    props.containsKey(SVNProperty.SPECIAL) ? FileMode.SYMLINK : FileMode.REGULAR_FILE,
                    entry.getRawProperties()));
        }
        return objectId;
    }

    public boolean migrateFilter(@NotNull GitFilter filter) throws IOException, SVNException {
        if (newFilter == null || objectId == null) {
            throw new IllegalStateException("Original object ID defined, but original Filter is not defined");
        }
        final GitObject<ObjectId> beforeId = objectId;
        if (!newFilter.equals(filter)) {
            final Repository repo = writer.getRepository().getRepository();

            try (final TemporaryOutputStream content = new TemporaryOutputStream();
                    final TemporaryOutputStream.Holder holder = content.holder()) {
                try (InputStream inputStream = newFilter.inputStream(objectId);
                        OutputStream outputStream = filter.outputStream(content, user)) {
                    ByteStreams.copy(inputStream, outputStream);
                }
                try (InputStream inputStream = content.toInputStream()) {
                    objectId = new GitObject<>(repo,
                            writer.getInserter().insert(Constants.OBJ_BLOB, content.size(), inputStream));
                    newFilter = filter;
                }
            }
        }
        return !beforeId.equals(objectId);
    }

    @Override
    public void applyTextDelta(String path, @Nullable String baseChecksum) throws SVNException {
        try {
            if ((originalMd5 != null) && (baseChecksum != null)) {
                if (!baseChecksum.equals(originalMd5)) {
                    throw new SVNException(SVNErrorMessage.create(SVNErrorCode.CHECKSUM_MISMATCH));
                }
            }

            if (window != null)
                throw new SVNException(SVNErrorMessage.create(SVNErrorCode.RA_SVN_CMD_ERR));

            newFilter = writer.getRepository().getFilter(
                    props.containsKey(SVNProperty.SPECIAL) ? FileMode.SYMLINK : FileMode.REGULAR_FILE,
                    entry.getRawProperties());
            window = new SVNDeltaProcessor();
            window.applyTextDelta(
                    (oldFilter != null && objectId != null) ? oldFilter.inputStream(objectId)
                            : new ByteArrayInputStream(GitRepository.emptyBytes),
                    newFilter.outputStream(temporaryStream, user), true);
        } catch (IOException e) {
            throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR), e);
        }
    }

    @Override
    public OutputStream textDeltaChunk(String path, SVNDiffWindow diffWindow) throws SVNException {
        if (window == null)
            throw new SVNException(SVNErrorMessage.create(SVNErrorCode.RA_SVN_CMD_ERR));

        return window.textDeltaChunk(diffWindow);
    }

    @Override
    public void textDeltaEnd(String path) throws SVNException {
        try (TemporaryOutputStream.Holder holder = temporaryStream.holder()) {
            if (window == null)
                throw new SVNException(SVNErrorMessage.create(SVNErrorCode.RA_SVN_CMD_ERR));

            final Repository repo = writer.getRepository().getRepository();
            md5 = window.textDeltaEnd();
            try (InputStream stream = temporaryStream.toInputStream()) {
                objectId = new GitObject<>(repo,
                        writer.getInserter().insert(Constants.OBJ_BLOB, temporaryStream.size(), stream));
            }
            log.info("Created blob {} for file: {}", objectId.getObject().getName(), path);
        } catch (IOException e) {
            throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR), e);
        }
    }

    @Override
    public void validateChecksum(@NotNull String md5) throws SVNException {
        if (window != null) {
            if (!md5.equals(this.md5)) {
                throw new SVNException(SVNErrorMessage.create(SVNErrorCode.CHECKSUM_MISMATCH));
            }
        } else if (originalMd5 != null) {
            if (!originalMd5.equals(md5)) {
                throw new SVNException(SVNErrorMessage.create(SVNErrorCode.CHECKSUM_MISMATCH));
            }
        }
    }

    @NotNull
    public String getFilterName() {
        if (newFilter != null)
            return newFilter.getName();
        if (oldFilter != null)
            return oldFilter.getName();
        throw new IllegalStateException();
    }
}