package org.tmatesoft.svn.core.internal.wc2.ng; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.internal.util.SVNDate; import org.tmatesoft.svn.core.internal.wc.*; import org.tmatesoft.svn.core.internal.wc.admin.SVNTranslator; import org.tmatesoft.svn.core.internal.wc17.SVNStatusEditor17; import org.tmatesoft.svn.core.internal.wc17.SVNWCContext; import org.tmatesoft.svn.core.internal.wc17.SVNWCContext.PristineContentsInfo; import org.tmatesoft.svn.core.internal.wc17.db.ISVNWCDb; import org.tmatesoft.svn.core.internal.wc17.db.Structure; import org.tmatesoft.svn.core.internal.wc17.db.StructureFields; import org.tmatesoft.svn.core.internal.wc17.db.StructureFields.ExternalNodeInfo; import org.tmatesoft.svn.core.internal.wc17.db.StructureFields.NodeInfo; import org.tmatesoft.svn.core.internal.wc17.db.StructureFields.NodeOriginInfo; import org.tmatesoft.svn.core.internal.wc17.db.SvnWcDbExternals; import org.tmatesoft.svn.core.wc.SVNEventAction; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc.SVNStatusType; import org.tmatesoft.svn.core.wc2.SvnExport; import org.tmatesoft.svn.core.wc2.SvnStatus; import org.tmatesoft.svn.util.SVNLogType; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.List; import java.util.Map; public class SvnNgExport extends SvnNgOperationRunner<Long, SvnExport> { @Override protected Long run(SVNWCContext context) throws SVNException { SVNRevision revision = getOperation().getRevision(); File to = getOperation().getFirstTarget().getFile(); File from = getOperation().getSource().getFile(); String eolStyle = getOperation().getEolStyle(); boolean ignoreKeywords = !getOperation().isExpandKeywords(); boolean force = getOperation().isForce(); SVNDepth depth = getOperation().getDepth(); if (revision == SVNRevision.UNDEFINED) { revision = SVNRevision.WORKING; } if (SVNFileType.getType(from) == SVNFileType.FILE) { if (SVNFileType.getType(to) == SVNFileType.DIRECTORY) { to = new File(to, from.getName()); } } copyVersionedDir(from, to, revision, eolStyle, ignoreKeywords, force, depth); handleEvent(SVNEventFactory.createSVNEvent(to, SVNNodeKind.NONE, null, -1, SVNEventAction.UPDATE_COMPLETED, null, null, null)); return Long.valueOf(revision.getNumber()); } private void copyVersionedDir(File from, File to, SVNRevision revision, String eolStyle, boolean ignoreKeywords, boolean force, SVNDepth depth) throws SVNException { if (revision != SVNRevision.WORKING) { Structure<NodeOriginInfo> originInfo = getWcContext().getNodeOrigin(from, false, NodeOriginInfo.isCopy, NodeOriginInfo.reposRelpath); if (originInfo.is(NodeOriginInfo.isCopy) && !originInfo.hasValue(NodeOriginInfo.reposRelpath)) { return; } originInfo.release(); } else { boolean isDeleted = getWcContext().isNodeStatusDeleted(from); if (isDeleted) { return; } } SVNNodeKind fromKind = getWcContext().readKind(from, false); if (fromKind == SVNNodeKind.DIR) { if (to.exists() && !force) { SVNErrorMessage error = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, "Destination directory exists, and will not be overwritten unless forced"); SVNErrorManager.error(error, SVNLogType.WC); } SVNFileUtil.ensureDirectoryExists(to); List<File> children = getWcContext().getNodeChildren(from, false); for (File child : children) { checkCancelled(); File targetPath = new File(to, child.getName()); SVNNodeKind childKind = getWcContext().readKind(child, false); if (childKind == SVNNodeKind.DIR && depth.compareTo(SVNDepth.IMMEDIATES) >= 0) { handleEvent(SVNEventFactory.createSVNEvent(targetPath, SVNNodeKind.NONE, null, -1, SVNEventAction.UPDATE_ADD, null, null, null)); if (depth == SVNDepth.INFINITY) { copyVersionedDir(child, targetPath, revision, eolStyle, ignoreKeywords, force, depth); } else { SVNFileUtil.ensureDirectoryExists(targetPath); } } else if (childKind == SVNNodeKind.FILE && depth.compareTo(SVNDepth.FILES) >= 0) { ISVNWCDb.SVNWCDbKind externalKind = null; try { Structure<ExternalNodeInfo> info = SvnWcDbExternals.readExternal(getWcContext(), child, child, ExternalNodeInfo.kind); externalKind = info.<ISVNWCDb.SVNWCDbKind>get(ExternalNodeInfo.kind); info.release(); } catch (SVNException e) { if (e.getErrorMessage().getErrorCode() != SVNErrorCode.WC_PATH_NOT_FOUND) { throw e; } externalKind = null; } if (externalKind != ISVNWCDb.SVNWCDbKind.File) { copyVersionedFile(child, targetPath, revision, eolStyle, ignoreKeywords); } } } SVNDepth nodeDepth = getWcContext().getNodeDepth(from); if (!getOperation().isIgnoreExternals() && depth == SVNDepth.INFINITY && nodeDepth == SVNDepth.INFINITY) { String externalProperty = getWcContext().getProperty(from, SVNProperty.EXTERNALS); if (externalProperty != null) { SVNExternal[] externals = SVNExternal.parseExternals(from, externalProperty); for (int i = 0; i < externals.length; i++) { File extFrom = new File(from, externals[i].getPath()); File extTo = new File(to, externals[i].getPath()); if (extTo.getParentFile() != null) { SVNFileUtil.ensureDirectoryExists(extTo.getParentFile()); } copyVersionedDir(extFrom, extTo, revision, eolStyle, ignoreKeywords, force, SVNDepth.INFINITY); } } } } else if (fromKind == SVNNodeKind.FILE) { SVNFileType toType = SVNFileType.getType(to); if ((toType == SVNFileType.FILE || toType == SVNFileType.UNKNOWN) && !force) { SVNErrorMessage error = SVNErrorMessage.create(SVNErrorCode.ILLEGAL_TARGET, "Destination file ''{0}'' exists, and will not be overwritten unless forced", to); SVNErrorManager.error(error, SVNLogType.WC); } else if (toType == SVNFileType.DIRECTORY) { SVNErrorMessage error = SVNErrorMessage.create(SVNErrorCode.ILLEGAL_TARGET, "Destination ''{0}'' exists. Cannot overwrite directory with non-directory", to); SVNErrorManager.error(error, SVNLogType.WC); } copyVersionedFile(from, to, revision, eolStyle, ignoreKeywords); } } private void copyVersionedFile(File from, File to, SVNRevision revision, String eolStyle, boolean ignoreKeywords) throws SVNException { boolean isDeleted = getWcContext().isNodeStatusDeleted(from); if (revision == SVNRevision.WORKING && isDeleted) { return; } File source = null; SVNProperties properties = null; boolean modified = false; if (revision != SVNRevision.WORKING) { PristineContentsInfo pristine = getWcContext().getPristineContents(from, false, true); if (pristine != null) { source = pristine.path; } if (source == null) { return; } properties = getWcContext().getPristineProps(from); } else { properties = getWcContext().getDb().readProperties(from); SvnStatus fromStatus = SVNStatusEditor17.internalStatus(getWcContext(), from); modified = fromStatus.getTextStatus() != SVNStatusType.STATUS_NORMAL; source = from; } long timestamp; boolean special = properties.getStringValue(SVNProperty.SPECIAL) != null; boolean executable = properties.getStringValue(SVNProperty.EXECUTABLE) != null; String keywords = properties.getStringValue(SVNProperty.KEYWORDS); String charsetProp = properties.getStringValue(SVNProperty.CHARSET); String mimeType = properties.getStringValue(SVNProperty.MIME_TYPE); String charset = SVNTranslator.getCharset(charsetProp, mimeType, from, getOperation().getOptions()); if (special && SVNFileUtil.symlinksSupported()) { String linkTarget = SVNFileUtil.getSymlinkName(from); SVNFileUtil.createSymlink(to, linkTarget); return; } byte[] eols = eolStyle != null ? SVNTranslator.getEOL(eolStyle, getOperation().getOptions()) : null; if (eols == null) { eolStyle = properties.getStringValue(SVNProperty.EOL_STYLE); eols = SVNTranslator.getEOL(eolStyle, getOperation().getOptions()); } SVNDate committedDate; if (modified) { timestamp = SVNFileUtil.getFileLastModified(from); committedDate = new SVNDate(timestamp, 0); } else { Structure<NodeInfo> nodeInfo = getWcContext().getDb().readInfo(from, NodeInfo.changedDate); committedDate = nodeInfo.<SVNDate>get(NodeInfo.changedDate); timestamp = committedDate.getTime(); nodeInfo.release(); } Map<String, byte[]> keywordsMap = null; if (keywords != null) { Structure<NodeInfo> nodeInfo = getWcContext().getDb().readInfo(from, NodeInfo.changedAuthor, NodeInfo.changedRev, NodeInfo.reposRootUrl); String rev = Long.toString(nodeInfo.lng(NodeInfo.changedRev)); String author = nodeInfo.get(NodeInfo.changedAuthor); SVNURL reposRootUrl = nodeInfo.get(NodeInfo.reposRootUrl); nodeInfo.release(); if (modified) { author = "(local)"; rev += "M"; } SVNURL nodeUrl = getWcContext().getNodeUrl(from); keywordsMap = SVNTranslator.computeKeywords(keywords, nodeUrl.toString(), reposRootUrl == null ? null : reposRootUrl.toString(), author, SVNDate.formatDate(committedDate), rev, getOperation().getOptions()); } File tmpFile = SVNFileUtil.createUniqueFile(to.getParentFile(), "svnkit", ".tmp", false); try { OutputStream os = null; InputStream is = null; try { is = SVNFileUtil.openFileForReading(source); os = SVNFileUtil.openFileForWriting(tmpFile); if (eols != null || keywordsMap != null) { os = SVNTranslator.getTranslatingOutputStream(os, charset, eols, false, keywordsMap, !ignoreKeywords); } SVNTranslator.copy(is, os); } catch (IOException e) { SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e), SVNLogType.WC); } finally { SVNFileUtil.closeFile(is); SVNFileUtil.closeFile(os); } SVNFileUtil.rename(tmpFile, to); } finally { SVNFileUtil.deleteFile(tmpFile); } if (executable) { SVNFileUtil.setExecutable(to, true); } if (!special && timestamp > 0) { SVNFileUtil.setLastModified(to, timestamp); } handleEvent(SVNEventFactory.createSVNEvent(to, SVNNodeKind.NONE, null, -1, SVNEventAction.UPDATE_ADD, null, null, null)); } }