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.admin;
013:
014: import java.io.File;
015: import java.io.IOException;
016: import java.io.InputStream;
017: import java.io.OutputStream;
018: import java.util.Date;
019: import java.util.HashMap;
020: import java.util.Map;
021:
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.internal.util.SVNTimeUtil;
028: import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
029: import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
030: import org.tmatesoft.svn.core.wc.SVNStatusType;
031: import org.tmatesoft.svn.util.SVNDebugLog;
032:
033: /**
034: * @version 1.1.1
035: * @author TMate Software Ltd.
036: */
037: public class SVNLogRunner {
038: private boolean myIsEntriesChanged;
039: private boolean myIsWCPropertiesChanged;
040:
041: public void runCommand(SVNAdminArea adminArea, String name,
042: Map attributes, int count) throws SVNException {
043: SVNException error = null;
044: String fileName = (String) attributes.get(SVNLog.NAME_ATTR);
045: if (SVNLog.DELETE_ENTRY.equals(name)) {
046: File path = adminArea.getFile(fileName);
047: SVNAdminArea dir = adminArea.getWCAccess().probeRetrieve(
048: path);
049: SVNEntry entry = dir.getWCAccess().getEntry(path, false);
050: if (entry == null) {
051: return;
052: }
053: try {
054: if (entry.isDirectory()) {
055: try {
056: SVNAdminArea childDir = dir.getWCAccess()
057: .retrieve(path);
058: // it should be null when there is no dir already.
059: if (childDir != null) {
060: childDir.removeFromRevisionControl(childDir
061: .getThisDirName(), true, false);
062: } else {
063: SVNErrorManager
064: .error(SVNErrorMessage
065: .create(SVNErrorCode.WC_NOT_LOCKED));
066: }
067: } catch (SVNException e) {
068: if (e.getErrorMessage().getErrorCode() == SVNErrorCode.WC_NOT_LOCKED) {
069: if (!entry.isScheduledForAddition()) {
070: adminArea.deleteEntry(fileName);
071: adminArea.saveEntries(false);
072: }
073: } else {
074: throw e;
075: }
076: }
077: } else if (entry.isFile()) {
078: adminArea.removeFromRevisionControl(fileName, true,
079: false);
080: }
081: } catch (SVNException e) {
082: if (e.getErrorMessage().getErrorCode() != SVNErrorCode.WC_LEFT_LOCAL_MOD) {
083: error = e;
084: }
085: }
086: } else if (SVNLog.MODIFY_ENTRY.equals(name)) {
087: try {
088: Map entryAttrs = new HashMap(attributes);
089: entryAttrs.remove("");
090: entryAttrs.remove(SVNLog.NAME_ATTR);
091: if (entryAttrs.containsKey(SVNProperty
092: .shortPropertyName(SVNProperty.TEXT_TIME))) {
093: String value = (String) entryAttrs.get(SVNProperty
094: .shortPropertyName(SVNProperty.TEXT_TIME));
095: if (SVNLog.WC_TIMESTAMP.equals(value)) {
096: File file = adminArea.getFile(fileName);
097: value = SVNTimeUtil.formatDate(new Date(file
098: .lastModified()));
099: entryAttrs
100: .put(
101: SVNProperty
102: .shortPropertyName(SVNProperty.TEXT_TIME),
103: value);
104: }
105: }
106: if (entryAttrs.containsKey(SVNProperty
107: .shortPropertyName(SVNProperty.PROP_TIME))) {
108: String value = (String) entryAttrs.get(SVNProperty
109: .shortPropertyName(SVNProperty.PROP_TIME));
110: if (SVNLog.WC_TIMESTAMP.equals(value)) {
111: SVNEntry entry = adminArea.getEntry(fileName,
112: false);
113: if (entry == null) {
114: return;
115: }
116: value = adminArea.getPropertyTime(fileName);
117: entryAttrs
118: .put(
119: SVNProperty
120: .shortPropertyName(SVNProperty.PROP_TIME),
121: value);
122: }
123: }
124: try {
125: adminArea.modifyEntry(fileName, entryAttrs, false,
126: false);
127: } catch (SVNException svne) {
128: SVNErrorCode code = count <= 1 ? SVNErrorCode.WC_BAD_ADM_LOG_START
129: : SVNErrorCode.WC_BAD_ADM_LOG;
130: SVNErrorMessage err = SVNErrorMessage.create(code,
131: "Error modifying entry for ''{0}''",
132: fileName);
133: SVNErrorManager.error(err, svne);
134: }
135: setEntriesChanged(true);
136: } catch (SVNException svne) {
137: error = svne;
138: }
139: } else if (SVNLog.MODIFY_WC_PROPERTY.equals(name)) {
140: try {
141: SVNVersionedProperties wcprops = adminArea
142: .getWCProperties(fileName);
143: if (wcprops != null) {
144: String propName = (String) attributes
145: .get(SVNLog.PROPERTY_NAME_ATTR);
146: String propValue = (String) attributes
147: .get(SVNLog.PROPERTY_VALUE_ATTR);
148: wcprops.setPropertyValue(propName, propValue);
149: setWCPropertiesChanged(true);
150: }
151: } catch (SVNException svne) {
152: error = svne;
153: }
154: } else if (SVNLog.DELETE_LOCK.equals(name)) {
155: try {
156: SVNEntry entry = adminArea.getEntry(fileName, true);
157: if (entry != null) {
158: entry.setLockToken(null);
159: entry.setLockOwner(null);
160: entry.setLockCreationDate(null);
161: entry.setLockComment(null);
162: setEntriesChanged(true);
163: }
164: } catch (SVNException svne) {
165: SVNErrorCode code = count <= 1 ? SVNErrorCode.WC_BAD_ADM_LOG_START
166: : SVNErrorCode.WC_BAD_ADM_LOG;
167: SVNErrorMessage err = SVNErrorMessage.create(code,
168: "Error removing lock from entry for ''{0}''",
169: fileName);
170: error = new SVNException(err, svne);
171: }
172: } else if (SVNLog.DELETE.equals(name)) {
173: File file = adminArea.getFile(fileName);
174: SVNFileUtil.deleteFile(file);
175: } else if (SVNLog.READONLY.equals(name)) {
176: File file = adminArea.getFile(fileName);
177: SVNFileUtil.setReadonly(file, true);
178: } else if (SVNLog.MOVE.equals(name)) {
179: File src = adminArea.getFile(fileName);
180: File dst = adminArea.getFile((String) attributes
181: .get(SVNLog.DEST_ATTR));
182: try {
183: SVNFileUtil.rename(src, dst);
184: } catch (SVNException svne) {
185: error = new SVNException(svne.getErrorMessage().wrap(
186: "Can't move source to dest"), svne);
187: }
188: } else if (SVNLog.APPEND.equals(name)) {
189: File src = adminArea.getFile(fileName);
190: File dst = adminArea.getFile((String) attributes
191: .get(SVNLog.DEST_ATTR));
192: OutputStream os = null;
193: InputStream is = null;
194: try {
195: os = SVNFileUtil.openFileForWriting(dst, true);
196: is = SVNFileUtil.openFileForReading(src);
197: while (true) {
198: int r = is.read();
199: if (r < 0) {
200: break;
201: }
202: os.write(r);
203: }
204: } catch (IOException e) {
205: if (src.exists()) {
206: SVNErrorMessage err = SVNErrorMessage
207: .create(SVNErrorCode.IO_ERROR,
208: "Cannot write to ''{0}'': {1}",
209: new Object[] { dst,
210: e.getLocalizedMessage() });
211: error = new SVNException(err, e);
212: }
213: } catch (SVNException svne) {
214: if (src.exists()) {
215: error = svne;
216: }
217: } finally {
218: SVNFileUtil.closeFile(os);
219: SVNFileUtil.closeFile(is);
220: }
221: } else if (SVNLog.SET_TIMESTAMP.equals(name)) {
222: File file = adminArea.getFile(fileName);
223: String timestamp = (String) attributes
224: .get(SVNLog.TIMESTAMP_ATTR);
225: try {
226: if (timestamp == null) {
227: SVNErrorCode code = count <= 1 ? SVNErrorCode.WC_BAD_ADM_LOG_START
228: : SVNErrorCode.WC_BAD_ADM_LOG;
229: SVNErrorMessage err = SVNErrorMessage.create(code,
230: "Missing 'timestamp' attribute in ''{0}''",
231: adminArea.getRoot());
232: SVNErrorManager.error(err);
233: }
234: Date time = SVNTimeUtil.parseDate(timestamp);
235: //TODO: what about special files (do not set for them).
236: if (!file.setLastModified(time.getTime())) {
237: if (!file.canWrite() && file.isFile()) {
238: SVNFileUtil.setReadonly(file, false);
239: file.setLastModified(time.getTime());
240: SVNFileUtil.setReadonly(file, true);
241: }
242: }
243: } catch (SVNException svne) {
244: error = svne;
245: }
246: } else if (SVNLog.UPGRADE_FORMAT.equals(name)) {
247: String format = (String) attributes.get(SVNLog.FORMAT_ATTR);
248: SVNErrorCode code = count <= 1 ? SVNErrorCode.WC_BAD_ADM_LOG_START
249: : SVNErrorCode.WC_BAD_ADM_LOG;
250: try {
251: if (format == null) {
252: SVNErrorMessage err = SVNErrorMessage.create(code,
253: "Invalid 'format' attribute");
254: SVNErrorManager.error(err);
255: }
256: int number = -1;
257: try {
258: number = Integer.parseInt(format);
259: } catch (NumberFormatException e) {
260: SVNErrorMessage err = SVNErrorMessage.create(code,
261: "Invalid 'format' attribute");
262: SVNErrorManager.error(err, e);
263: }
264: adminArea.postUpgradeFormat(number);
265: setEntriesChanged(true);
266: } catch (SVNException svne) {
267: error = svne;
268: }
269: } else if (SVNLog.MAYBE_READONLY.equals(name)) {
270: File file = adminArea.getFile(fileName);
271: try {
272: SVNEntry entry = adminArea.getEntry(fileName, false);
273: if (entry != null) {
274: SVNVersionedProperties props = adminArea
275: .getProperties(fileName);
276: String needsLock = props
277: .getPropertyValue(SVNProperty.NEEDS_LOCK);
278: if (entry.getLockToken() == null
279: && needsLock != null) {
280: SVNFileUtil.setReadonly(file, true);
281: }
282: }
283: } catch (SVNException svne) {
284: error = svne;
285: }
286: } else if (SVNLog.COPY_AND_TRANSLATE.equals(name)) {
287: String dstName = (String) attributes.get(SVNLog.DEST_ATTR);
288: File src = adminArea.getFile(fileName);
289: File dst = adminArea.getFile(dstName);
290: try {
291: try {
292: SVNTranslator.translate(adminArea, dstName,
293: fileName, dstName, true);
294: } catch (SVNException svne) {
295: if (src.exists()) {
296: throw svne;
297: }
298: }
299: // get properties for this entry.
300: SVNVersionedProperties props = adminArea
301: .getProperties(dstName);
302: boolean executable = SVNFileUtil.isWindows ? false
303: : props
304: .getPropertyValue(SVNProperty.EXECUTABLE) != null;
305:
306: if (executable) {
307: SVNFileUtil.setExecutable(dst, true);
308: }
309: SVNEntry entry = adminArea.getEntry(dstName, false);
310: if (entry != null
311: && entry.getLockToken() == null
312: && props
313: .getPropertyValue(SVNProperty.NEEDS_LOCK) != null) {
314: SVNFileUtil.setReadonly(dst, true);
315: }
316: } catch (SVNException svne) {
317: error = svne;
318: }
319: } else if (SVNLog.COPY_AND_DETRANSLATE.equals(name)) {
320: String dstName = (String) attributes.get(SVNLog.DEST_ATTR);
321: try {
322: SVNTranslator.translate(adminArea, fileName, fileName,
323: dstName, false);
324: } catch (SVNException svne) {
325: error = svne;
326: }
327: } else if (SVNLog.COPY.equals(name)) {
328: File src = adminArea.getFile(fileName);
329: File dst = adminArea.getFile((String) attributes
330: .get(SVNLog.DEST_ATTR));
331: try {
332: SVNFileUtil.copy(src, dst, true, false);
333: } catch (SVNException svne) {
334: error = svne;
335: }
336: } else if (SVNLog.MERGE.equals(name)) {
337: File target = adminArea.getFile(fileName);
338: try {
339: SVNErrorCode code = count <= 1 ? SVNErrorCode.WC_BAD_ADM_LOG_START
340: : SVNErrorCode.WC_BAD_ADM_LOG;
341: String leftPath = (String) attributes.get(SVNLog.ATTR1);
342: if (leftPath == null) {
343: SVNErrorMessage err = SVNErrorMessage.create(code,
344: "Missing 'left' attribute in ''{0}''",
345: adminArea.getRoot());
346: SVNErrorManager.error(err);
347: }
348: String rightPath = (String) attributes
349: .get(SVNLog.ATTR2);
350: if (rightPath == null) {
351: SVNErrorMessage err = SVNErrorMessage.create(code,
352: "Missing 'right' attribute in ''{0}''",
353: adminArea.getRoot());
354: SVNErrorManager.error(err);
355: }
356: String leftLabel = (String) attributes
357: .get(SVNLog.ATTR3);
358: leftLabel = leftLabel == null ? ".old" : leftLabel;
359: String rightLabel = (String) attributes
360: .get(SVNLog.ATTR4);
361: rightLabel = rightLabel == null ? ".new" : rightLabel;
362: String targetLabel = (String) attributes
363: .get(SVNLog.ATTR5);
364: targetLabel = targetLabel == null ? ".working"
365: : targetLabel;
366:
367: SVNVersionedProperties props = adminArea
368: .getProperties(fileName);
369: SVNEntry entry = adminArea.getEntry(fileName, true);
370:
371: String leaveConglictsAttr = (String) attributes
372: .get(SVNLog.ATTR6);
373: boolean leaveConflicts = Boolean.TRUE.toString()
374: .equals(leaveConglictsAttr);
375: SVNStatusType mergeResult = adminArea.mergeText(
376: fileName, adminArea.getFile(leftPath),
377: adminArea.getFile(rightPath), targetLabel,
378: leftLabel, rightLabel, leaveConflicts, false);
379:
380: if (props.getPropertyValue(SVNProperty.EXECUTABLE) != null) {
381: SVNFileUtil.setExecutable(target, true);
382: }
383: if (props.getPropertyValue(SVNProperty.NEEDS_LOCK) != null
384: && entry.getLockToken() == null) {
385: SVNFileUtil.setReadonly(target, true);
386: }
387: setEntriesChanged(mergeResult == SVNStatusType.CONFLICTED
388: || mergeResult == SVNStatusType.CONFLICTED_UNRESOLVED);
389: } catch (SVNException svne) {
390: error = svne;
391: }
392: } else if (SVNLog.COMMIT.equals(name)) {
393: try {
394: SVNErrorCode code = count <= 1 ? SVNErrorCode.WC_BAD_ADM_LOG_START
395: : SVNErrorCode.WC_BAD_ADM_LOG;
396: if (attributes.get(SVNLog.REVISION_ATTR) == null) {
397: SVNErrorMessage err = SVNErrorMessage.create(code,
398: "Missing revision attribute for ''{0}''",
399: fileName);
400: SVNErrorManager.error(err);
401: }
402:
403: SVNEntry entry = adminArea.getEntry(fileName, true);
404: if (entry == null
405: || (!adminArea.getThisDirName()
406: .equals(fileName) && entry.getKind() != SVNNodeKind.FILE)) {
407: SVNErrorMessage err = SVNErrorMessage
408: .create(
409: code,
410: "Log command for directory ''{0}'' is mislocated",
411: adminArea.getRoot());
412: SVNErrorManager.error(err);
413: }
414: boolean implicit = attributes.get("implicit") != null
415: && entry.isCopied();
416: setEntriesChanged(true);
417: long revisionNumber = Long
418: .parseLong((String) attributes
419: .get(SVNLog.REVISION_ATTR));
420: adminArea.postCommit(fileName, revisionNumber,
421: implicit, code);
422: } catch (SVNException svne) {
423: error = svne;
424: }
425: } else {
426: SVNErrorCode code = count <= 1 ? SVNErrorCode.WC_BAD_ADM_LOG_START
427: : SVNErrorCode.WC_BAD_ADM_LOG;
428: SVNErrorMessage err = SVNErrorMessage.create(code,
429: "Unrecognized logfile element ''{0}'' in ''{1}''",
430: new Object[] { name, adminArea.getRoot() });
431: SVNErrorManager.error(err.wrap("In directory ''{0}''",
432: adminArea.getRoot()));
433: }
434:
435: if (error != null) {
436: SVNErrorCode code = count <= 1 ? SVNErrorCode.WC_BAD_ADM_LOG_START
437: : SVNErrorCode.WC_BAD_ADM_LOG;
438: SVNErrorMessage err = SVNErrorMessage.create(code,
439: "Error processing command ''{0}'' in ''{1}''",
440: new Object[] { name, adminArea.getRoot() });
441: SVNErrorManager.error(err, error);
442: }
443: }
444:
445: private void setEntriesChanged(boolean modified) {
446: myIsEntriesChanged |= modified;
447: }
448:
449: private void setWCPropertiesChanged(boolean modified) {
450: myIsWCPropertiesChanged |= modified;
451: }
452:
453: public void logFailed(SVNAdminArea adminArea) throws SVNException {
454: if (myIsWCPropertiesChanged) {
455: adminArea.saveWCProperties(true);
456: } else {
457: adminArea.closeWCProperties();
458: }
459: if (myIsEntriesChanged) {
460: adminArea.saveEntries(false);
461: } else {
462: adminArea.closeEntries();
463: }
464: }
465:
466: public void logCompleted(SVNAdminArea adminArea)
467: throws SVNException {
468: if (myIsWCPropertiesChanged) {
469: adminArea.saveWCProperties(true);
470: }
471:
472: if (myIsEntriesChanged) {
473: adminArea.saveEntries(false);
474: }
475: boolean killMe = adminArea.isKillMe();
476: if (killMe) {
477: SVNEntry entry = adminArea.getEntry(adminArea
478: .getThisDirName(), false);
479: long dirRevision = entry != null ? entry.getRevision() : -1;
480: // deleted dir, files and entry in parent.
481: File dir = adminArea.getRoot();
482: SVNWCAccess access = adminArea.getWCAccess();
483: boolean isWCRoot = access.isWCRoot(adminArea.getRoot());
484: try {
485: adminArea.removeFromRevisionControl(adminArea
486: .getThisDirName(), true, false);
487: } catch (SVNException svne) {
488: SVNDebugLog.getDefaultLog().info(svne);
489: if (svne.getErrorMessage().getErrorCode() != SVNErrorCode.WC_LEFT_LOCAL_MOD) {
490: throw svne;
491: }
492: }
493: if (isWCRoot) {
494: return;
495: }
496: // compare revision with parent's one
497: SVNAdminArea parentArea = access.retrieve(dir
498: .getParentFile());
499: SVNEntry parentEntry = parentArea.getEntry(parentArea
500: .getThisDirName(), false);
501: if (dirRevision > parentEntry.getRevision()) {
502: SVNEntry entryInParent = parentArea.addEntry(dir
503: .getName());
504: entryInParent.setDeleted(true);
505: entryInParent.setKind(SVNNodeKind.DIR);
506: entryInParent.setRevision(dirRevision);
507: parentArea.saveEntries(false);
508: }
509: }
510: myIsEntriesChanged = false;
511: myIsWCPropertiesChanged = false;
512: }
513:
514: }
|