0001: /*
0002: * ====================================================================
0003: * Copyright (c) 2004-2008 TMate Software Ltd. All rights reserved.
0004: *
0005: * This software is licensed as described in the file COPYING, which
0006: * you should have received as part of this distribution. The terms
0007: * are also available at http://svnkit.com/license.html
0008: * If newer versions of this license are posted there, you may use a
0009: * newer version instead, at your option.
0010: * ====================================================================
0011: */
0012: package org.tmatesoft.svn.core.internal.wc.admin;
0013:
0014: import java.io.BufferedOutputStream;
0015: import java.io.BufferedReader;
0016: import java.io.File;
0017: import java.io.FileOutputStream;
0018: import java.io.IOException;
0019: import java.io.InputStreamReader;
0020: import java.io.OutputStream;
0021: import java.io.OutputStreamWriter;
0022: import java.io.Writer;
0023: import java.util.ArrayList;
0024: import java.util.Collections;
0025: import java.util.Date;
0026: import java.util.HashMap;
0027: import java.util.HashSet;
0028: import java.util.Iterator;
0029: import java.util.LinkedList;
0030: import java.util.List;
0031: import java.util.Map;
0032: import java.util.Set;
0033:
0034: import org.tmatesoft.svn.core.SVNErrorCode;
0035: import org.tmatesoft.svn.core.SVNErrorMessage;
0036: import org.tmatesoft.svn.core.SVNException;
0037: import org.tmatesoft.svn.core.SVNNodeKind;
0038: import org.tmatesoft.svn.core.SVNProperty;
0039: import org.tmatesoft.svn.core.internal.io.fs.FSFile;
0040: import org.tmatesoft.svn.core.internal.util.SVNEncodingUtil;
0041: import org.tmatesoft.svn.core.internal.util.SVNFormatUtil;
0042: import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
0043: import org.tmatesoft.svn.core.internal.util.SVNTimeUtil;
0044: import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
0045: import org.tmatesoft.svn.core.internal.wc.SVNFileListUtil;
0046: import org.tmatesoft.svn.core.internal.wc.SVNFileType;
0047: import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
0048: import org.tmatesoft.svn.core.internal.wc.SVNProperties;
0049: import org.tmatesoft.svn.util.SVNDebugLog;
0050:
0051: /**
0052: * @version 1.1.1
0053: * @author TMate Software Ltd.
0054: */
0055: public class SVNAdminArea14 extends SVNAdminArea {
0056: public static final String[] ourCachableProperties = new String[] {
0057: SVNProperty.SPECIAL, SVNProperty.EXTERNALS,
0058: SVNProperty.NEEDS_LOCK };
0059:
0060: public static final int WC_FORMAT = 8;
0061:
0062: private static final String ATTRIBUTE_COPIED = "copied";
0063: private static final String ATTRIBUTE_DELETED = "deleted";
0064: private static final String ATTRIBUTE_ABSENT = "absent";
0065: private static final String ATTRIBUTE_INCOMPLETE = "incomplete";
0066: private static final String THIS_DIR = "";
0067:
0068: private File myLockFile;
0069: private File myEntriesFile;
0070:
0071: private static boolean ourIsOptimizedWritingEnabled;
0072:
0073: public SVNAdminArea14(File dir) {
0074: super (dir);
0075: myLockFile = new File(getAdminDirectory(), "lock");
0076: myEntriesFile = new File(getAdminDirectory(), "entries");
0077: }
0078:
0079: public static void setOptimizedWritingEnabled(boolean enabled) {
0080: ourIsOptimizedWritingEnabled = enabled;
0081: }
0082:
0083: public static String[] getCachableProperties() {
0084: return ourCachableProperties;
0085: }
0086:
0087: public void saveWCProperties(boolean close) throws SVNException {
0088: Map wcPropsCache = getWCPropertiesStorage(false);
0089: if (wcPropsCache == null) {
0090: return;
0091: }
0092:
0093: boolean hasAnyProps = false;
0094: File dstFile = getAdminFile("all-wcprops");
0095: File tmpFile = getAdminFile("tmp/all-wcprops");
0096:
0097: for (Iterator entries = wcPropsCache.keySet().iterator(); entries
0098: .hasNext();) {
0099: String name = (String) entries.next();
0100: SVNVersionedProperties props = (SVNVersionedProperties) wcPropsCache
0101: .get(name);
0102: if (!props.isEmpty()) {
0103: hasAnyProps = true;
0104: break;
0105: }
0106: }
0107:
0108: if (hasAnyProps) {
0109: OutputStream target = null;
0110: try {
0111: target = SVNFileUtil.openFileForWriting(tmpFile);
0112: SVNVersionedProperties props = (SVNVersionedProperties) wcPropsCache
0113: .get(getThisDirName());
0114: if (props != null && !props.isEmpty()) {
0115: SVNProperties.setProperties(props.asMap(), target,
0116: SVNProperties.SVN_HASH_TERMINATOR);
0117: } else {
0118: SVNProperties.setProperties(Collections.EMPTY_MAP,
0119: target, SVNProperties.SVN_HASH_TERMINATOR);
0120: }
0121:
0122: for (Iterator entries = wcPropsCache.keySet()
0123: .iterator(); entries.hasNext();) {
0124: String name = (String) entries.next();
0125: if (getThisDirName().equals(name)) {
0126: continue;
0127: }
0128: props = (SVNVersionedProperties) wcPropsCache
0129: .get(name);
0130: if (!props.isEmpty()) {
0131: target.write(name.getBytes("UTF-8"));
0132: target.write('\n');
0133: SVNProperties.setProperties(props.asMap(),
0134: target,
0135: SVNProperties.SVN_HASH_TERMINATOR);
0136: }
0137: }
0138: } catch (IOException ioe) {
0139: SVNErrorMessage err = SVNErrorMessage.create(
0140: SVNErrorCode.IO_ERROR, ioe
0141: .getLocalizedMessage());
0142: SVNErrorManager.error(err, ioe);
0143: } finally {
0144: SVNFileUtil.closeFile(target);
0145: }
0146: SVNFileUtil.rename(tmpFile, dstFile);
0147: SVNFileUtil.setReadonly(dstFile, true);
0148: } else {
0149: SVNFileUtil.deleteFile(dstFile);
0150: }
0151: if (close) {
0152: closeWCProperties();
0153: }
0154: }
0155:
0156: public SVNVersionedProperties getBaseProperties(String name)
0157: throws SVNException {
0158: Map basePropsCache = getBasePropertiesStorage(true);
0159: SVNVersionedProperties props = (SVNVersionedProperties) basePropsCache
0160: .get(name);
0161: if (props != null) {
0162: return props;
0163: }
0164:
0165: Map baseProps = null;
0166: try {
0167: baseProps = readBaseProperties(name);
0168: } catch (SVNException svne) {
0169: SVNErrorMessage err = svne.getErrorMessage().wrap(
0170: "Failed to load properties from disk");
0171: SVNErrorManager.error(err);
0172: }
0173:
0174: props = new SVNProperties13(baseProps);
0175: basePropsCache.put(name, props);
0176: return props;
0177: }
0178:
0179: public SVNVersionedProperties getRevertProperties(String name)
0180: throws SVNException {
0181: Map revertPropsCache = getRevertPropertiesStorage(true);
0182: SVNVersionedProperties props = (SVNVersionedProperties) revertPropsCache
0183: .get(name);
0184: if (props != null) {
0185: return props;
0186: }
0187:
0188: Map revertProps = null;
0189: try {
0190: revertProps = readRevertProperties(name);
0191: } catch (SVNException svne) {
0192: SVNErrorMessage err = svne.getErrorMessage().wrap(
0193: "Failed to load properties from disk");
0194: SVNErrorManager.error(err);
0195: }
0196:
0197: props = new SVNProperties13(revertProps);
0198: revertPropsCache.put(name, props);
0199: return props;
0200: }
0201:
0202: public SVNVersionedProperties getProperties(String name)
0203: throws SVNException {
0204: Map propsCache = getPropertiesStorage(true);
0205: SVNVersionedProperties props = (SVNVersionedProperties) propsCache
0206: .get(name);
0207: if (props != null) {
0208: return props;
0209: }
0210:
0211: final String entryName = name;
0212: props = new SVNProperties14(null, this , name) {
0213:
0214: protected Map loadProperties() throws SVNException {
0215: Map props = getPropertiesMap();
0216: if (props == null) {
0217: try {
0218: props = readProperties(entryName);
0219: } catch (SVNException svne) {
0220: SVNErrorMessage err = svne
0221: .getErrorMessage()
0222: .wrap(
0223: "Failed to load properties from disk");
0224: SVNErrorManager.error(err);
0225: }
0226: props = props != null ? props : new HashMap();
0227: setPropertiesMap(props);
0228: }
0229: return props;
0230: }
0231: };
0232:
0233: propsCache.put(name, props);
0234: return props;
0235: }
0236:
0237: public SVNVersionedProperties getWCProperties(String entryName)
0238: throws SVNException {
0239: SVNEntry entry = getEntry(entryName, false);
0240: if (entry == null) {
0241: return null;
0242: }
0243:
0244: Map wcPropsCache = getWCPropertiesStorage(true);
0245: SVNVersionedProperties props = (SVNVersionedProperties) wcPropsCache
0246: .get(entryName);
0247: if (props != null) {
0248: return props;
0249: }
0250:
0251: if (wcPropsCache.isEmpty()) {
0252: wcPropsCache = readAllWCProperties();
0253: }
0254:
0255: props = (SVNVersionedProperties) wcPropsCache.get(entryName);
0256: if (props == null) {
0257: props = new SVNProperties13(new HashMap());
0258: wcPropsCache.put(entryName, props);
0259: }
0260: return props;
0261: }
0262:
0263: private Map readAllWCProperties() throws SVNException {
0264: Map wcPropsCache = getWCPropertiesStorage(true);
0265: wcPropsCache.clear();
0266: File propertiesFile = getAdminFile("all-wcprops");
0267: if (!propertiesFile.exists()) {
0268: return wcPropsCache;
0269: }
0270:
0271: FSFile wcpropsFile = null;
0272: try {
0273: wcpropsFile = new FSFile(propertiesFile);
0274: Map wcProps = wcpropsFile.readProperties(false);
0275: SVNVersionedProperties entryWCProps = new SVNProperties13(
0276: wcProps);
0277: wcPropsCache.put(getThisDirName(), entryWCProps);
0278:
0279: String name = null;
0280: StringBuffer buffer = new StringBuffer();
0281: while (true) {
0282: try {
0283: name = wcpropsFile.readLine(buffer);
0284: } catch (SVNException e) {
0285: if (e.getErrorMessage().getErrorCode() == SVNErrorCode.STREAM_UNEXPECTED_EOF
0286: && buffer.length() > 0) {
0287: SVNErrorMessage err = SVNErrorMessage
0288: .create(
0289: SVNErrorCode.WC_CORRUPT,
0290: "Missing end of line in wcprops file for ''{0}''",
0291: getRoot());
0292: SVNErrorManager.error(err, e);
0293: }
0294: break;
0295: }
0296: wcProps = wcpropsFile.readProperties(false);
0297: entryWCProps = new SVNProperties13(wcProps);
0298: wcPropsCache.put(name, entryWCProps);
0299: buffer.delete(0, buffer.length());
0300: }
0301: } catch (SVNException svne) {
0302: SVNErrorMessage err = svne.getErrorMessage().wrap(
0303: "Failed to load properties from disk");
0304: SVNErrorManager.error(err);
0305: } finally {
0306: wcpropsFile.close();
0307: }
0308: return wcPropsCache;
0309: }
0310:
0311: private Map readBaseProperties(String name) throws SVNException {
0312: File propertiesFile = getBasePropertiesFile(name, false);
0313: SVNProperties props = new SVNProperties(propertiesFile, null);
0314: return props.asMap();
0315: }
0316:
0317: private Map readRevertProperties(String name) throws SVNException {
0318: File propertiesFile = getRevertPropertiesFile(name, false);
0319: SVNProperties props = new SVNProperties(propertiesFile, null);
0320: return props.asMap();
0321: }
0322:
0323: private Map readProperties(String name) throws SVNException {
0324: // SVNEntry entry = getEntry(name, false);
0325: if (hasPropModifications(name)
0326: /*|| (entry != null && entry.isScheduledForReplacement())*/) {
0327: // ignore base props when entry is schedule for replacement.
0328: File propertiesFile = getPropertiesFile(name, false);
0329: SVNProperties props = new SVNProperties(propertiesFile,
0330: null);
0331: return props.asMap();
0332: }
0333:
0334: Map basePropsCache = getBasePropertiesStorage(true);
0335: if (basePropsCache != null) {
0336: SVNVersionedProperties baseProps = (SVNVersionedProperties) basePropsCache
0337: .get(name);
0338: if (baseProps != null) {
0339: return baseProps.asMap();
0340: }
0341: }
0342: if (hasProperties(name)) {
0343: return readBaseProperties(name);
0344: }
0345: return new HashMap();
0346: }
0347:
0348: public void saveVersionedProperties(SVNLog log, boolean close)
0349: throws SVNException {
0350: Map command = new HashMap();
0351: Set processedEntries = new HashSet();
0352:
0353: Map propsCache = getPropertiesStorage(false);
0354: if (propsCache != null && !propsCache.isEmpty()) {
0355: for (Iterator entries = propsCache.keySet().iterator(); entries
0356: .hasNext();) {
0357: String name = (String) entries.next();
0358: SVNVersionedProperties props = (SVNVersionedProperties) propsCache
0359: .get(name);
0360: if (props.isModified()) {
0361: SVNVersionedProperties baseProps = getBaseProperties(name);
0362: SVNVersionedProperties propsDiff = baseProps
0363: .compareTo(props);
0364: String[] cachableProps = SVNAdminArea14
0365: .getCachableProperties();
0366: command.put(SVNProperty.CACHABLE_PROPS, asString(
0367: cachableProps, " "));
0368: Map propsMap = props.loadProperties();
0369: LinkedList presentProps = new LinkedList();
0370: for (int i = 0; i < cachableProps.length; i++) {
0371: if (propsMap.containsKey(cachableProps[i])) {
0372: presentProps.addLast(cachableProps[i]);
0373: }
0374: }
0375:
0376: if (presentProps.size() > 0) {
0377: String presentPropsString = asString(
0378: (String[]) presentProps
0379: .toArray(new String[presentProps
0380: .size()]), " ");
0381: command.put(SVNProperty.PRESENT_PROPS,
0382: presentPropsString);
0383: } else {
0384: command.put(SVNProperty.PRESENT_PROPS, "");
0385: }
0386:
0387: command.put(SVNProperty.HAS_PROPS, SVNProperty
0388: .toString(!props.isEmpty()));
0389:
0390: boolean hasPropModifications = !propsDiff.isEmpty();
0391: command.put(SVNProperty.HAS_PROP_MODS, SVNProperty
0392: .toString(hasPropModifications));
0393: command.put(SVNLog.NAME_ATTR, name);
0394: log.addCommand(SVNLog.MODIFY_ENTRY, command, false);
0395: processedEntries.add(name);
0396: command.clear();
0397:
0398: String dstPath = getThisDirName().equals(name) ? "dir-props"
0399: : "props/" + name + ".svn-work";
0400: dstPath = getAdminDirectory().getName() + "/"
0401: + dstPath;
0402:
0403: if (hasPropModifications) {
0404: String tmpPath = "tmp/";
0405: tmpPath += getThisDirName().equals(name) ? "dir-props"
0406: : "props/" + name + ".svn-work";
0407: File tmpFile = getAdminFile(tmpPath);
0408: String srcPath = getAdminDirectory().getName()
0409: + "/" + tmpPath;
0410: SVNProperties tmpProps = new SVNProperties(
0411: tmpFile, srcPath);
0412: tmpProps.setProperties(props.asMap());
0413: command.put(SVNLog.NAME_ATTR, srcPath);
0414: command.put(SVNLog.DEST_ATTR, dstPath);
0415: log.addCommand(SVNLog.MOVE, command, false);
0416: command.clear();
0417: command.put(SVNLog.NAME_ATTR, dstPath);
0418: log.addCommand(SVNLog.READONLY, command, false);
0419: } else {
0420: command.put(SVNLog.NAME_ATTR, dstPath);
0421: log.addCommand(SVNLog.DELETE, command, false);
0422: }
0423: command.clear();
0424: props.setModified(false);
0425: }
0426: }
0427: }
0428:
0429: Map basePropsCache = getBasePropertiesStorage(false);
0430: if (basePropsCache != null && !basePropsCache.isEmpty()) {
0431: for (Iterator entries = basePropsCache.keySet().iterator(); entries
0432: .hasNext();) {
0433: String name = (String) entries.next();
0434: SVNVersionedProperties baseProps = (SVNVersionedProperties) basePropsCache
0435: .get(name);
0436: if (baseProps.isModified()) {
0437: String dstPath = getThisDirName().equals(name) ? "dir-prop-base"
0438: : "prop-base/" + name + ".svn-base";
0439: dstPath = getAdminDirectory().getName() + "/"
0440: + dstPath;
0441: boolean isEntryProcessed = processedEntries
0442: .contains(name);
0443: if (!isEntryProcessed) {
0444: SVNVersionedProperties props = getProperties(name);
0445:
0446: String[] cachableProps = SVNAdminArea14
0447: .getCachableProperties();
0448: command.put(SVNProperty.CACHABLE_PROPS,
0449: asString(cachableProps, " "));
0450:
0451: Map propsMap = props.loadProperties();
0452: LinkedList presentProps = new LinkedList();
0453: for (int i = 0; i < cachableProps.length; i++) {
0454: if (propsMap.containsKey(cachableProps[i])) {
0455: presentProps.addLast(cachableProps[i]);
0456: }
0457: }
0458:
0459: if (presentProps.size() > 0) {
0460: String presentPropsString = asString(
0461: (String[]) presentProps
0462: .toArray(new String[presentProps
0463: .size()]), " ");
0464: command.put(SVNProperty.PRESENT_PROPS,
0465: presentPropsString);
0466: } else {
0467: command.put(SVNProperty.PRESENT_PROPS, "");
0468: }
0469:
0470: command.put(SVNProperty.HAS_PROPS, SVNProperty
0471: .toString(!props.isEmpty()));
0472: SVNVersionedProperties propsDiff = baseProps
0473: .compareTo(props);
0474: boolean hasPropModifications = !propsDiff
0475: .isEmpty();
0476: command
0477: .put(
0478: SVNProperty.HAS_PROP_MODS,
0479: SVNProperty
0480: .toString(hasPropModifications));
0481: command.put(SVNLog.NAME_ATTR, name);
0482: log.addCommand(SVNLog.MODIFY_ENTRY, command,
0483: false);
0484: command.clear();
0485:
0486: if (!hasPropModifications) {
0487: String workingPropsPath = getThisDirName()
0488: .equals(name) ? "dir-props"
0489: : "props/" + name + ".svn-work";
0490: workingPropsPath = getAdminDirectory()
0491: .getName()
0492: + "/" + workingPropsPath;
0493: command.put(SVNLog.NAME_ATTR,
0494: workingPropsPath);
0495: log.addCommand(SVNLog.DELETE, command,
0496: false);
0497: command.clear();
0498: }
0499: }
0500:
0501: if (baseProps.isEmpty()) {
0502: command.put(SVNLog.NAME_ATTR, dstPath);
0503: log.addCommand(SVNLog.DELETE, command, false);
0504: } else {
0505: String tmpPath = "tmp/";
0506: tmpPath += getThisDirName().equals(name) ? "dir-prop-base"
0507: : "prop-base/" + name + ".svn-base";
0508: File tmpFile = getAdminFile(tmpPath);
0509: String srcPath = getAdminDirectory().getName()
0510: + "/" + tmpPath;
0511: SVNProperties tmpProps = new SVNProperties(
0512: tmpFile, srcPath);
0513: tmpProps.setProperties(baseProps.asMap());
0514:
0515: command.put(SVNLog.NAME_ATTR, srcPath);
0516: command.put(SVNLog.DEST_ATTR, dstPath);
0517: log.addCommand(SVNLog.MOVE, command, false);
0518: command.clear();
0519: command.put(SVNLog.NAME_ATTR, dstPath);
0520: log.addCommand(SVNLog.READONLY, command, false);
0521: }
0522: baseProps.setModified(false);
0523: }
0524: }
0525: }
0526:
0527: if (close) {
0528: closeVersionedProperties();
0529: }
0530: }
0531:
0532: public void saveEntries(boolean close) throws SVNException {
0533: if (myEntries != null) {
0534: if (!isLocked()) {
0535: SVNErrorMessage err = SVNErrorMessage.create(
0536: SVNErrorCode.WC_NOT_LOCKED,
0537: "No write-lock in ''{0}''", getRoot());
0538: SVNErrorManager.error(err);
0539: }
0540:
0541: SVNEntry rootEntry = (SVNEntry) myEntries
0542: .get(getThisDirName());
0543: if (rootEntry == null) {
0544: SVNErrorMessage err = SVNErrorMessage.create(
0545: SVNErrorCode.ENTRY_NOT_FOUND,
0546: "No default entry in directory ''{0}''",
0547: getRoot());
0548: SVNErrorManager.error(err);
0549: }
0550:
0551: String reposURL = rootEntry.getRepositoryRoot();
0552: String url = rootEntry.getURL();
0553: if (reposURL != null
0554: && !SVNPathUtil.isAncestor(reposURL, url)) {
0555: SVNErrorMessage err = SVNErrorMessage
0556: .create(
0557: SVNErrorCode.UNKNOWN,
0558: "Entry ''{0}'' has inconsistent repository root and url",
0559: getThisDirName());
0560: SVNErrorManager.error(err);
0561: }
0562:
0563: if (ourIsOptimizedWritingEnabled) {
0564: File tmpFile = new File(getAdminDirectory(),
0565: "tmp/entries");
0566: boolean renamed = myEntriesFile.renameTo(tmpFile);
0567: if (!renamed) {
0568: myEntriesFile.delete();
0569: tmpFile = null;
0570: }
0571: Writer os = null;
0572: try {
0573: os = new OutputStreamWriter(
0574: new BufferedOutputStream(
0575: new FileOutputStream(myEntriesFile)),
0576: "UTF-8");
0577: writeEntries(os);
0578: } catch (IOException e) {
0579: SVNFileUtil.closeFile(os);
0580: if (tmpFile != null) {
0581: tmpFile.renameTo(myEntriesFile);
0582: }
0583: SVNErrorMessage err = SVNErrorMessage.create(
0584: SVNErrorCode.IO_ERROR,
0585: "Cannot write entries file ''{0}'': {1}",
0586: new Object[] { myEntriesFile,
0587: e.getLocalizedMessage() });
0588: SVNErrorManager.error(err, e);
0589: } finally {
0590: SVNFileUtil.closeFile(os);
0591: SVNFileUtil.deleteFile(tmpFile);
0592: }
0593: myEntriesFile.setReadOnly();
0594: } else {
0595: File tmpFile = new File(getAdminDirectory(),
0596: "tmp/entries");
0597: Writer os = null;
0598: try {
0599: os = new OutputStreamWriter(
0600: new BufferedOutputStream(
0601: new FileOutputStream(tmpFile)),
0602: "UTF-8");
0603: writeEntries(os);
0604: } catch (IOException e) {
0605: SVNFileUtil.closeFile(os);
0606: SVNFileUtil.deleteFile(tmpFile);
0607: SVNErrorMessage err = SVNErrorMessage.create(
0608: SVNErrorCode.IO_ERROR,
0609: "Cannot write entries file ''{0}'': {1}",
0610: new Object[] { myEntriesFile,
0611: e.getMessage() });
0612: SVNErrorManager.error(err, e);
0613: } finally {
0614: SVNFileUtil.closeFile(os);
0615: }
0616: tmpFile.setReadOnly();
0617: SVNFileUtil.rename(tmpFile, myEntriesFile);
0618: }
0619: if (close) {
0620: closeEntries();
0621: }
0622: }
0623: }
0624:
0625: protected Map fetchEntries() throws SVNException {
0626: if (!myEntriesFile.exists()) {
0627: return null;
0628: }
0629:
0630: Map entries = new HashMap();
0631: BufferedReader reader = null;
0632: try {
0633: reader = new BufferedReader(new InputStreamReader(
0634: SVNFileUtil.openFileForReading(myEntriesFile),
0635: "UTF-8"));
0636: //skip format line
0637: reader.readLine();
0638: int entryNumber = 1;
0639: while (true) {
0640: try {
0641: SVNEntry entry = readEntry(reader, entryNumber);
0642: if (entry == null) {
0643: break;
0644: }
0645: entries.put(entry.getName(), entry);
0646: } catch (SVNException svne) {
0647: SVNErrorMessage err = svne
0648: .getErrorMessage()
0649: .wrap(
0650: "Error at entry {0,number,integer} in entries file for ''{1}'':",
0651: new Object[] {
0652: new Integer(entryNumber),
0653: getRoot() });
0654: SVNErrorManager.error(err);
0655: }
0656: ++entryNumber;
0657: }
0658: } catch (IOException e) {
0659: SVNErrorMessage err = SVNErrorMessage.create(
0660: SVNErrorCode.IO_ERROR,
0661: "Cannot read entries file ''{0}'': {1}",
0662: new Object[] { myEntriesFile, e.getMessage() });
0663: SVNErrorManager.error(err, e);
0664: } finally {
0665: SVNFileUtil.closeFile(reader);
0666: }
0667:
0668: SVNEntry defaultEntry = (SVNEntry) entries
0669: .get(getThisDirName());
0670: if (defaultEntry == null) {
0671: SVNErrorMessage err = SVNErrorMessage.create(
0672: SVNErrorCode.ENTRY_NOT_FOUND,
0673: "Missing default entry");
0674: SVNErrorManager.error(err);
0675: }
0676:
0677: Map defaultEntryAttrs = defaultEntry.asMap();
0678: if (defaultEntryAttrs.get(SVNProperty.REVISION) == null) {
0679: SVNErrorMessage err = SVNErrorMessage.create(
0680: SVNErrorCode.ENTRY_MISSING_REVISION,
0681: "Default entry has no revision number");
0682: SVNErrorManager.error(err);
0683: }
0684:
0685: if (defaultEntryAttrs.get(SVNProperty.URL) == null) {
0686: SVNErrorMessage err = SVNErrorMessage.create(
0687: SVNErrorCode.ENTRY_MISSING_URL,
0688: "Default entry is missing URL");
0689: SVNErrorManager.error(err);
0690: }
0691:
0692: for (Iterator entriesIter = entries.keySet().iterator(); entriesIter
0693: .hasNext();) {
0694: String name = (String) entriesIter.next();
0695: SVNEntry entry = (SVNEntry) entries.get(name);
0696: if (getThisDirName().equals(name)) {
0697: continue;
0698: }
0699:
0700: Map entryAttributes = entry.asMap();
0701: SVNNodeKind kind = SVNNodeKind
0702: .parseKind((String) entryAttributes
0703: .get(SVNProperty.KIND));
0704: if (kind == SVNNodeKind.FILE) {
0705: if (entryAttributes.get(SVNProperty.REVISION) == null
0706: || Long.parseLong((String) entryAttributes
0707: .get(SVNProperty.REVISION), 10) < 0) {
0708: entryAttributes
0709: .put(SVNProperty.REVISION,
0710: defaultEntryAttrs
0711: .get(SVNProperty.REVISION));
0712: }
0713: if (entryAttributes.get(SVNProperty.URL) == null) {
0714: String rootURL = (String) defaultEntryAttrs
0715: .get(SVNProperty.URL);
0716: String url = SVNPathUtil.append(rootURL,
0717: SVNEncodingUtil.uriEncode(name));
0718: entryAttributes.put(SVNProperty.URL, url);
0719: }
0720: if (entryAttributes.get(SVNProperty.REPOS) == null) {
0721: entryAttributes.put(SVNProperty.REPOS,
0722: defaultEntryAttrs.get(SVNProperty.REPOS));
0723: }
0724: if (entryAttributes.get(SVNProperty.UUID) == null) {
0725: String schedule = (String) entryAttributes
0726: .get(SVNProperty.SCHEDULE);
0727: if (!(SVNProperty.SCHEDULE_ADD.equals(schedule) || SVNProperty.SCHEDULE_REPLACE
0728: .equals(schedule))) {
0729: entryAttributes
0730: .put(SVNProperty.UUID,
0731: defaultEntryAttrs
0732: .get(SVNProperty.UUID));
0733: }
0734: }
0735: if (entryAttributes.get(SVNProperty.CACHABLE_PROPS) == null) {
0736: entryAttributes.put(SVNProperty.CACHABLE_PROPS,
0737: defaultEntryAttrs
0738: .get(SVNProperty.CACHABLE_PROPS));
0739: }
0740: }
0741: }
0742: return entries;
0743: }
0744:
0745: private SVNEntry readEntry(BufferedReader reader, int entryNumber)
0746: throws IOException, SVNException {
0747: String line = reader.readLine();
0748: if (line == null && entryNumber > 1) {
0749: return null;
0750: }
0751:
0752: String name = parseString(line);
0753: name = name != null ? name : getThisDirName();
0754:
0755: Map entryAttrs = new HashMap();
0756: entryAttrs.put(SVNProperty.NAME, name);
0757: SVNEntry entry = new SVNEntry(entryAttrs, this , name);
0758:
0759: line = reader.readLine();
0760: String kind = parseValue(line);
0761: if (kind != null) {
0762: SVNNodeKind parsedKind = SVNNodeKind.parseKind(kind);
0763: if (parsedKind != SVNNodeKind.UNKNOWN
0764: && parsedKind != SVNNodeKind.NONE) {
0765: entryAttrs.put(SVNProperty.KIND, kind);
0766: } else {
0767: SVNErrorMessage err = SVNErrorMessage.create(
0768: SVNErrorCode.NODE_UNKNOWN_KIND,
0769: "Entry ''{0}'' has invalid node kind", name);
0770: SVNErrorManager.error(err);
0771: }
0772: } else {
0773: entryAttrs.put(SVNProperty.KIND, SVNNodeKind.NONE
0774: .toString());
0775: }
0776:
0777: line = reader.readLine();
0778: if (isEntryFinished(line)) {
0779: return entry;
0780: }
0781: String revision = parseValue(line);
0782: if (revision != null) {
0783: entryAttrs.put(SVNProperty.REVISION, revision);
0784: }
0785:
0786: line = reader.readLine();
0787: if (isEntryFinished(line)) {
0788: return entry;
0789: }
0790: String url = parseString(line);
0791: if (url != null) {
0792: entryAttrs.put(SVNProperty.URL, url);
0793: }
0794:
0795: line = reader.readLine();
0796: if (isEntryFinished(line)) {
0797: return entry;
0798: }
0799: String reposRoot = parseString(line);
0800: if (reposRoot != null && url != null
0801: && !SVNPathUtil.isAncestor(reposRoot, url)) {
0802: SVNErrorMessage err = SVNErrorMessage.create(
0803: SVNErrorCode.WC_CORRUPT,
0804: "Entry for ''{0}'' has invalid repository root",
0805: name);
0806: SVNErrorManager.error(err);
0807: } else if (reposRoot != null) {
0808: entryAttrs.put(SVNProperty.REPOS, reposRoot);
0809: }
0810:
0811: line = reader.readLine();
0812: if (isEntryFinished(line)) {
0813: return entry;
0814: }
0815: String schedule = parseValue(line);
0816: if (schedule != null) {
0817: if (SVNProperty.SCHEDULE_ADD.equals(schedule)
0818: || SVNProperty.SCHEDULE_DELETE.equals(schedule)
0819: || SVNProperty.SCHEDULE_REPLACE.equals(schedule)) {
0820: entryAttrs.put(SVNProperty.SCHEDULE, schedule);
0821: } else {
0822: SVNErrorMessage err = SVNErrorMessage.create(
0823: SVNErrorCode.ENTRY_ATTRIBUTE_INVALID,
0824: "Entry ''{0}'' has invalid ''{1}'' value",
0825: new Object[] { name, SVNProperty.SCHEDULE });
0826: SVNErrorManager.error(err);
0827: }
0828: }
0829:
0830: line = reader.readLine();
0831: if (isEntryFinished(line)) {
0832: return entry;
0833: }
0834: String timestamp = parseValue(line);
0835: if (timestamp != null) {
0836: entryAttrs.put(SVNProperty.TEXT_TIME, timestamp);
0837: }
0838:
0839: line = reader.readLine();
0840: if (isEntryFinished(line)) {
0841: return entry;
0842: }
0843: String checksum = parseString(line);
0844: if (checksum != null) {
0845: entryAttrs.put(SVNProperty.CHECKSUM, checksum);
0846: }
0847:
0848: line = reader.readLine();
0849: if (isEntryFinished(line)) {
0850: return entry;
0851: }
0852: String committedDate = parseValue(line);
0853: if (committedDate != null) {
0854: entryAttrs.put(SVNProperty.COMMITTED_DATE, committedDate);
0855: }
0856:
0857: line = reader.readLine();
0858: if (isEntryFinished(line)) {
0859: return entry;
0860: }
0861: String committedRevision = parseValue(line);
0862: if (committedRevision != null) {
0863: entryAttrs.put(SVNProperty.COMMITTED_REVISION,
0864: committedRevision);
0865: }
0866:
0867: line = reader.readLine();
0868: if (isEntryFinished(line)) {
0869: return entry;
0870: }
0871: String committedAuthor = parseString(line);
0872: if (committedAuthor != null) {
0873: entryAttrs.put(SVNProperty.LAST_AUTHOR, committedAuthor);
0874: }
0875:
0876: line = reader.readLine();
0877: if (isEntryFinished(line)) {
0878: return entry;
0879: }
0880: boolean hasProps = parseBoolean(line, SVNProperty.HAS_PROPS);
0881: if (hasProps) {
0882: entryAttrs.put(SVNProperty.HAS_PROPS, SVNProperty
0883: .toString(hasProps));
0884: }
0885:
0886: line = reader.readLine();
0887: if (isEntryFinished(line)) {
0888: return entry;
0889: }
0890: boolean hasPropMods = parseBoolean(line,
0891: SVNProperty.HAS_PROP_MODS);
0892: if (hasPropMods) {
0893: entryAttrs.put(SVNProperty.HAS_PROP_MODS, SVNProperty
0894: .toString(hasPropMods));
0895: }
0896:
0897: line = reader.readLine();
0898: if (isEntryFinished(line)) {
0899: return entry;
0900: }
0901: String cachablePropsStr = parseValue(line);
0902: if (cachablePropsStr != null) {
0903: String[] cachableProps = fromString(cachablePropsStr, " ");
0904: entryAttrs.put(SVNProperty.CACHABLE_PROPS, cachableProps);
0905: }
0906:
0907: line = reader.readLine();
0908: if (isEntryFinished(line)) {
0909: return entry;
0910: }
0911: String presentPropsStr = parseValue(line);
0912: if (presentPropsStr != null) {
0913: String[] presentProps = fromString(presentPropsStr, " ");
0914: entryAttrs.put(SVNProperty.PRESENT_PROPS, presentProps);
0915: }
0916:
0917: line = reader.readLine();
0918: if (isEntryFinished(line)) {
0919: return entry;
0920: }
0921: String prejFile = parseString(line);
0922: if (prejFile != null) {
0923: entryAttrs.put(SVNProperty.PROP_REJECT_FILE, prejFile);
0924: }
0925:
0926: line = reader.readLine();
0927: if (isEntryFinished(line)) {
0928: return entry;
0929: }
0930: String conflictOldFile = parseString(line);
0931: if (conflictOldFile != null) {
0932: entryAttrs.put(SVNProperty.CONFLICT_OLD, conflictOldFile);
0933: }
0934:
0935: line = reader.readLine();
0936: if (isEntryFinished(line)) {
0937: return entry;
0938: }
0939: String conflictNewFile = parseString(line);
0940: if (conflictNewFile != null) {
0941: entryAttrs.put(SVNProperty.CONFLICT_NEW, conflictNewFile);
0942: }
0943:
0944: line = reader.readLine();
0945: if (isEntryFinished(line)) {
0946: return entry;
0947: }
0948: String conflictWorkFile = parseString(line);
0949: if (conflictWorkFile != null) {
0950: entryAttrs.put(SVNProperty.CONFLICT_WRK, conflictWorkFile);
0951: }
0952:
0953: line = reader.readLine();
0954: if (isEntryFinished(line)) {
0955: return entry;
0956: }
0957: boolean isCopied = parseBoolean(line, ATTRIBUTE_COPIED);
0958: if (isCopied) {
0959: entryAttrs.put(SVNProperty.COPIED, SVNProperty
0960: .toString(isCopied));
0961: }
0962:
0963: line = reader.readLine();
0964: if (isEntryFinished(line)) {
0965: return entry;
0966: }
0967: String copyfromURL = parseString(line);
0968: if (copyfromURL != null) {
0969: entryAttrs.put(SVNProperty.COPYFROM_URL, copyfromURL);
0970: }
0971:
0972: line = reader.readLine();
0973: if (isEntryFinished(line)) {
0974: return entry;
0975: }
0976: String copyfromRevision = parseValue(line);
0977: if (copyfromRevision != null) {
0978: entryAttrs.put(SVNProperty.COPYFROM_REVISION,
0979: copyfromRevision);
0980: }
0981:
0982: line = reader.readLine();
0983: if (isEntryFinished(line)) {
0984: return entry;
0985: }
0986: boolean isDeleted = parseBoolean(line, ATTRIBUTE_DELETED);
0987: if (isDeleted) {
0988: entryAttrs.put(SVNProperty.DELETED, SVNProperty
0989: .toString(isDeleted));
0990: }
0991:
0992: line = reader.readLine();
0993: if (isEntryFinished(line)) {
0994: return entry;
0995: }
0996: boolean isAbsent = parseBoolean(line, ATTRIBUTE_ABSENT);
0997: if (isAbsent) {
0998: entryAttrs.put(SVNProperty.ABSENT, SVNProperty
0999: .toString(isAbsent));
1000: }
1001:
1002: line = reader.readLine();
1003: if (isEntryFinished(line)) {
1004: return entry;
1005: }
1006: boolean isIncomplete = parseBoolean(line, ATTRIBUTE_INCOMPLETE);
1007: if (isIncomplete) {
1008: entryAttrs.put(SVNProperty.INCOMPLETE, SVNProperty
1009: .toString(isIncomplete));
1010: }
1011:
1012: line = reader.readLine();
1013: if (isEntryFinished(line)) {
1014: return entry;
1015: }
1016: String uuid = parseString(line);
1017: if (uuid != null) {
1018: entryAttrs.put(SVNProperty.UUID, uuid);
1019: }
1020:
1021: line = reader.readLine();
1022: if (isEntryFinished(line)) {
1023: return entry;
1024: }
1025: String lockToken = parseString(line);
1026: if (lockToken != null) {
1027: entryAttrs.put(SVNProperty.LOCK_TOKEN, lockToken);
1028: }
1029:
1030: line = reader.readLine();
1031: if (isEntryFinished(line)) {
1032: return entry;
1033: }
1034: String lockOwner = parseString(line);
1035: if (lockOwner != null) {
1036: entryAttrs.put(SVNProperty.LOCK_OWNER, lockOwner);
1037: }
1038:
1039: line = reader.readLine();
1040: if (isEntryFinished(line)) {
1041: return entry;
1042: }
1043: String lockComment = parseString(line);
1044: if (lockComment != null) {
1045: entryAttrs.put(SVNProperty.LOCK_COMMENT, lockComment);
1046: }
1047:
1048: line = reader.readLine();
1049: if (isEntryFinished(line)) {
1050: return entry;
1051: }
1052: String lockCreationDate = parseValue(line);
1053: if (lockCreationDate != null) {
1054: entryAttrs.put(SVNProperty.LOCK_CREATION_DATE,
1055: lockCreationDate);
1056: }
1057:
1058: do {
1059: line = reader.readLine();
1060: if (line == null) {
1061: SVNErrorMessage err = SVNErrorMessage.create(
1062: SVNErrorCode.WC_CORRUPT,
1063: "Missing entry terminator");
1064: SVNErrorManager.error(err);
1065: } else if (line.length() == 1 && line.charAt(0) == '\f') {
1066: break;
1067: }
1068: } while (line != null);
1069:
1070: return entry;
1071: }
1072:
1073: private boolean isEntryFinished(String line) {
1074: return line != null && line.length() > 0
1075: && line.charAt(0) == '\f';
1076: }
1077:
1078: private boolean parseBoolean(String line, String field)
1079: throws SVNException {
1080: line = parseValue(line);
1081: if (line != null) {
1082: if (!line.equals(field)) {
1083: SVNErrorMessage err = SVNErrorMessage.create(
1084: SVNErrorCode.WC_CORRUPT,
1085: "Invalid value for field ''{0}''", field);
1086: SVNErrorManager.error(err);
1087: }
1088: return true;
1089: }
1090: return false;
1091: }
1092:
1093: private String parseString(String line) throws SVNException {
1094: if (line == null) {
1095: SVNErrorMessage err = SVNErrorMessage.create(
1096: SVNErrorCode.WC_CORRUPT, "Unexpected end of entry");
1097: SVNErrorManager.error(err);
1098: } else if ("".equals(line)) {
1099: return null;
1100: }
1101:
1102: int fromIndex = 0;
1103: int ind = -1;
1104: StringBuffer buffer = null;
1105: String escapedString = null;
1106: while ((ind = line.indexOf('\\', fromIndex)) != -1) {
1107: if (line.length() < ind + 4
1108: || line.charAt(ind + 1) != 'x'
1109: || !SVNEncodingUtil
1110: .isHexDigit(line.charAt(ind + 2))
1111: || !SVNEncodingUtil
1112: .isHexDigit(line.charAt(ind + 3))) {
1113: SVNErrorMessage err = SVNErrorMessage.create(
1114: SVNErrorCode.WC_CORRUPT,
1115: "Invalid escape sequence");
1116: SVNErrorManager.error(err);
1117: }
1118: if (buffer == null) {
1119: buffer = new StringBuffer();
1120: }
1121:
1122: escapedString = line.substring(ind + 2, ind + 4);
1123: int escapedByte = Integer.parseInt(escapedString, 16);
1124:
1125: if (ind > fromIndex) {
1126: buffer.append(line.substring(fromIndex, ind));
1127: buffer.append((char) (escapedByte & 0xFF));
1128: } else {
1129: buffer.append((char) (escapedByte & 0xFF));
1130: }
1131: fromIndex = ind + 4;
1132: }
1133:
1134: if (buffer != null) {
1135: if (fromIndex < line.length()) {
1136: buffer.append(line.substring(fromIndex));
1137: }
1138: return buffer.toString();
1139: }
1140: return line;
1141: }
1142:
1143: private String parseValue(String line) throws SVNException {
1144: if (line == null) {
1145: SVNErrorMessage err = SVNErrorMessage.create(
1146: SVNErrorCode.WC_CORRUPT, "Unexpected end of entry");
1147: SVNErrorManager.error(err);
1148: } else if ("".equals(line)) {
1149: return null;
1150: }
1151: return line;
1152: }
1153:
1154: public String getThisDirName() {
1155: return THIS_DIR;
1156: }
1157:
1158: protected void writeEntries(Writer writer) throws IOException {
1159: SVNEntry rootEntry = (SVNEntry) myEntries.get(getThisDirName());
1160: writer.write(getFormatVersion() + "\n");
1161: writeEntry(writer, getThisDirName(), rootEntry.asMap(), null);
1162:
1163: List names = new ArrayList(myEntries.keySet());
1164: Collections.sort(names);
1165: for (Iterator entries = names.iterator(); entries.hasNext();) {
1166: String name = (String) entries.next();
1167: SVNEntry entry = (SVNEntry) myEntries.get(name);
1168: if (getThisDirName().equals(name)) {
1169: continue;
1170: }
1171:
1172: Map entryAttributes = entry.asMap();
1173: Map defaultEntryAttrs = rootEntry.asMap();
1174: SVNNodeKind kind = SVNNodeKind
1175: .parseKind((String) entryAttributes
1176: .get(SVNProperty.KIND));
1177: if (kind == SVNNodeKind.FILE) {
1178: if (entryAttributes.get(SVNProperty.REVISION) == null
1179: || Long.parseLong((String) entryAttributes
1180: .get(SVNProperty.REVISION), 10) < 0) {
1181: entryAttributes
1182: .put(SVNProperty.REVISION,
1183: defaultEntryAttrs
1184: .get(SVNProperty.REVISION));
1185: }
1186: if (entryAttributes.get(SVNProperty.URL) == null) {
1187: String rootURL = (String) defaultEntryAttrs
1188: .get(SVNProperty.URL);
1189: String url = SVNPathUtil.append(rootURL,
1190: SVNEncodingUtil.uriEncode(name));
1191: entryAttributes.put(SVNProperty.URL, url);
1192: }
1193: if (entryAttributes.get(SVNProperty.REPOS) == null) {
1194: entryAttributes.put(SVNProperty.REPOS,
1195: defaultEntryAttrs.get(SVNProperty.REPOS));
1196: }
1197: if (entryAttributes.get(SVNProperty.UUID) == null) {
1198: String schedule = (String) entryAttributes
1199: .get(SVNProperty.SCHEDULE);
1200: if (!(SVNProperty.SCHEDULE_ADD.equals(schedule) || SVNProperty.SCHEDULE_REPLACE
1201: .equals(schedule))) {
1202: entryAttributes
1203: .put(SVNProperty.UUID,
1204: defaultEntryAttrs
1205: .get(SVNProperty.UUID));
1206: }
1207: }
1208: if (entryAttributes.get(SVNProperty.CACHABLE_PROPS) == null) {
1209: entryAttributes.put(SVNProperty.CACHABLE_PROPS,
1210: defaultEntryAttrs
1211: .get(SVNProperty.CACHABLE_PROPS));
1212: }
1213: }
1214:
1215: writeEntry(writer, name, entryAttributes, rootEntry.asMap());
1216: }
1217: }
1218:
1219: private void writeEntry(Writer writer, String name, Map entry,
1220: Map rootEntry) throws IOException {
1221: boolean isThisDir = getThisDirName().equals(name);
1222: boolean isSubDir = !isThisDir
1223: && SVNProperty.KIND_DIR.equals(entry
1224: .get(SVNProperty.KIND));
1225: int emptyFields = 0;
1226:
1227: if (!writeString(writer, name, emptyFields)) {
1228: ++emptyFields;
1229: }
1230:
1231: String kind = (String) entry.get(SVNProperty.KIND);
1232: if (writeValue(writer, kind, emptyFields)) {
1233: emptyFields = 0;
1234: } else {
1235: ++emptyFields;
1236: }
1237:
1238: String revision = null;
1239: if (isThisDir) {
1240: revision = (String) entry.get(SVNProperty.REVISION);
1241: } else if (!isSubDir) {
1242: revision = (String) entry.get(SVNProperty.REVISION);
1243: if (revision != null
1244: && revision.equals(rootEntry
1245: .get(SVNProperty.REVISION))) {
1246: revision = null;
1247: }
1248: }
1249: if (writeRevision(writer, revision, emptyFields)) {
1250: emptyFields = 0;
1251: } else {
1252: ++emptyFields;
1253: }
1254:
1255: String url = null;
1256: if (isThisDir) {
1257: url = (String) entry.get(SVNProperty.URL);
1258: } else if (!isSubDir) {
1259: url = (String) entry.get(SVNProperty.URL);
1260: String expectedURL = SVNPathUtil.append((String) rootEntry
1261: .get(SVNProperty.URL), name);
1262: if (url != null && url.equals(expectedURL)) {
1263: url = null;
1264: }
1265: }
1266: if (writeString(writer, url, emptyFields)) {
1267: emptyFields = 0;
1268: } else {
1269: ++emptyFields;
1270: }
1271:
1272: String root = null;
1273: if (isThisDir) {
1274: root = (String) entry.get(SVNProperty.REPOS);
1275: } else if (!isSubDir) {
1276: String this DirRoot = (String) rootEntry
1277: .get(SVNProperty.REPOS);
1278: root = (String) entry.get(SVNProperty.REPOS);
1279: if (root != null && root.equals(this DirRoot)) {
1280: root = null;
1281: }
1282: }
1283: if (writeString(writer, root, emptyFields)) {
1284: emptyFields = 0;
1285: } else {
1286: ++emptyFields;
1287: }
1288:
1289: String schedule = (String) entry.get(SVNProperty.SCHEDULE);
1290: if (schedule != null
1291: && (!SVNProperty.SCHEDULE_ADD.equals(schedule)
1292: && !SVNProperty.SCHEDULE_DELETE
1293: .equals(schedule) && !SVNProperty.SCHEDULE_REPLACE
1294: .equals(schedule))) {
1295: schedule = null;
1296: }
1297: if (writeValue(writer, schedule, emptyFields)) {
1298: emptyFields = 0;
1299: } else {
1300: ++emptyFields;
1301: }
1302:
1303: String textTime = (String) entry.get(SVNProperty.TEXT_TIME);
1304: if (writeValue(writer, textTime, emptyFields)) {
1305: emptyFields = 0;
1306: } else {
1307: ++emptyFields;
1308: }
1309:
1310: String checksum = (String) entry.get(SVNProperty.CHECKSUM);
1311: if (writeValue(writer, checksum, emptyFields)) {
1312: emptyFields = 0;
1313: } else {
1314: ++emptyFields;
1315: }
1316:
1317: String committedDate = (String) entry
1318: .get(SVNProperty.COMMITTED_DATE);
1319: if (writeValue(writer, committedDate, emptyFields)) {
1320: emptyFields = 0;
1321: } else {
1322: ++emptyFields;
1323: }
1324:
1325: String committedRevision = (String) entry
1326: .get(SVNProperty.COMMITTED_REVISION);
1327: if (writeRevision(writer, committedRevision, emptyFields)) {
1328: emptyFields = 0;
1329: } else {
1330: ++emptyFields;
1331: }
1332:
1333: String committedAuthor = (String) entry
1334: .get(SVNProperty.LAST_AUTHOR);
1335: if (writeString(writer, committedAuthor, emptyFields)) {
1336: emptyFields = 0;
1337: } else {
1338: ++emptyFields;
1339: }
1340:
1341: String hasProps = (String) entry.get(SVNProperty.HAS_PROPS);
1342: if (SVNProperty.booleanValue(hasProps)) {
1343: writeValue(writer, SVNProperty.HAS_PROPS, emptyFields);
1344: emptyFields = 0;
1345: } else {
1346: ++emptyFields;
1347: }
1348:
1349: String hasPropMods = (String) entry
1350: .get(SVNProperty.HAS_PROP_MODS);
1351: if (SVNProperty.booleanValue(hasPropMods)) {
1352: writeValue(writer, SVNProperty.HAS_PROP_MODS, emptyFields);
1353: emptyFields = 0;
1354: } else {
1355: ++emptyFields;
1356: }
1357:
1358: String cachableProps = asString((String[]) entry
1359: .get(SVNProperty.CACHABLE_PROPS), " ");
1360: if (!isThisDir) {
1361: String this DirCachableProps = asString((String[]) rootEntry
1362: .get(SVNProperty.CACHABLE_PROPS), " ");
1363: if (this DirCachableProps != null && cachableProps != null
1364: && this DirCachableProps.equals(cachableProps)) {
1365: cachableProps = null;
1366: }
1367: }
1368: if (writeValue(writer, cachableProps, emptyFields)) {
1369: emptyFields = 0;
1370: } else {
1371: ++emptyFields;
1372: }
1373:
1374: String presentProps = asString((String[]) entry
1375: .get(SVNProperty.PRESENT_PROPS), " ");
1376: if (writeValue(writer, presentProps, emptyFields)) {
1377: emptyFields = 0;
1378: } else {
1379: ++emptyFields;
1380: }
1381:
1382: String propRejectFile = (String) entry
1383: .get(SVNProperty.PROP_REJECT_FILE);
1384: if (writeString(writer, propRejectFile, emptyFields)) {
1385: emptyFields = 0;
1386: } else {
1387: ++emptyFields;
1388: }
1389:
1390: String conflictOldFile = (String) entry
1391: .get(SVNProperty.CONFLICT_OLD);
1392: if (writeString(writer, conflictOldFile, emptyFields)) {
1393: emptyFields = 0;
1394: } else {
1395: ++emptyFields;
1396: }
1397:
1398: String conflictNewFile = (String) entry
1399: .get(SVNProperty.CONFLICT_NEW);
1400: if (writeString(writer, conflictNewFile, emptyFields)) {
1401: emptyFields = 0;
1402: } else {
1403: ++emptyFields;
1404: }
1405:
1406: String conflictWrkFile = (String) entry
1407: .get(SVNProperty.CONFLICT_WRK);
1408: if (writeString(writer, conflictWrkFile, emptyFields)) {
1409: emptyFields = 0;
1410: } else {
1411: ++emptyFields;
1412: }
1413:
1414: String copiedAttr = (String) entry.get(SVNProperty.COPIED);
1415: if (SVNProperty.booleanValue(copiedAttr)) {
1416: writeValue(writer, ATTRIBUTE_COPIED, emptyFields);
1417: emptyFields = 0;
1418: } else {
1419: ++emptyFields;
1420: }
1421:
1422: String copyfromURL = (String) entry
1423: .get(SVNProperty.COPYFROM_URL);
1424: if (writeString(writer, copyfromURL, emptyFields)) {
1425: emptyFields = 0;
1426: } else {
1427: ++emptyFields;
1428: }
1429:
1430: String copyfromRevision = (String) entry
1431: .get(SVNProperty.COPYFROM_REVISION);
1432: if (writeRevision(writer, copyfromRevision, emptyFields)) {
1433: emptyFields = 0;
1434: } else {
1435: ++emptyFields;
1436: }
1437:
1438: String deletedAttr = (String) entry.get(SVNProperty.DELETED);
1439: if (SVNProperty.booleanValue(deletedAttr)) {
1440: writeValue(writer, ATTRIBUTE_DELETED, emptyFields);
1441: emptyFields = 0;
1442: } else {
1443: ++emptyFields;
1444: }
1445:
1446: String absentAttr = (String) entry.get(SVNProperty.ABSENT);
1447: if (SVNProperty.booleanValue(absentAttr)) {
1448: writeValue(writer, ATTRIBUTE_ABSENT, emptyFields);
1449: emptyFields = 0;
1450: } else {
1451: ++emptyFields;
1452: }
1453:
1454: String incompleteAttr = (String) entry
1455: .get(SVNProperty.INCOMPLETE);
1456: if (SVNProperty.booleanValue(incompleteAttr)) {
1457: writeValue(writer, ATTRIBUTE_INCOMPLETE, emptyFields);
1458: emptyFields = 0;
1459: } else {
1460: ++emptyFields;
1461: }
1462:
1463: String uuid = (String) entry.get(SVNProperty.UUID);
1464: if (!isThisDir) {
1465: String this DirUUID = (String) rootEntry
1466: .get(SVNProperty.UUID);
1467: if (this DirUUID != null && uuid != null
1468: && this DirUUID.equals(uuid)) {
1469: uuid = null;
1470: }
1471: }
1472: if (writeValue(writer, uuid, emptyFields)) {
1473: emptyFields = 0;
1474: } else {
1475: ++emptyFields;
1476: }
1477:
1478: String lockToken = (String) entry.get(SVNProperty.LOCK_TOKEN);
1479: if (writeString(writer, lockToken, emptyFields)) {
1480: emptyFields = 0;
1481: } else {
1482: ++emptyFields;
1483: }
1484:
1485: String lockOwner = (String) entry.get(SVNProperty.LOCK_OWNER);
1486: if (writeString(writer, lockOwner, emptyFields)) {
1487: emptyFields = 0;
1488: } else {
1489: ++emptyFields;
1490: }
1491:
1492: String lockComment = (String) entry
1493: .get(SVNProperty.LOCK_COMMENT);
1494: if (writeString(writer, lockComment, emptyFields)) {
1495: emptyFields = 0;
1496: } else {
1497: ++emptyFields;
1498: }
1499:
1500: String lockCreationDate = (String) entry
1501: .get(SVNProperty.LOCK_CREATION_DATE);
1502: writeValue(writer, lockCreationDate, emptyFields);
1503: writer.write("\f\n");
1504: writer.flush();
1505: }
1506:
1507: private boolean writeString(Writer writer, String str,
1508: int emptyFields) throws IOException {
1509: if (str != null && str.length() > 0) {
1510: for (int i = 0; i < emptyFields; i++) {
1511: writer.write('\n');
1512: }
1513: for (int i = 0; i < str.length(); i++) {
1514: char ch = str.charAt(i);
1515: if (SVNEncodingUtil.isASCIIControlChar(ch)
1516: || ch == '\\') {
1517: writer.write("\\x");
1518: writer.write(SVNFormatUtil
1519: .getHexNumberFromByte((byte) ch));
1520: } else {
1521: writer.write(ch);
1522: }
1523: }
1524: writer.write('\n');
1525: return true;
1526: }
1527: return false;
1528: }
1529:
1530: private boolean writeValue(Writer writer, String val,
1531: int emptyFields) throws IOException {
1532: if (val != null && val.length() > 0) {
1533: for (int i = 0; i < emptyFields; i++) {
1534: writer.write('\n');
1535: }
1536: writer.write(val);
1537: writer.write('\n');
1538: return true;
1539: }
1540: return false;
1541: }
1542:
1543: private boolean writeRevision(Writer writer, String rev,
1544: int emptyFields) throws IOException {
1545: if (rev != null && rev.length() > 0 && Long.parseLong(rev) >= 0) {
1546: for (int i = 0; i < emptyFields; i++) {
1547: writer.write('\n');
1548: }
1549: writer.write(rev);
1550: writer.write('\n');
1551: return true;
1552: }
1553: return false;
1554: }
1555:
1556: public boolean hasPropModifications(String name)
1557: throws SVNException {
1558: SVNEntry entry = getEntry(name, true);
1559: if (entry != null) {
1560: Map entryAttrs = entry.asMap();
1561: return SVNProperty.booleanValue((String) entryAttrs
1562: .get(SVNProperty.HAS_PROP_MODS));
1563: }
1564: return false;
1565: }
1566:
1567: public boolean hasProperties(String name) throws SVNException {
1568: SVNEntry entry = getEntry(name, true);
1569: if (entry != null) {
1570: Map entryAttrs = entry.asMap();
1571: return SVNProperty.booleanValue((String) entryAttrs
1572: .get(SVNProperty.HAS_PROPS));
1573: }
1574: return false;
1575: }
1576:
1577: public boolean lock() throws SVNException {
1578: return lock(false);
1579: }
1580:
1581: public boolean lock(boolean stealLock) throws SVNException {
1582: if (!isVersioned()) {
1583: return false;
1584: }
1585: if (stealLock && myLockFile.isFile()) {
1586: setLocked(true);
1587: return true;
1588: }
1589: boolean created = false;
1590: try {
1591: created = myLockFile.createNewFile();
1592: } catch (IOException e) {
1593: SVNErrorCode code = e.getMessage().indexOf("denied") >= 0 ? SVNErrorCode.WC_LOCKED
1594: : SVNErrorCode.WC_NOT_LOCKED;
1595: SVNErrorMessage err = SVNErrorMessage
1596: .create(code,
1597: "Cannot lock working copy ''{0}'': {1}",
1598: new Object[] { getRoot(),
1599: e.getLocalizedMessage() });
1600: SVNErrorManager.error(err, e);
1601: }
1602: if (created) {
1603: setLocked(true);
1604: return created;
1605: }
1606: if (myLockFile.isFile()) {
1607: SVNErrorMessage err = SVNErrorMessage
1608: .create(
1609: SVNErrorCode.WC_LOCKED,
1610: "Working copy ''{0}'' locked; try performing ''cleanup''",
1611: getRoot());
1612: SVNErrorManager.error(err);
1613: } else {
1614: SVNErrorMessage err = SVNErrorMessage.create(
1615: SVNErrorCode.WC_NOT_LOCKED,
1616: "Cannot lock working copy ''{0}''", getRoot());
1617: SVNErrorManager.error(err);
1618: }
1619: return false;
1620: }
1621:
1622: private void createFormatFile(File formatFile, boolean createMyself)
1623: throws SVNException {
1624: OutputStream os = null;
1625: try {
1626: formatFile = createMyself ? getAdminFile("format")
1627: : formatFile;
1628: os = SVNFileUtil.openFileForWriting(formatFile);
1629: os.write(String.valueOf(WC_FORMAT).getBytes("UTF-8"));
1630: os.write('\n');
1631: } catch (IOException e) {
1632: SVNErrorMessage err = SVNErrorMessage.create(
1633: SVNErrorCode.IO_ERROR, e.getLocalizedMessage());
1634: SVNErrorManager.error(err, e);
1635: } finally {
1636: SVNFileUtil.closeFile(os);
1637: }
1638: }
1639:
1640: public SVNAdminArea createVersionedDirectory(File dir, String url,
1641: String rootURL, String uuid, long revNumber,
1642: boolean createMyself) throws SVNException {
1643: dir = createMyself ? getRoot() : dir;
1644: dir.mkdirs();
1645: File adminDir = createMyself ? getAdminDirectory() : new File(
1646: dir, SVNFileUtil.getAdminDirectoryName());
1647: adminDir.mkdir();
1648: SVNFileUtil.setHidden(adminDir, true);
1649: // lock dir.
1650: File lockFile = createMyself ? myLockFile : new File(adminDir,
1651: "lock");
1652: SVNFileUtil.createEmptyFile(lockFile);
1653:
1654: File[] tmp = {
1655: createMyself ? getAdminFile("tmp") : new File(adminDir,
1656: "tmp"),
1657: createMyself ? getAdminFile("tmp" + File.separatorChar
1658: + "props") : new File(adminDir, "tmp"
1659: + File.separatorChar + "props"),
1660: createMyself ? getAdminFile("tmp" + File.separatorChar
1661: + "prop-base") : new File(adminDir, "tmp"
1662: + File.separatorChar + "prop-base"),
1663: createMyself ? getAdminFile("tmp" + File.separatorChar
1664: + "text-base") : new File(adminDir, "tmp"
1665: + File.separatorChar + "text-base"),
1666: createMyself ? getAdminFile("props") : new File(
1667: adminDir, "props"),
1668: createMyself ? getAdminFile("prop-base") : new File(
1669: adminDir, "prop-base"),
1670: createMyself ? getAdminFile("text-base") : new File(
1671: adminDir, "text-base") };
1672:
1673: for (int i = 0; i < tmp.length; i++) {
1674: tmp[i].mkdir();
1675: }
1676: // for backward compatibility
1677: createFormatFile(createMyself ? null : new File(adminDir,
1678: "format"), createMyself);
1679:
1680: SVNAdminArea adminArea = createMyself ? this
1681: : new SVNAdminArea14(dir);
1682: adminArea.setLocked(true);
1683: SVNEntry rootEntry = adminArea.getEntry(adminArea
1684: .getThisDirName(), true);
1685: if (rootEntry == null) {
1686: rootEntry = adminArea.addEntry(adminArea.getThisDirName());
1687: }
1688: if (url != null) {
1689: rootEntry.setURL(url);
1690: }
1691: rootEntry.setRepositoryRoot(rootURL);
1692: rootEntry.setRevision(revNumber);
1693: rootEntry.setKind(SVNNodeKind.DIR);
1694: if (uuid != null) {
1695: rootEntry.setUUID(uuid);
1696: }
1697: if (revNumber > 0) {
1698: rootEntry.setIncomplete(true);
1699: }
1700: rootEntry.setCachableProperties(ourCachableProperties);
1701: try {
1702: adminArea.saveEntries(false);
1703: } catch (SVNException svne) {
1704: SVNErrorMessage err = svne.getErrorMessage().wrap(
1705: "Error writing entries file for ''{0}''", dir);
1706: SVNErrorManager.error(err, svne);
1707: }
1708:
1709: // unlock dir.
1710: SVNFileUtil.deleteFile(lockFile);
1711: return adminArea;
1712: }
1713:
1714: public SVNAdminArea upgradeFormat(SVNAdminArea adminArea)
1715: throws SVNException {
1716: File logFile = adminArea.getAdminFile("log");
1717: SVNFileType type = SVNFileType.getType(logFile);
1718: if (type == SVNFileType.FILE) {
1719: SVNDebugLog.getDefaultLog().info(
1720: "Upgrade failed: found a log file at '" + logFile
1721: + "'");
1722: return adminArea;
1723: }
1724:
1725: SVNLog log = getLog();
1726: Map command = new HashMap();
1727: command.put(SVNLog.FORMAT_ATTR, String
1728: .valueOf(getFormatVersion()));
1729: log.addCommand(SVNLog.UPGRADE_FORMAT, command, false);
1730: command.clear();
1731:
1732: setWCAccess(adminArea.getWCAccess());
1733: Iterator entries = adminArea.entries(true);
1734: myEntries = new HashMap();
1735: Map basePropsCache = getBasePropertiesStorage(true);
1736: Map propsCache = getPropertiesStorage(true);
1737:
1738: for (; entries.hasNext();) {
1739: SVNEntry entry = (SVNEntry) entries.next();
1740: SVNEntry newEntry = new SVNEntry(
1741: new HashMap(entry.asMap()), this , entry.getName());
1742: myEntries.put(entry.getName(), newEntry);
1743:
1744: if (entry.getKind() != SVNNodeKind.FILE
1745: && !adminArea.getThisDirName().equals(
1746: entry.getName())) {
1747: continue;
1748: }
1749:
1750: SVNVersionedProperties srcBaseProps = adminArea
1751: .getBaseProperties(entry.getName());
1752: Map basePropsHolder = srcBaseProps.asMap();
1753: SVNVersionedProperties dstBaseProps = new SVNProperties13(
1754: basePropsHolder);
1755: basePropsCache.put(entry.getName(), dstBaseProps);
1756: dstBaseProps.setModified(true);
1757:
1758: SVNVersionedProperties srcProps = adminArea
1759: .getProperties(entry.getName());
1760: SVNVersionedProperties dstProps = new SVNProperties14(
1761: srcProps.asMap(), this , entry.getName()) {
1762:
1763: protected Map loadProperties() throws SVNException {
1764: return getPropertiesMap();
1765: }
1766: };
1767: propsCache.put(entry.getName(), dstProps);
1768: dstProps.setModified(true);
1769:
1770: command.put(SVNLog.NAME_ATTR, entry.getName());
1771: command.put(SVNProperty
1772: .shortPropertyName(SVNProperty.PROP_TIME),
1773: SVNTimeUtil.formatDate(new Date(0), true));
1774: log.addCommand(SVNLog.MODIFY_ENTRY, command, false);
1775: command.clear();
1776:
1777: SVNVersionedProperties wcProps = adminArea
1778: .getWCProperties(entry.getName());
1779: log
1780: .logChangedWCProperties(entry.getName(), wcProps
1781: .asMap());
1782: }
1783: saveVersionedProperties(log, true);
1784: log.save();
1785:
1786: SVNFileUtil.deleteFile(getAdminFile("README.txt"));
1787: SVNFileUtil.deleteFile(getAdminFile("empty-file"));
1788: SVNFileUtil.deleteAll(getAdminFile("wcprops"), true);
1789: SVNFileUtil.deleteAll(getAdminFile("tmp/wcprops"), true);
1790: SVNFileUtil.deleteAll(getAdminFile("dir-wcprops"), true);
1791:
1792: runLogs();
1793: return this ;
1794: }
1795:
1796: public void postUpgradeFormat(int format) throws SVNException {
1797: if (format == WC_FORMAT) {
1798: createFormatFile(null, true);
1799: return;
1800: }
1801: SVNErrorMessage err = SVNErrorMessage.create(
1802: SVNErrorCode.UNKNOWN, "Unexpected format number:\n"
1803: + " expected: {0,number,integer}\n"
1804: + " actual: {1,number,integer}",
1805: new Object[] { new Integer(WC_FORMAT),
1806: new Integer(format) });
1807: SVNErrorManager.error(err);
1808: }
1809:
1810: public void postCommit(String fileName, long revisionNumber,
1811: boolean implicit, SVNErrorCode errorCode)
1812: throws SVNException {
1813: SVNEntry entry = getEntry(fileName, true);
1814: if (entry == null
1815: || (!getThisDirName().equals(fileName) && entry
1816: .getKind() != SVNNodeKind.FILE)) {
1817: SVNErrorMessage err = SVNErrorMessage.create(errorCode,
1818: "Log command for directory ''{0}'' is mislocated",
1819: getRoot());
1820: SVNErrorManager.error(err);
1821: }
1822:
1823: if (!implicit && entry.isScheduledForDeletion()) {
1824: if (getThisDirName().equals(fileName)) {
1825: entry.setRevision(revisionNumber);
1826: entry.setKind(SVNNodeKind.DIR);
1827: File killMe = getAdminFile("KILLME");
1828: if (killMe.getParentFile().isDirectory()) {
1829: try {
1830: killMe.createNewFile();
1831: } catch (IOException e) {
1832: SVNErrorMessage err = SVNErrorMessage.create(
1833: SVNErrorCode.IO_ERROR,
1834: "Cannot create file ''{0}'': {1}",
1835: new Object[] { killMe,
1836: e.getLocalizedMessage() });
1837: SVNErrorManager.error(err, e);
1838: }
1839: }
1840: } else {
1841: removeFromRevisionControl(fileName, false, false);
1842: SVNEntry parentEntry = getEntry(getThisDirName(), true);
1843: if (revisionNumber > parentEntry.getRevision()) {
1844: SVNEntry fileEntry = addEntry(fileName);
1845: fileEntry.setKind(SVNNodeKind.FILE);
1846: fileEntry.setDeleted(true);
1847: fileEntry.setRevision(revisionNumber);
1848: }
1849: }
1850: return;
1851: }
1852:
1853: if (!implicit && entry.isScheduledForReplacement()
1854: && getThisDirName().equals(fileName)) {
1855: for (Iterator ents = entries(true); ents.hasNext();) {
1856: SVNEntry currentEntry = (SVNEntry) ents.next();
1857: if (!currentEntry.isScheduledForDeletion()) {
1858: continue;
1859: }
1860: if (currentEntry.getKind() == SVNNodeKind.FILE
1861: || currentEntry.getKind() == SVNNodeKind.DIR) {
1862: removeFromRevisionControl(currentEntry.getName(),
1863: false, false);
1864: }
1865: }
1866: }
1867:
1868: long textTime = 0;
1869: if (!implicit && !getThisDirName().equals(fileName)) {
1870: File tmpFile = getBaseFile(fileName, true);
1871: SVNFileType fileType = SVNFileType.getType(tmpFile);
1872: if (fileType == SVNFileType.FILE
1873: || fileType == SVNFileType.SYMLINK) {
1874: boolean modified = false;
1875: File workingFile = getFile(fileName);
1876: long tmpTimestamp = tmpFile.lastModified();
1877: long wkTimestamp = workingFile.lastModified();
1878: if (tmpTimestamp != wkTimestamp) {
1879: // check if wc file is not modified
1880: File tmpFile2 = SVNFileUtil.createUniqueFile(
1881: tmpFile.getParentFile(), fileName, ".tmp");
1882: try {
1883: String tmpFile2Path = SVNFileUtil
1884: .getBasePath(tmpFile2);
1885: SVNTranslator.translate(this , fileName,
1886: fileName, tmpFile2Path, false);
1887: modified = !SVNFileUtil.compareFiles(tmpFile,
1888: tmpFile2, null);
1889: } catch (SVNException svne) {
1890: SVNErrorMessage err = SVNErrorMessage.create(
1891: errorCode,
1892: "Error comparing ''{0}'' and ''{1}''",
1893: new Object[] { workingFile, tmpFile });
1894: SVNErrorManager.error(err, svne);
1895: } finally {
1896: tmpFile2.delete();
1897: }
1898: }
1899:
1900: textTime = modified ? tmpTimestamp : wkTimestamp;
1901: }
1902: }
1903: if (!implicit && entry.isScheduledForReplacement()) {
1904: SVNFileUtil.deleteFile(getBasePropertiesFile(fileName,
1905: false));
1906: }
1907:
1908: boolean setReadWrite = false;
1909: boolean setNotExecutable = false;
1910: SVNVersionedProperties baseProps = getBaseProperties(fileName);
1911: SVNVersionedProperties wcProps = getProperties(fileName);
1912:
1913: //TODO: to work properly we must create a tmp working props file
1914: //instead of tmp base props one
1915: File tmpPropsFile = getPropertiesFile(fileName, true);
1916: File wcPropsFile = getPropertiesFile(fileName, false);
1917: File basePropertiesFile = getBasePropertiesFile(fileName, false);
1918: SVNFileType tmpPropsType = SVNFileType.getType(tmpPropsFile);
1919: // tmp may be missing when there were no prop change at all!
1920: if (tmpPropsType == SVNFileType.FILE) {
1921: if (!getThisDirName().equals(fileName)) {
1922: SVNVersionedProperties propDiff = baseProps
1923: .compareTo(wcProps);
1924: setReadWrite = propDiff != null
1925: && propDiff
1926: .containsProperty(SVNProperty.NEEDS_LOCK)
1927: && propDiff
1928: .getPropertyValue(SVNProperty.NEEDS_LOCK) == null;
1929: setNotExecutable = propDiff != null
1930: && propDiff
1931: .containsProperty(SVNProperty.EXECUTABLE)
1932: && propDiff
1933: .getPropertyValue(SVNProperty.EXECUTABLE) == null;
1934: }
1935: try {
1936: if (!tmpPropsFile.exists()
1937: || tmpPropsFile.length() <= 4) {
1938: SVNFileUtil.deleteFile(basePropertiesFile);
1939: } else {
1940: SVNFileUtil.copyFile(tmpPropsFile,
1941: basePropertiesFile, true);
1942: SVNFileUtil.setReadonly(basePropertiesFile, true);
1943: }
1944: } finally {
1945: SVNFileUtil.deleteFile(tmpPropsFile);
1946: }
1947: }
1948:
1949: if (!getThisDirName().equals(fileName) && !implicit) {
1950: File tmpFile = getBaseFile(fileName, true);
1951: File baseFile = getBaseFile(fileName, false);
1952: File wcFile = getFile(fileName);
1953: File tmpFile2 = null;
1954: try {
1955: tmpFile2 = SVNFileUtil.createUniqueFile(tmpFile
1956: .getParentFile(), fileName, ".tmp");
1957: boolean overwritten = false;
1958: SVNFileType fileType = SVNFileType.getType(tmpFile);
1959: boolean special = getProperties(fileName)
1960: .getPropertyValue(SVNProperty.SPECIAL) != null;
1961: if (SVNFileUtil.isWindows || !special) {
1962: if (fileType == SVNFileType.FILE) {
1963: SVNTranslator
1964: .translate(this , fileName, SVNFileUtil
1965: .getBasePath(tmpFile),
1966: SVNFileUtil
1967: .getBasePath(tmpFile2),
1968: true);
1969: } else {
1970: SVNTranslator.translate(this , fileName,
1971: fileName, SVNFileUtil
1972: .getBasePath(tmpFile2), true);
1973: }
1974: if (!SVNFileUtil.compareFiles(tmpFile2, wcFile,
1975: null)) {
1976: SVNFileUtil.copyFile(tmpFile2, wcFile, true);
1977: overwritten = true;
1978: }
1979: }
1980: boolean needsReadonly = getProperties(fileName)
1981: .getPropertyValue(SVNProperty.NEEDS_LOCK) != null
1982: && entry.getLockToken() == null;
1983: boolean needsExecutable = getProperties(fileName)
1984: .getPropertyValue(SVNProperty.EXECUTABLE) != null;
1985: if (needsReadonly) {
1986: SVNFileUtil.setReadonly(wcFile, true);
1987: overwritten = true;
1988: }
1989: if (needsExecutable) {
1990: SVNFileUtil.setExecutable(wcFile, true);
1991: overwritten = true;
1992: }
1993: if (fileType == SVNFileType.FILE) {
1994: SVNFileUtil.rename(tmpFile, baseFile);
1995: }
1996: if (setReadWrite) {
1997: SVNFileUtil.setReadonly(wcFile, false);
1998: overwritten = true;
1999: }
2000: if (setNotExecutable) {
2001: SVNFileUtil.setExecutable(wcFile, false);
2002: overwritten = true;
2003: }
2004: if (overwritten) {
2005: textTime = wcFile.lastModified();
2006: }
2007: } catch (SVNException svne) {
2008: SVNErrorMessage err = SVNErrorMessage.create(errorCode,
2009: "Error replacing text-base of ''{0}''",
2010: fileName);
2011: SVNErrorManager.error(err, svne);
2012: } finally {
2013: tmpFile2.delete();
2014: tmpFile.delete();
2015: }
2016: }
2017:
2018: // update entry
2019: Map entryAttrs = new HashMap();
2020: entryAttrs.put(SVNProperty
2021: .shortPropertyName(SVNProperty.REVISION), SVNProperty
2022: .toString(revisionNumber));
2023: entryAttrs
2024: .put(
2025: SVNProperty.shortPropertyName(SVNProperty.KIND),
2026: getThisDirName().equals(fileName) ? SVNProperty.KIND_DIR
2027: : SVNProperty.KIND_FILE);
2028: if (!implicit) {
2029: entryAttrs.put(SVNProperty
2030: .shortPropertyName(SVNProperty.SCHEDULE), null);
2031: }
2032: entryAttrs.put(SVNProperty
2033: .shortPropertyName(SVNProperty.COPIED), SVNProperty
2034: .toString(false));
2035: entryAttrs.put(SVNProperty
2036: .shortPropertyName(SVNProperty.DELETED), SVNProperty
2037: .toString(false));
2038: if (textTime != 0 && !implicit) {
2039: entryAttrs.put(SVNProperty
2040: .shortPropertyName(SVNProperty.TEXT_TIME),
2041: SVNTimeUtil.formatDate(new Date(textTime)));
2042: }
2043: entryAttrs.put(SVNProperty
2044: .shortPropertyName(SVNProperty.CONFLICT_NEW), null);
2045: entryAttrs.put(SVNProperty
2046: .shortPropertyName(SVNProperty.CONFLICT_OLD), null);
2047: entryAttrs.put(SVNProperty
2048: .shortPropertyName(SVNProperty.CONFLICT_WRK), null);
2049: entryAttrs.put(SVNProperty
2050: .shortPropertyName(SVNProperty.PROP_REJECT_FILE), null);
2051: entryAttrs
2052: .put(
2053: SVNProperty
2054: .shortPropertyName(SVNProperty.COPYFROM_REVISION),
2055: null);
2056: entryAttrs.put(SVNProperty
2057: .shortPropertyName(SVNProperty.COPYFROM_URL), null);
2058: entryAttrs.put(SVNProperty
2059: .shortPropertyName(SVNProperty.HAS_PROP_MODS),
2060: SVNProperty.toString(false));
2061:
2062: try {
2063: modifyEntry(fileName, entryAttrs, false, true);
2064: } catch (SVNException svne) {
2065: SVNErrorMessage err = SVNErrorMessage.create(errorCode,
2066: "Error modifying entry of ''{0}''", fileName);
2067: SVNErrorManager.error(err, svne);
2068: }
2069: SVNFileUtil.deleteFile(wcPropsFile);
2070:
2071: if (!getThisDirName().equals(fileName)) {
2072: return;
2073: }
2074: // update entry in parent.
2075: File dirFile = getRoot();
2076: if (getWCAccess().isWCRoot(getRoot())) {
2077: return;
2078: }
2079:
2080: boolean unassociated = false;
2081: SVNAdminArea parentArea = null;
2082: try {
2083: parentArea = getWCAccess()
2084: .retrieve(dirFile.getParentFile());
2085: } catch (SVNException svne) {
2086: if (svne.getErrorMessage().getErrorCode() == SVNErrorCode.WC_NOT_LOCKED) {
2087: parentArea = getWCAccess().open(
2088: dirFile.getParentFile(), true, false, 0);
2089: unassociated = true;
2090: }
2091: throw svne;
2092: }
2093:
2094: SVNEntry entryInParent = parentArea.getEntry(dirFile.getName(),
2095: false);
2096: if (entryInParent != null) {
2097: entryAttrs.clear();
2098:
2099: if (!implicit) {
2100: entryAttrs.put(SVNProperty
2101: .shortPropertyName(SVNProperty.SCHEDULE), null);
2102: }
2103: entryAttrs.put(SVNProperty
2104: .shortPropertyName(SVNProperty.COPIED), SVNProperty
2105: .toString(false));
2106: entryAttrs.put(SVNProperty
2107: .shortPropertyName(SVNProperty.COPYFROM_REVISION),
2108: null);
2109: entryAttrs.put(SVNProperty
2110: .shortPropertyName(SVNProperty.COPYFROM_URL), null);
2111: entryAttrs.put(SVNProperty
2112: .shortPropertyName(SVNProperty.DELETED),
2113: SVNProperty.toString(false));
2114: try {
2115: parentArea.modifyEntry(entryInParent.getName(),
2116: entryAttrs, true, true);
2117: } catch (SVNException svne) {
2118: SVNErrorMessage err = SVNErrorMessage.create(errorCode,
2119: "Error modifying entry of ''{0}''", fileName);
2120: SVNErrorManager.error(err, svne);
2121: }
2122: }
2123: parentArea.saveEntries(false);
2124:
2125: if (unassociated) {
2126: getWCAccess().closeAdminArea(dirFile.getParentFile());
2127: }
2128: }
2129:
2130: public boolean unlock() throws SVNException {
2131: if (!myLockFile.exists()) {
2132: return true;
2133: }
2134: // only if there are not locks or killme files.
2135: boolean killMe = getAdminFile("KILLME").exists();
2136: if (killMe) {
2137: return false;
2138: }
2139: File[] logs = SVNFileListUtil.listFiles(getAdminDirectory());
2140: for (int i = 0; logs != null && i < logs.length; i++) {
2141: File log = logs[i];
2142: if ("log".equals(log.getName())
2143: || log.getName().startsWith("log.")) {
2144: return false;
2145: }
2146: }
2147: boolean deleted = SVNFileUtil.deleteFile(myLockFile);
2148: if (!deleted) {
2149: SVNErrorMessage err = SVNErrorMessage.create(
2150: SVNErrorCode.WC_LOCKED,
2151: "Failed to unlock working copy ''{0}''", getRoot());
2152: SVNErrorManager.error(err);
2153: }
2154: return deleted;
2155: }
2156:
2157: public boolean isVersioned() {
2158: if (getAdminDirectory().isDirectory()
2159: && myEntriesFile.canRead()) {
2160: try {
2161: if (getEntry("", false) != null) {
2162: return true;
2163: }
2164: } catch (SVNException e) {
2165: //
2166: }
2167: }
2168: return false;
2169: }
2170:
2171: public boolean isLocked() throws SVNException {
2172: if (!myWasLocked) {
2173: return false;
2174: }
2175: SVNFileType type = SVNFileType.getType(myLockFile);
2176: if (type == SVNFileType.FILE) {
2177: return true;
2178: } else if (type == SVNFileType.NONE) {
2179: return false;
2180: }
2181:
2182: SVNErrorMessage err = SVNErrorMessage.create(
2183: SVNErrorCode.WC_LOCKED,
2184: "Lock file ''{0}'' is not a regular file", myLockFile);
2185: SVNErrorManager.error(err);
2186: return false;
2187: }
2188:
2189: protected int getFormatVersion() {
2190: return WC_FORMAT;
2191: }
2192:
2193: protected boolean isEntryPropertyApplicable(String name) {
2194: return true;
2195: }
2196:
2197: }
|