/* * ==================================================================== * Copyright (c) 2004-2010 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.wc17; import java.io.File; import java.io.OutputStream; import java.util.LinkedList; import org.tmatesoft.svn.core.SVNCommitInfo; 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.SVNPropertyValue; import org.tmatesoft.svn.core.internal.wc.ISVNUpdateEditor; import org.tmatesoft.svn.core.internal.wc.SVNErrorManager; import org.tmatesoft.svn.core.internal.wc.SVNFileUtil; import org.tmatesoft.svn.core.internal.wc17.db.ISVNWCDb.SVNWCDbKind; import org.tmatesoft.svn.core.internal.wc17.db.ISVNWCDb.SVNWCDbStatus; import org.tmatesoft.svn.core.internal.wc17.db.ISVNWCDb.WCDbBaseInfo; import org.tmatesoft.svn.core.internal.wc17.db.ISVNWCDb.WCDbBaseInfo.BaseInfoField; import org.tmatesoft.svn.core.internal.wc17.db.ISVNWCDb.WCDbInfo; import org.tmatesoft.svn.core.internal.wc17.db.ISVNWCDb.WCDbInfo.InfoField; import org.tmatesoft.svn.core.io.ISVNEditor; import org.tmatesoft.svn.core.io.diff.SVNDiffWindow; import org.tmatesoft.svn.util.SVNLogType; /** * @version 1.3 * @author TMate Software Ltd. */ public class SVNAmbientDepthFilterEditor17 implements ISVNUpdateEditor { private ISVNEditor myDelegate; private SVNWCContext myWcContext; private File myAnchor; private String myTarget; private boolean myReadBase; private LinkedList<DirBaton> myDirs; private DirBaton myCurrentDirBaton; private FileBaton myCurrentFileBaton; public SVNAmbientDepthFilterEditor17(ISVNUpdateEditor editor, SVNWCContext wcContext, File anchor, String target, boolean readBase) { myDelegate = editor; myWcContext = wcContext; myAnchor = anchor; myTarget = target; myReadBase = readBase; myDirs = new LinkedList<DirBaton>(); } public static ISVNEditor wrap(SVNWCContext wcContext, File anchor, String target, ISVNUpdateEditor editor, boolean depthIsSticky) { if (!depthIsSticky) { return new SVNAmbientDepthFilterEditor17(editor, wcContext, anchor, target, true); } return editor; } public void applyTextDelta(String path, String baseChecksum) throws SVNException { if (myCurrentFileBaton.myIsAmbientlyExcluded) { return; } myDelegate.applyTextDelta(path, baseChecksum); } public OutputStream textDeltaChunk(String path, SVNDiffWindow diffWindow) throws SVNException { if (myCurrentFileBaton.myIsAmbientlyExcluded) { return SVNFileUtil.DUMMY_OUT; } return myDelegate.textDeltaChunk(path, diffWindow); } public void textDeltaEnd(String path) throws SVNException { if (myCurrentFileBaton.myIsAmbientlyExcluded) { return; } myDelegate.textDeltaEnd(path); } public void targetRevision(long revision) throws SVNException { myDelegate.targetRevision(revision); } public void openRoot(long revision) throws SVNException { myCurrentDirBaton = makeDirBaton(null, null); if (myCurrentDirBaton.myIsAmbientlyExcluded) { return; } if (myTarget == null || "".equals(myTarget)) { AmbientReadInfo aInfo = ambientReadInfo(myAnchor, myReadBase); if (aInfo.kind != SVNWCDbKind.Unknown && !aInfo.hidden) { myCurrentDirBaton.myAmbientDepth = aInfo.depth; } } myDelegate.openRoot(revision); } public void deleteEntry(String path, long revision) throws SVNException { if (myCurrentDirBaton.myIsAmbientlyExcluded) { return; } if (myCurrentDirBaton.myAmbientDepth.compareTo(SVNDepth.IMMEDIATES) < 0) { File abspath = SVNFileUtil.createFilePath(myAnchor, SVNWCUtils.getPathAsChild(myAnchor, SVNFileUtil.createFilePath(path))); AmbientReadInfo aInfo = ambientReadInfo(abspath, myReadBase); if (aInfo.kind == SVNWCDbKind.Unknown || aInfo.hidden) { return; } } myDelegate.deleteEntry(path, revision); } public void absentDir(String path) throws SVNException { if (myCurrentDirBaton.myIsAmbientlyExcluded) { return; } myDelegate.absentDir(path); } public void absentFile(String path) throws SVNException { if (myCurrentDirBaton.myIsAmbientlyExcluded) { return; } myDelegate.absentFile(path); } public void addDir(String path, String copyFromPath, long copyFromRevision) throws SVNException { DirBaton parentBaton = myCurrentDirBaton; myCurrentDirBaton = makeDirBaton(path, parentBaton); if (myCurrentDirBaton.myIsAmbientlyExcluded) { return; } if (path.equals(myTarget)) { myCurrentDirBaton.myAmbientDepth = SVNDepth.INFINITY; } else if (parentBaton.myAmbientDepth == SVNDepth.IMMEDIATES) { myCurrentDirBaton.myAmbientDepth = SVNDepth.EMPTY; } else { myCurrentDirBaton.myAmbientDepth = SVNDepth.INFINITY; } myDelegate.addDir(path, copyFromPath, copyFromRevision); } public void openDir(String path, long revision) throws SVNException { DirBaton parentBaton = myCurrentDirBaton; myCurrentDirBaton = makeDirBaton(path, parentBaton); if (myCurrentDirBaton.myIsAmbientlyExcluded) { return; } myDelegate.openDir(path, revision); File abspath = SVNFileUtil.createFilePath(myAnchor, SVNWCUtils.skipAncestor(myAnchor, SVNFileUtil.createFilePath(path))); AmbientReadInfo aInfo = ambientReadInfo(abspath, myReadBase); if (aInfo.kind != SVNWCDbKind.Unknown && !aInfo.hidden) { myCurrentDirBaton.myAmbientDepth = aInfo.depth; } } public void changeDirProperty(String name, SVNPropertyValue value) throws SVNException { if (myCurrentDirBaton.myIsAmbientlyExcluded) { return; } myDelegate.changeDirProperty(name, value); } public void closeDir() throws SVNException { DirBaton closedDir = myDirs.removeLast(); if (myDirs.isEmpty()) { myCurrentDirBaton = null; } else { myCurrentDirBaton = myDirs.getLast(); } if (closedDir.myIsAmbientlyExcluded) { return; } myDelegate.closeDir(); } public void addFile(String path, String copyFromPath, long copyFromRevision) throws SVNException { myCurrentFileBaton = makeFileBaton(myCurrentDirBaton, path); if (myCurrentFileBaton.myIsAmbientlyExcluded) { return; } myDelegate.addFile(path, copyFromPath, copyFromRevision); } public void openFile(String path, long revision) throws SVNException { myCurrentFileBaton = makeFileBaton(myCurrentDirBaton, path); if (myCurrentFileBaton.myIsAmbientlyExcluded) { return; } myDelegate.openFile(path, revision); } public void changeFileProperty(String path, String propertyName, SVNPropertyValue propertyValue) throws SVNException { if (myCurrentFileBaton.myIsAmbientlyExcluded) { return; } myDelegate.changeFileProperty(path, propertyName, propertyValue); } public void closeFile(String path, String textChecksum) throws SVNException { if (myCurrentFileBaton.myIsAmbientlyExcluded) { return; } myDelegate.closeFile(path, textChecksum); } public SVNCommitInfo closeEdit() throws SVNException { return myDelegate.closeEdit(); } public void abortEdit() throws SVNException { } private DirBaton makeDirBaton(String path, DirBaton parentBaton) throws SVNException { if (parentBaton != null && path == null) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNKNOWN, "aborting in SVNAmbientDepthFilterEditor17.makeDirBation(): parentBaton != null" + " while path == null"); SVNErrorManager.error(err, SVNLogType.DEFAULT); } if (parentBaton != null && parentBaton.myIsAmbientlyExcluded) { myDirs.addLast(parentBaton); return parentBaton; } DirBaton dirBaton = new DirBaton(); myDirs.addLast(dirBaton); if (parentBaton != null && parentBaton.myAmbientDepth != SVNDepth.UNKNOWN) { File abspath = SVNFileUtil.createFilePath(myAnchor, SVNWCUtils.getPathAsChild(myAnchor, SVNFileUtil.createFilePath(path))); AmbientReadInfo aInfo = ambientReadInfo(abspath, myReadBase); SVNWCDbStatus status = aInfo.status; SVNWCDbKind kind = aInfo.kind; boolean exclude; boolean exists = kind != SVNWCDbKind.Unknown; if (parentBaton.myAmbientDepth == SVNDepth.EMPTY || parentBaton.myAmbientDepth == SVNDepth.FILES) { exclude = !exists; } else { exclude = exists && (status == SVNWCDbStatus.Excluded); } if (exclude) { dirBaton.myIsAmbientlyExcluded = true; return dirBaton; } } dirBaton.myAmbientDepth = SVNDepth.UNKNOWN; return dirBaton; } private FileBaton makeFileBaton(DirBaton parentBaton, String path) throws SVNException { if (path == null) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNKNOWN, "aborting in SVNAmbientDepthFilterEditor.makeFileBation(): path == null"); SVNErrorManager.error(err, SVNLogType.DEFAULT); } FileBaton fileBaton = new FileBaton(); if (parentBaton.myIsAmbientlyExcluded) { fileBaton.myIsAmbientlyExcluded = true; return fileBaton; } File abspath = SVNFileUtil.createFilePath(myAnchor, SVNWCUtils.getPathAsChild(myAnchor, SVNFileUtil.createFilePath(path))); AmbientReadInfo aInfo = ambientReadInfo(abspath, myReadBase); if (parentBaton.myAmbientDepth == SVNDepth.EMPTY) { if (aInfo.hidden || aInfo.kind == SVNWCDbKind.Unknown) { fileBaton.myIsAmbientlyExcluded = true; } } if (parentBaton.myAmbientDepth != SVNDepth.UNKNOWN && aInfo.status == SVNWCDbStatus.Excluded) { fileBaton.myIsAmbientlyExcluded = true; } return fileBaton; } private class AmbientReadInfo { public boolean hidden; public SVNWCDbStatus status; public SVNWCDbKind kind; public SVNDepth depth; } private AmbientReadInfo ambientReadInfo(File localAbspath, boolean readBase) throws SVNException { final AmbientReadInfo info = new AmbientReadInfo(); try { if (readBase) { WCDbBaseInfo baseInfo = myWcContext.getDb().getBaseInfo(localAbspath, BaseInfoField.status, BaseInfoField.kind, BaseInfoField.depth); info.status = baseInfo.status; info.kind = baseInfo.kind; info.depth = baseInfo.depth; } else { WCDbInfo readInfo = myWcContext.getDb().readInfo(localAbspath, InfoField.status, InfoField.kind, InfoField.depth); info.status = readInfo.status; info.kind = readInfo.kind; info.depth = readInfo.depth; } } catch (SVNException e) { if (e.getErrorMessage().getErrorCode() == SVNErrorCode.WC_PATH_NOT_FOUND) { info.status = SVNWCDbStatus.Normal; info.kind = SVNWCDbKind.Unknown; info.depth = SVNDepth.UNKNOWN; } else { throw e; } } info.hidden = false; switch (info.status) { case NotPresent: case ServerExcluded: case Excluded: info.hidden = true; break; default: break; } return info; } private class DirBaton { boolean myIsAmbientlyExcluded; SVNDepth myAmbientDepth; } private class FileBaton { boolean myIsAmbientlyExcluded; } public long getTargetRevision() { if (myDelegate instanceof ISVNUpdateEditor) { return ((ISVNUpdateEditor) myDelegate).getTargetRevision(); } return SVNWCContext.INVALID_REVNUM; } }