001: /*
002: * ====================================================================
003: * Copyright (c) 2004-2008 TMate Software Ltd. All rights reserved.
004: *
005: * This software is licensed as described in the file COPYING, which
006: * you should have received as part of this distribution. The terms
007: * are also available at http://svnkit.com/license.html
008: * If newer versions of this license are posted there, you may use a
009: * newer version instead, at your option.
010: * ====================================================================
011: */
012: package org.tmatesoft.svn.core.internal.wc;
013:
014: import java.io.File;
015: import java.io.InputStream;
016: import java.util.Collection;
017: import java.util.Iterator;
018: import java.util.Map;
019: import java.util.TreeMap;
020:
021: import org.tmatesoft.svn.core.SVNCommitInfo;
022: import org.tmatesoft.svn.core.SVNErrorCode;
023: import org.tmatesoft.svn.core.SVNErrorMessage;
024: import org.tmatesoft.svn.core.SVNException;
025: import org.tmatesoft.svn.core.SVNNodeKind;
026: import org.tmatesoft.svn.core.SVNProperty;
027: import org.tmatesoft.svn.core.SVNURL;
028: import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
029: import org.tmatesoft.svn.core.internal.wc.admin.SVNAdminArea;
030: import org.tmatesoft.svn.core.internal.wc.admin.SVNEntry;
031: import org.tmatesoft.svn.core.internal.wc.admin.SVNTranslator;
032: import org.tmatesoft.svn.core.internal.wc.admin.SVNVersionedProperties;
033: import org.tmatesoft.svn.core.internal.wc.admin.SVNWCAccess;
034: import org.tmatesoft.svn.core.io.ISVNEditor;
035: import org.tmatesoft.svn.core.io.diff.SVNDeltaGenerator;
036: import org.tmatesoft.svn.core.wc.ISVNEventHandler;
037: import org.tmatesoft.svn.core.wc.SVNCommitItem;
038: import org.tmatesoft.svn.core.wc.SVNEvent;
039: import org.tmatesoft.svn.core.wc.SVNEventAction;
040:
041: /**
042: * @version 1.1.1
043: * @author TMate Software Ltd.
044: */
045: public class SVNCommitter implements ISVNCommitPathHandler {
046:
047: private Map myCommitItems;
048: private Map myModifiedFiles;
049: private Collection myTmpFiles;
050: private String myRepositoryRoot;
051: private SVNDeltaGenerator myDeltaGenerator;
052:
053: public SVNCommitter(Map commitItems, String reposRoot,
054: Collection tmpFiles) {
055: myCommitItems = commitItems;
056: myModifiedFiles = new TreeMap();
057: myTmpFiles = tmpFiles;
058: myRepositoryRoot = reposRoot;
059: }
060:
061: public boolean handleCommitPath(String commitPath,
062: ISVNEditor commitEditor) throws SVNException {
063: SVNCommitItem item = (SVNCommitItem) myCommitItems
064: .get(commitPath);
065: SVNWCAccess wcAccess = item.getWCAccess();
066: wcAccess.checkCancelled();
067: if (item.isCopied()) {
068: if (item.getCopyFromURL() == null) {
069: SVNErrorMessage err = SVNErrorMessage
070: .create(
071: SVNErrorCode.BAD_URL,
072: "Commit item ''{0}'' has copy flag but no copyfrom URL",
073: item.getFile());
074: SVNErrorManager.error(err);
075: } else if (item.getRevision().getNumber() < 0) {
076: SVNErrorMessage err = SVNErrorMessage
077: .create(
078: SVNErrorCode.CLIENT_BAD_REVISION,
079: "Commit item ''{0}'' has copy flag but an invalid revision",
080: item.getFile());
081: SVNErrorManager.error(err);
082: }
083: }
084: SVNEvent event = null;
085: boolean closeDir = false;
086:
087: if (item.isAdded() && item.isDeleted()) {
088: event = SVNEventFactory.createCommitEvent(wcAccess
089: .getAnchor(), item.getFile(),
090: SVNEventAction.COMMIT_REPLACED, item.getKind(),
091: null);
092: } else if (item.isAdded()) {
093: String mimeType = null;
094: if (item.getKind() == SVNNodeKind.FILE) {
095: SVNAdminArea dir = item.getWCAccess().retrieve(
096: item.getFile().getParentFile());
097: mimeType = dir.getProperties(item.getFile().getName())
098: .getPropertyValue(SVNProperty.MIME_TYPE);
099: }
100: event = SVNEventFactory.createCommitEvent(wcAccess
101: .getAnchor(), item.getFile(),
102: SVNEventAction.COMMIT_ADDED, item.getKind(),
103: mimeType);
104: } else if (item.isDeleted()) {
105: event = SVNEventFactory
106: .createCommitEvent(wcAccess.getAnchor(), item
107: .getFile(), SVNEventAction.COMMIT_DELETED,
108: item.getKind(), null);
109: } else if (item.isContentsModified()
110: || item.isPropertiesModified()) {
111: event = SVNEventFactory.createCommitEvent(wcAccess
112: .getAnchor(), item.getFile(),
113: SVNEventAction.COMMIT_MODIFIED, item.getKind());
114: }
115: if (event != null) {
116: event.setPath(item.getPath());
117: wcAccess.handleEvent(event, ISVNEventHandler.UNKNOWN);
118: }
119: long rev = item.getRevision().getNumber();
120: long cfRev = item.getCopyFromRevision().getNumber();//item.getCopyFromURL() != null ? rev : -1;
121: if (item.isDeleted()) {
122: commitEditor.deleteEntry(commitPath, rev);
123: }
124: boolean fileOpen = false;
125: if (item.isAdded()) {
126: String copyFromPath = getCopyFromPath(item.getCopyFromURL());
127: if (item.getKind() == SVNNodeKind.FILE) {
128: commitEditor.addFile(commitPath, copyFromPath, cfRev);
129: fileOpen = true;
130: } else {
131: commitEditor.addDir(commitPath, copyFromPath, cfRev);
132: closeDir = true;
133: }
134: }
135: if (item.isPropertiesModified()) {
136: if (item.getKind() == SVNNodeKind.FILE) {
137: if (!fileOpen) {
138: commitEditor.openFile(commitPath, rev);
139: }
140: fileOpen = true;
141: } else if (!item.isAdded()) {
142: // do not open dir twice.
143: if ("".equals(commitPath)) {
144: commitEditor.openRoot(rev);
145: } else {
146: commitEditor.openDir(commitPath, rev);
147: }
148: closeDir = true;
149: }
150: sendPropertiedDelta(commitPath, item, commitEditor);
151: }
152: if (item.isContentsModified()
153: && item.getKind() == SVNNodeKind.FILE) {
154: if (!fileOpen) {
155: commitEditor.openFile(commitPath, rev);
156: }
157: myModifiedFiles.put(commitPath, item);
158: } else if (fileOpen) {
159: commitEditor.closeFile(commitPath, null);
160: }
161: return closeDir;
162: }
163:
164: public void sendTextDeltas(ISVNEditor editor) throws SVNException {
165: for (Iterator paths = myModifiedFiles.keySet().iterator(); paths
166: .hasNext();) {
167: String path = (String) paths.next();
168: SVNCommitItem item = (SVNCommitItem) myModifiedFiles
169: .get(path);
170: SVNWCAccess wcAccess = item.getWCAccess();
171: wcAccess.checkCancelled();
172:
173: SVNEvent event = SVNEventFactory.createCommitEvent(wcAccess
174: .getAnchor(), item.getFile(),
175: SVNEventAction.COMMIT_DELTA_SENT, SVNNodeKind.FILE,
176: null);
177: wcAccess.handleEvent(event, ISVNEventHandler.UNKNOWN);
178:
179: SVNAdminArea dir = wcAccess.retrieve(item.getFile()
180: .getParentFile());
181: String name = SVNPathUtil.tail(item.getPath());
182: SVNEntry entry = dir.getEntry(name, false);
183:
184: File tmpFile = dir.getBaseFile(name, true);
185: myTmpFiles.add(tmpFile);
186: SVNTranslator.translate(dir, name, name, SVNFileUtil
187: .getBasePath(tmpFile), false);
188:
189: String checksum = null;
190: if (!item.isAdded()) {
191: checksum = SVNFileUtil.computeChecksum(dir.getBaseFile(
192: name, false));
193: String realChecksum = entry.getChecksum();
194: if (realChecksum != null
195: && !realChecksum.equals(checksum)) {
196: SVNErrorMessage err = SVNErrorMessage
197: .create(
198: SVNErrorCode.WC_CORRUPT_TEXT_BASE,
199: "Checksum mismatch for ''{0}''; expected: ''{1}'', actual: ''{2}''",
200: new Object[] { dir.getFile(name),
201: realChecksum, checksum });
202: SVNErrorManager.error(err);
203: }
204: }
205: editor.applyTextDelta(path, checksum);
206: if (myDeltaGenerator == null) {
207: myDeltaGenerator = new SVNDeltaGenerator();
208: }
209: InputStream sourceIS = null;
210: InputStream targetIS = null;
211: File baseFile = dir.getBaseFile(name, false);
212: String newChecksum = null;
213: try {
214: sourceIS = !item.isAdded() && baseFile.exists() ? SVNFileUtil
215: .openFileForReading(baseFile)
216: : SVNFileUtil.DUMMY_IN;
217: targetIS = tmpFile.exists() ? SVNFileUtil
218: .openFileForReading(tmpFile)
219: : SVNFileUtil.DUMMY_IN;
220: newChecksum = myDeltaGenerator.sendDelta(path,
221: sourceIS, 0, targetIS, editor, true);
222: } finally {
223: SVNFileUtil.closeFile(sourceIS);
224: SVNFileUtil.closeFile(targetIS);
225: }
226: editor.closeFile(path, newChecksum);
227: }
228: }
229:
230: private void sendPropertiedDelta(String commitPath,
231: SVNCommitItem item, ISVNEditor editor) throws SVNException {
232: SVNAdminArea dir;
233: String name;
234: SVNWCAccess wcAccess = item.getWCAccess();
235: if (item.getKind() == SVNNodeKind.DIR) {
236: dir = wcAccess.retrieve(item.getFile());
237: name = "";
238: } else {
239: dir = wcAccess.retrieve(item.getFile().getParentFile());
240: name = SVNPathUtil.tail(item.getPath());
241: }
242: if (!dir.hasPropModifications(name)) {
243: return;
244: }
245: SVNEntry entry = dir.getEntry(name, false);
246: boolean replaced = false;
247: if (entry != null) {
248: replaced = entry.isScheduledForReplacement();
249: }
250: SVNVersionedProperties props = dir.getProperties(name);
251: SVNVersionedProperties baseProps = replaced ? null : dir
252: .getBaseProperties(name);
253: Map diff = replaced ? props.asMap() : baseProps
254: .compareTo(props).asMap();
255: if (diff != null && !diff.isEmpty()) {
256: File tmpPropsFile = dir.getPropertiesFile(name, true);
257: SVNProperties tmpProps = new SVNProperties(tmpPropsFile,
258: null);
259: for (Iterator propNames = props.getPropertyNames(null)
260: .iterator(); propNames.hasNext();) {
261: String propName = (String) propNames.next();
262: String propValue = props.getPropertyValue(propName);
263: tmpProps.setPropertyValue(propName, propValue);
264: }
265: if (!tmpPropsFile.exists()) {
266: // create empty tmp (!) file just to make sure it will be used on post-commit.
267: SVNFileUtil.createEmptyFile(tmpPropsFile);
268: }
269: myTmpFiles.add(tmpPropsFile);
270:
271: for (Iterator names = diff.keySet().iterator(); names
272: .hasNext();) {
273: String propName = (String) names.next();
274: String value = (String) diff.get(propName);
275: if (item.getKind() == SVNNodeKind.FILE) {
276: editor.changeFileProperty(commitPath, propName,
277: value);
278: } else {
279: editor.changeDirProperty(propName, value);
280: }
281: }
282: }
283: }
284:
285: private String getCopyFromPath(SVNURL url) {
286: if (url == null) {
287: return null;
288: }
289: String path = url.getPath();
290: if (myRepositoryRoot.equals(path)) {
291: return "/";
292: }
293: return path.substring(myRepositoryRoot.length());
294: }
295:
296: public static SVNCommitInfo commit(Collection tmpFiles,
297: Map commitItems, String repositoryRoot,
298: ISVNEditor commitEditor) throws SVNException {
299: SVNCommitter committer = new SVNCommitter(commitItems,
300: repositoryRoot, tmpFiles);
301: SVNCommitUtil.driveCommitEditor(committer,
302: commitItems.keySet(), commitEditor, -1);
303: committer.sendTextDeltas(commitEditor);
304:
305: return commitEditor.closeEdit();
306: }
307: }
|