Package org.tmatesoft.svn.core.internal.wc

Source Code of org.tmatesoft.svn.core.internal.wc.SVNExtendedMergeDriver$MergeInfoFetcherExt

/*
* ====================================================================
* Copyright (c) 2004-2009 TMate Software Ltd.  All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution.  The terms
* are also available at http://svnkit.com/license.html
* If newer versions of this license are posted there, you may use a
* newer version instead, at your option.
* ====================================================================
*/
package org.tmatesoft.svn.core.internal.wc;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNMergeInfoInheritance;
import org.tmatesoft.svn.core.SVNMergeRange;
import org.tmatesoft.svn.core.SVNMergeRangeList;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.util.SVNHashSet;
import org.tmatesoft.svn.core.internal.util.SVNMergeInfoUtil;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.internal.wc.admin.ISVNEntryHandler;
import org.tmatesoft.svn.core.internal.wc.admin.SVNAdminArea;
import org.tmatesoft.svn.core.internal.wc.admin.SVNEntry;
import org.tmatesoft.svn.core.internal.wc.admin.SVNWCAccess;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.wc.ISVNOptions;
import org.tmatesoft.svn.core.wc.ISVNRepositoryPool;
import org.tmatesoft.svn.core.wc.SVNCopySource;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.util.SVNDebugLog;
import org.tmatesoft.svn.util.SVNLogType;

/**
* @author TMate Software Ltd.
* @version 1.3
* @since 1.2
*/
public abstract class SVNExtendedMergeDriver extends SVNMergeDriver {

    private ISVNExtendedMergeCallback myExtendedMergeCallback;
    private SVNCopyDriver myCopyDriver;
    private SVNURL myPrimaryURL;
    private SVNURL mySecondURL;
    private long myRevision1;
    private long myRevision2;
    private File myTempDirectory;
    private File myReportFile;
    private Set myPendingTargets;
    private SVNMergeRangeList myCurrentRemainingRanges;
    private Boolean myRecordMergeInfo;

    private SVNRepository myRepository;

    public SVNExtendedMergeDriver(ISVNAuthenticationManager authManager, ISVNOptions options) {
        super(authManager, options);
    }

    protected SVNExtendedMergeDriver(ISVNRepositoryPool repositoryPool, ISVNOptions options) {
        super(repositoryPool, options);
    }

    public ISVNExtendedMergeCallback getExtendedMergeCallback() {
        return myExtendedMergeCallback;
    }

    public void setExtendedMergeCallback(ISVNExtendedMergeCallback extendedMergeCallback) {
        myExtendedMergeCallback = extendedMergeCallback;
    }

    private Set getPendingFiles() {
        if (myPendingTargets == null) {
            myPendingTargets = new SVNHashSet();
        }
        return myPendingTargets;
    }

    private File getReportFile() throws SVNException {
        if (myReportFile == null) {
            myReportFile = SVNFileUtil.createUniqueFile(getTempDirectory(), "svnkit", ".extmerge", false);
        }
        return myReportFile;
    }

    private void deleteReportFile() throws SVNException {
        if (myReportFile != null) {
            try {
                SVNFileUtil.deleteFile(myReportFile);
            } finally {
                myReportFile = null;
            }
        }
    }

    protected File getTempDirectory() {
        return myTempDirectory;
    }

    protected void setTempDirectory(File tempDirectory) {
        myTempDirectory = tempDirectory;
    }

    private boolean skipExtendedMerge() {
        return myExtendedMergeCallback == null;
    }

    private SVNRepository getRepository(SVNURL url) throws SVNException {
        if (url == null) {
            return null;
        }
        if (myRepository == null) {
            myRepository = createRepository(url, null, null, false);
        } else {
            myRepository.setLocation(url, false);
        }
        return myRepository;
    }

    protected SVNCopyDriver getCopyDriver() {
        if (myCopyDriver == null) {
            myCopyDriver = new SVNCopyDriver(getRepositoryPool(), getOptions());
            myCopyDriver.setWCAccess(myWCAccess);
        }
        return myCopyDriver;
    }

    protected void copy(SVNCopySource copySource, File dst, boolean save) throws SVNException {
        if (copySource == null || dst == null) {
            return;
        }
        SVNEntry entry = myWCAccess.getEntry(dst, false);
        if (entry != null) {
            doVirtualCopy(entry, copySource, save);
        } else {
            getCopyDriver().setupCopy(new SVNCopySource[]{copySource}, new SVNPath(dst.getAbsolutePath()), false, true, null, null, null, null, null);
        }
    }

    protected void doVirtualCopy(SVNEntry dstEntry, SVNCopySource copySource, boolean save) throws SVNException {
        dstEntry.setCopyFromURL(copySource.getURL().toString());
        long cfRevision = copySource.getRevision().getNumber();
        dstEntry.setCopyFromRevision(cfRevision);
        if (save) {
            SVNAdminArea dir = dstEntry.getAdminArea();
            dir.saveEntries(false);
        }
    }

    protected long getRevision(SVNCopySource copySource) throws SVNException {
        return getRevisionNumber(copySource.getRevision(), getRepository(copySource.getURL()), copySource.getFile());
    }

    protected SVNRemoteDiffEditor getMergeReportEditor(long defaultStart, long revision, SVNAdminArea adminArea, SVNDepth depth, SVNMergeCallback mergeCallback, SVNRemoteDiffEditor editor) throws SVNException {
        if (skipExtendedMerge()) {
            return super.getMergeReportEditor(defaultStart, revision, adminArea, depth, mergeCallback, editor);
        }
        if (editor == null) {
            editor = new SVNExtendedMergeEditor(this, getExtendedMergeCallback(), adminArea, adminArea.getRoot(),
                    mergeCallback, myPrimaryURL, myRepository2, defaultStart, revision, myIsDryRun, depth, this, this);
        } else {
            editor.reset(defaultStart, revision);
        }
        File tmp = mergeCallback.createTempDirectory();
        setTempDirectory(tmp);
        return editor;
    }

    protected void addMergeSource(String mergeSource, SVNURL[] mergeSources, File target, SVNMergeRangeList remainingRanges, boolean adjustMergeInfo, SVNCopySource targetCopySource) throws SVNException {
        if (getPendingFiles().contains(target)) {
            SVNDebugLog.getDefaultLog().logFine(SVNLogType.WC, "ext merge: skip new additional target " + target.getAbsolutePath());
            return;
        }
        getPendingFiles().add(target);

        SVNURL sourceURL = myPrimaryURL.appendPath(mergeSource, false);       
        mergeSources = getMergeSources(sourceURL, mergeSources);
        SVNURL url1 = mergeSources[0];
        SVNURL url2 = mergeSources[1];

        BufferedWriter writer = createWriter();
        SVNMergeTask mergeTask = new SVNMergeTask(url1, url2, target, remainingRanges, adjustMergeInfo, targetCopySource);
        SVNDebugLog.getDefaultLog().logFine(SVNLogType.WC, "ext merge: " + mergeTask.toString());
        try {
            mergeTask.writeTo(writer);
        } catch (IOException e) {
            SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e), SVNLogType.DEFAULT);
        } finally {
            SVNFileUtil.closeFile(writer);
        }
    }

    protected boolean mergeInfoConflicts(SVNMergeRangeList rangeList, File path) {
        if (rangeList == null) {
            return false;
        }
        SVNMergeRange currentRange = new SVNMergeRange(Math.min(myRevision1, myRevision2), Math.max(myRevision1, myRevision2), false);
        SVNMergeRange[] ranges = rangeList.getRanges();
        for (int i = 0; i < ranges.length; i++) {
            SVNMergeRange range = ranges[i];
            if (currentRange.intersects(range, false)) {
                if (range.contains(currentRange, false)) {
                    continue;
                }
                SVNDebugLog.getDefaultLog().logFine(SVNLogType.WC, "merge ext: merge info conflict found on " + path.getAbsolutePath());
                return true;
            }
        }
        return false;
    }

    protected SVNMergeRangeList calculateRemainingRanges(File file, SVNURL sourceURL, SVNURL[] mergeSources) throws SVNException {
        if (skipExtendedMerge()) {
            return null;
        }

        SVNEntry entry = myWCAccess.getEntry(file, false);
        if (entry == null) {
            return null;
        }

        SVNMergeRangeList remainingRangeList = null;
        if (isHonorMergeInfo()) {
            MergePath mergeTarget = new MergePath();
            Map targetMergeInfo;
            Map implicitMergeInfo;
            SVNRepository repository = getRepository(entry.getSVNURL());
            SVNURL sourceRoot = repository.getRepositoryRoot(true);
            boolean[] indirect = {false};

            Map[] fullMergeInfo = getFullMergeInfo(entry, indirect, SVNMergeInfoInheritance.INHERITED,
                    repository, file, Math.max(myRevision1, myRevision2), Math.min(myRevision1, myRevision2));
            targetMergeInfo = fullMergeInfo[0];
            implicitMergeInfo = fullMergeInfo[1];

            mergeSources = getMergeSources(sourceURL, mergeSources);
            SVNURL url1 = mergeSources[0];
            SVNURL url2 = mergeSources[1];

            calculateRemainingRanges(null, mergeTarget, sourceRoot, url1, myRevision1, url2, myRevision2,
                    targetMergeInfo, implicitMergeInfo, false, entry, repository);
            remainingRangeList = mergeTarget.myRemainingRanges;
        }
        return remainingRangeList;
    }

    private SVNURL[] getMergeSources(SVNURL sourceURL, SVNURL[] mergeSources) throws SVNException {
        if (mergeSources == null) {
            mergeSources = new SVNURL[2];
        }
        if (mergeSources[0] != null && mergeSources[1] != null) {
            return mergeSources;
        }
        SVNURL sourceCopiedFrom = getExtendedMergeCallback().transformLocation(sourceURL, Math.max(myRevision1, myRevision2), Math.min(myRevision1, myRevision2));
        if (sourceCopiedFrom != null) {
            mergeSources[0] = sourceCopiedFrom;
            mergeSources[1] = sourceURL;
        } else {
            mergeSources[0] = sourceURL;
            String relativePath = SVNPathUtil.getRelativePath(myPrimaryURL.getPath(), sourceURL.getPath());           
            mergeSources[1] = mySecondURL.appendPath(relativePath, false);
        }
        return mergeSources;
    }

    protected ISVNEntryHandler getMergeInfoEntryHandler(String mergeSrcPath, SVNURL sourceRootURL, long revision1, long revision2, SVNRepository repository, SVNDepth depth, List childrenWithMergeInfo) {
        if (skipExtendedMerge()) {
            return super.getMergeInfoEntryHandler(mergeSrcPath, sourceRootURL, revision1, revision2, repository, depth, childrenWithMergeInfo);
        }
        return new MergeInfoFetcherExt(mergeSrcPath, sourceRootURL, revision1, revision2, repository, depth, childrenWithMergeInfo);
    }

    protected void doDirectoryMerge(SVNURL url1, long revision1, SVNURL url2, long revision2, SVNEntry parentEntry, SVNAdminArea adminArea, SVNDepth depth) throws SVNException {
        if (skipExtendedMerge()) {
            super.doDirectoryMerge(url1, revision1, url2, revision2, parentEntry, adminArea, depth);
            return;
        }

        myPrimaryURL = revision1 < revision2 ? url1 : url2;
        mySecondURL = revision1 < revision2 ? url2 : url1;
        myRevision1 = revision1;
        myRevision2 = revision2;
        try {
            super.doDirectoryMerge(url1, revision1, url2, revision2, parentEntry, adminArea, depth);
            doAdditionalMerge();
        } catch (Throwable th) {
            SVNErrorMessage error = SVNErrorMessage.create(SVNErrorCode.UNKNOWN,
                    "Error while processing extended merge: ''{0}''", new Object[]{th.getMessage()},
                    SVNErrorMessage.TYPE_ERROR, th);
            SVNErrorManager.error(error, th, SVNLogType.DEFAULT);
        } finally {
            deleteReportFile();
            getPendingFiles().clear();
        }
    }

    protected void doAdditionalMerge() throws SVNException {
        SVNURL oldURL1 = myRepository1.getLocation();
        SVNURL oldURL2 = myRepository2.getLocation();

        BufferedReader reader = createReader();
        try {
            SVNMergeTask nextTask = readTask(reader);
            while (nextTask != null) {
                runMergeTask(nextTask, myRevision1, myRevision2);
                nextTask = readTask(reader);
            }
        } finally {
            SVNFileUtil.closeFile(reader);
            myRepository1.setLocation(oldURL1, false);
            myRepository2.setLocation(oldURL2, false);
            myRepository = null;
        }
    }

    private void runMergeTask(SVNMergeTask mergeTask, long revision1, long revision2) throws SVNException {
        boolean rollback = revision1 > revision2;
        SVNURL mergeSource = mergeTask.getMergeSource();
        SVNURL mergeSource2 = mergeTask.getMergeSource2();
        File mergeTarget = mergeTask.getMergeTarget();
        SVNMergeRangeList remainingRanges = mergeTask.getRemainingRanges();
        boolean updateWCMergeInfo = mergeTask.isUpdateWCMergeInfo();
        SVNCopySource copySource = mergeTask.getTargetCopySource();
        if (copySource != null) {
            copy(copySource, mergeTarget, true);
        }

        SVNURL mergeURL1 = !rollback ? mergeSource : mergeSource2;
        SVNURL mergeURL2 = !rollback ? mergeSource2 : mergeSource;
        myRepository1.setLocation(mergeURL1, false);
        myRepository2.setLocation(mergeURL2, false);
        SVNAdminArea targetArea = retrieve(myWCAccess, mergeTarget.getParentFile());

        myCurrentRemainingRanges = remainingRanges;
        myRecordMergeInfo = Boolean.valueOf(updateWCMergeInfo);
        try {
            doFileMerge(mergeURL1, revision1, mergeURL2, revision2, mergeTarget, targetArea, true);
        } finally {
            myCurrentRemainingRanges = null;
            myRecordMergeInfo = null;
        }
    }

    protected boolean isRecordMergeInfo() {
        boolean defaultOption =  super.isRecordMergeInfo();
        if (skipExtendedMerge()) {
            return defaultOption;
        }
        boolean recordMergeInfo = myRecordMergeInfo == null ? true : myRecordMergeInfo.booleanValue();
        return recordMergeInfo && defaultOption;
    }

    protected Object[] calculateRemainingRangeList(File targetFile, SVNEntry entry, SVNURL sourceRoot, boolean[] indirect,
                                                   SVNURL url1, long revision1, SVNURL url2, long revision2, SVNMergeRange range) throws SVNException {
        if (skipExtendedMerge() || myCurrentRemainingRanges == null) {
            return super.calculateRemainingRangeList(targetFile, entry, sourceRoot, indirect, url1, revision1, url2, revision2, range);
        }
//      targetMergeInfo and implicitMergeInfo should be processed anyway.
        Map targetMergeInfo = null;
        Map implicitMergeInfo = null;
        if (isHonorMergeInfo()) {
            myRepository1.setLocation(entry.getSVNURL(), false);
            Map[] fullMergeInfo = getFullMergeInfo(entry, indirect, SVNMergeInfoInheritance.INHERITED,
                    myRepository1, targetFile, Math.max(revision1, revision2), Math.min(revision1, revision2));
            targetMergeInfo = fullMergeInfo[0];
            implicitMergeInfo = fullMergeInfo[1];
            myRepository1.setLocation(url1, false);
        }
        return new Object[]{myCurrentRemainingRanges, targetMergeInfo, implicitMergeInfo};
    }

    private static SVNAdminArea retrieve(SVNWCAccess access, File target) throws SVNException {
        SVNAdminArea area = access.getAdminArea(target);
        if (area == null) {
            area = access.probeTry(target, true, 0);
        }
        return area;
    }

    protected SVNMergeTask readTask(BufferedReader reader) throws SVNException {
        SVNURL sourceURL = null;
        SVNURL sourceURL2 = null;
        File target = null;
        SVNMergeRangeList remainingRanges = null;
        boolean updateWCMergeInfo = true;
        SVNCopySource copySource = null;

        try {
            String sourceLine = reader.readLine();

            if (sourceLine == null) {
                return null;
            }
            sourceURL = SVNURL.parseURIEncoded(sourceLine);
            sourceURL2 = SVNURL.parseURIEncoded(reader.readLine());
            target = new File(reader.readLine());

            String mergeRangesRepresentation = reader.readLine();
            if (mergeRangesRepresentation != null && mergeRangesRepresentation.length() != 0) {
                SVNMergeRange[] ranges = SVNMergeInfoUtil.parseRevisionList(new StringBuffer(mergeRangesRepresentation), target.getPath());
                remainingRanges = new SVNMergeRangeList(ranges);
            }

            updateWCMergeInfo = Boolean.TRUE.toString().equals(reader.readLine());

            String path = reader.readLine();
            SVNRevision pegRevision = SVNRevision.parse(reader.readLine());
            SVNRevision revision = SVNRevision.parse(reader.readLine());
            reader.readLine();

            if (path.length() == 0) {
                copySource = null;
            } else if (SVNPathUtil.isURL(path)) {
                copySource = new SVNCopySource(pegRevision, revision, SVNURL.parseURIEncoded(path));
            } else {
                copySource = new SVNCopySource(pegRevision, revision, new File(path));
            }
        } catch (IOException e) {
            SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e), SVNLogType.DEFAULT);
        }
        return new SVNMergeTask(sourceURL, sourceURL2, target, remainingRanges, updateWCMergeInfo, copySource);
    }

    private BufferedReader createReader() throws SVNException {
        try {
            return new BufferedReader(new InputStreamReader(SVNFileUtil.openFileForReading(getReportFile()), "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            return new BufferedReader(new InputStreamReader(SVNFileUtil.openFileForReading(getReportFile())));
        }
    }

    private BufferedWriter createWriter() throws SVNException {
        try {
            return new BufferedWriter(new OutputStreamWriter(new SVNCancellableOutputStream(SVNFileUtil.openFileForWriting(getReportFile(), true), getEventDispatcher()), "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            return new BufferedWriter(new OutputStreamWriter(SVNFileUtil.openFileForWriting(getReportFile(), true)));
        }
    }

    private class SVNMergeTask {

        private SVNURL myMergeSource;
        private SVNURL myMergeSource2;
        private File myMergeTarget;
        private SVNMergeRangeList myRemainingRanges;
        private boolean myUpdateWCMergeInfo;
        private SVNCopySource myTargetCopySource;

        protected SVNMergeTask(SVNURL mergeSource, SVNURL mergeSource2, File mergeTarget, SVNMergeRangeList remainingRanges, boolean updateWCMergeInfo, SVNCopySource targetCopySource) {
            myMergeSource = mergeSource;
            myMergeSource2 = mergeSource2;
            myMergeTarget = mergeTarget;
            myRemainingRanges = remainingRanges;
            myUpdateWCMergeInfo = updateWCMergeInfo;
            myTargetCopySource = targetCopySource;
        }

        protected SVNURL getMergeSource() {
            return myMergeSource;
        }

        protected SVNURL getMergeSource2() {
            return myMergeSource2;
        }

        protected File getMergeTarget() {
            return myMergeTarget;
        }

        protected SVNMergeRangeList getRemainingRanges() {
            return myRemainingRanges;
        }

        protected boolean isUpdateWCMergeInfo() {
            return myUpdateWCMergeInfo;
        }

        protected SVNCopySource getTargetCopySource() {
            return myTargetCopySource;
        }

        protected void writeTo(BufferedWriter writer) throws IOException {
            writer.write(getMergeSource().toString());
            writer.newLine();

            writer.write(getMergeSource2().toString());
            writer.newLine();

            writer.write(getMergeTarget().getAbsolutePath());
            writer.newLine();

            if (getRemainingRanges() != null) {
                writer.write(getRemainingRanges().toString());
            }
            writer.newLine();

            writer.write(String.valueOf(isUpdateWCMergeInfo()));
            writer.newLine();

            SVNCopySource source = getTargetCopySource();
            if (source != null) {
                String path = source.getURL() == null ? source.getFile().getAbsolutePath() : source.getURL().toString();
                writer.write(path);
            }
            writer.newLine();
            if (source != null) {
                SVNRevision pegRevision = source.getPegRevision() == null ? SVNRevision.UNDEFINED : source.getPegRevision();
                writer.write(pegRevision.toString());
            }
            writer.newLine();
            if (source != null) {
                SVNRevision revision = source.getRevision() == null ? SVNRevision.UNDEFINED : source.getRevision();
                writer.write(revision.toString());
            }
            writer.newLine();
            writer.newLine();
        }

        public String toString() {
            StringBuffer buffer = new StringBuffer();
            buffer.append("merge task: source from ");
            buffer.append(getMergeSource());
            buffer.append("; source to");
            buffer.append(getMergeSource2());
            buffer.append("; target ");
            buffer.append(getMergeTarget());
            buffer.append("; revision ranges ");
            if (getRemainingRanges() == null) {
                buffer.append("[NULL]");
            } else {
                buffer.append(getRemainingRanges().toString());
            }
            buffer.append("; copy source ");
            if (getTargetCopySource() != null) {
                buffer.append(getTargetCopySource().isURL() ? getTargetCopySource().getURL().toString() : getTargetCopySource().getFile().getAbsolutePath());
                buffer.append("@");
                buffer.append(getTargetCopySource().getPegRevision().toString());
                buffer.append(" revision ");
                buffer.append(getTargetCopySource().getRevision());
            } else {
                buffer.append("[NULL]");
            }
            return buffer.toString();
        }
    }

    private class MergeInfoFetcherExt extends MergeInfoFetcher {

        private MergeInfoFetcherExt(String mergeSrcPath, SVNURL sourceRootURL, long revision1, long revision2, SVNRepository repository, SVNDepth depth, List childrenWithMergeInfo) {
            super(mergeSrcPath, sourceRootURL, revision1, revision2, repository, depth, childrenWithMergeInfo);
        }

        public void handleEntry(File path, SVNEntry entry) throws SVNException {
            SVNURL entryURL = entry.getSVNURL();
            if (entryURL != null && !myTarget.equals(path)) {
                SVNEntry parentEntry = myWCAccess.getVersionedEntry(path.getParentFile(), false);
                SVNURL expectedURL = parentEntry.getSVNURL().appendPath(SVNPathUtil.tail(path.getAbsolutePath().replace(File.separatorChar, '/')), false);
                if (!entryURL.equals(expectedURL)) {
                    SVNErrorMessage error = SVNErrorMessage.create(SVNErrorCode.ILLEGAL_TARGET, "''{0}'' is switched entry which is not allowed for this kind of merge", path);
                    SVNErrorManager.error(error, SVNLogType.WC);
                }
            }
            super.handleEntry(path, entry);
        }
    }
}
TOP

Related Classes of org.tmatesoft.svn.core.internal.wc.SVNExtendedMergeDriver$MergeInfoFetcherExt

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.