/* * ==================================================================== * Copyright (c) 2004-2007 TMate Software Ltd. All rights reserved. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://svnkit.com/license.html * If newer versions of this license are posted there, you may use a * newer version instead, at your option. * ==================================================================== */ package org.tmatesoft.svn.core.internal.wc.admin; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import org.tmatesoft.svn.core.SVNErrorCode; import org.tmatesoft.svn.core.SVNErrorMessage; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNNodeKind; import org.tmatesoft.svn.core.SVNProperty; import org.tmatesoft.svn.core.internal.io.fs.FSFile; import org.tmatesoft.svn.core.internal.util.SVNEncodingUtil; import org.tmatesoft.svn.core.internal.util.SVNFormatUtil; import org.tmatesoft.svn.core.internal.util.SVNPathUtil; import org.tmatesoft.svn.core.internal.util.SVNTimeUtil; import org.tmatesoft.svn.core.internal.wc.SVNErrorManager; import org.tmatesoft.svn.core.internal.wc.SVNFileListUtil; import org.tmatesoft.svn.core.internal.wc.SVNFileType; import org.tmatesoft.svn.core.internal.wc.SVNFileUtil; import org.tmatesoft.svn.core.internal.wc.SVNProperties; import org.tmatesoft.svn.util.SVNDebugLog; /** * @version 1.1.1 * @author TMate Software Ltd. */ public class SVNAdminArea14 extends SVNAdminArea { public static final String[] ourCachableProperties = new String[] { SVNProperty.SPECIAL, SVNProperty.EXTERNALS, SVNProperty.NEEDS_LOCK }; public static final int WC_FORMAT = 8; private static final String ATTRIBUTE_COPIED = "copied"; private static final String ATTRIBUTE_DELETED = "deleted"; private static final String ATTRIBUTE_ABSENT = "absent"; private static final String ATTRIBUTE_INCOMPLETE = "incomplete"; private static final String THIS_DIR = ""; private File myLockFile; private File myEntriesFile; public SVNAdminArea14(File dir) { super(dir); myLockFile = new File(getAdminDirectory(), "lock"); myEntriesFile = new File(getAdminDirectory(), "entries"); } public static String[] getCachableProperties() { return ourCachableProperties; } public void saveWCProperties(boolean close) throws SVNException { Map wcPropsCache = getWCPropertiesStorage(false); if (wcPropsCache == null) { return; } boolean hasAnyProps = false; File dstFile = getAdminFile("all-wcprops"); File tmpFile = getAdminFile("tmp/all-wcprops"); for(Iterator entries = wcPropsCache.keySet().iterator(); entries.hasNext();) { String name = (String)entries.next(); SVNVersionedProperties props = (SVNVersionedProperties)wcPropsCache.get(name); if (!props.isEmpty()) { hasAnyProps = true; break; } } if (hasAnyProps) { OutputStream target = null; try { target = SVNFileUtil.openFileForWriting(tmpFile); SVNVersionedProperties props = (SVNVersionedProperties)wcPropsCache.get(getThisDirName()); if (props != null && !props.isEmpty()) { SVNProperties.setProperties(props.asMap(), target, SVNProperties.SVN_HASH_TERMINATOR); } else { SVNProperties.setProperties(Collections.EMPTY_MAP, target, SVNProperties.SVN_HASH_TERMINATOR); } for(Iterator entries = wcPropsCache.keySet().iterator(); entries.hasNext();) { String name = (String)entries.next(); if (getThisDirName().equals(name)) { continue; } props = (SVNVersionedProperties)wcPropsCache.get(name); if (!props.isEmpty()) { target.write(name.getBytes("UTF-8")); target.write('\n'); SVNProperties.setProperties(props.asMap(), target, SVNProperties.SVN_HASH_TERMINATOR); } } } catch (IOException ioe) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, ioe.getLocalizedMessage()); SVNErrorManager.error(err, ioe); } finally { SVNFileUtil.closeFile(target); } SVNFileUtil.rename(tmpFile, dstFile); SVNFileUtil.setReadonly(dstFile, true); } else { SVNFileUtil.deleteFile(dstFile); } if (close) { closeWCProperties(); } } public SVNVersionedProperties getBaseProperties(String name) throws SVNException { Map basePropsCache = getBasePropertiesStorage(true); SVNVersionedProperties props = (SVNVersionedProperties)basePropsCache.get(name); if (props != null) { return props; } Map baseProps = null; try { baseProps = readBaseProperties(name); } catch (SVNException svne) { SVNErrorMessage err = svne.getErrorMessage().wrap("Failed to load properties from disk"); SVNErrorManager.error(err); } props = new SVNProperties13(baseProps); basePropsCache.put(name, props); return props; } public SVNVersionedProperties getRevertProperties(String name) throws SVNException { Map revertPropsCache = getRevertPropertiesStorage(true); SVNVersionedProperties props = (SVNVersionedProperties)revertPropsCache.get(name); if (props != null) { return props; } Map revertProps = null; try { revertProps = readRevertProperties(name); } catch (SVNException svne) { SVNErrorMessage err = svne.getErrorMessage().wrap("Failed to load properties from disk"); SVNErrorManager.error(err); } props = new SVNProperties13(revertProps); revertPropsCache.put(name, props); return props; } public SVNVersionedProperties getProperties(String name) throws SVNException { Map propsCache = getPropertiesStorage(true); SVNVersionedProperties props = (SVNVersionedProperties)propsCache.get(name); if (props != null) { return props; } final String entryName = name; props = new SVNProperties14(null, this, name){ protected Map loadProperties() throws SVNException { Map props = getPropertiesMap(); if (props == null) { try { props = readProperties(entryName); } catch (SVNException svne) { SVNErrorMessage err = svne.getErrorMessage().wrap("Failed to load properties from disk"); SVNErrorManager.error(err); } props = props != null ? props : new HashMap(); setPropertiesMap(props); } return props; } }; propsCache.put(name, props); return props; } public SVNVersionedProperties getWCProperties(String entryName) throws SVNException { SVNEntry entry = getEntry(entryName, false); if (entry == null) { return null; } Map wcPropsCache = getWCPropertiesStorage(true); SVNVersionedProperties props = (SVNVersionedProperties)wcPropsCache.get(entryName); if (props != null) { return props; } if (wcPropsCache.isEmpty()) { wcPropsCache = readAllWCProperties(); } props = (SVNVersionedProperties)wcPropsCache.get(entryName); if (props == null) { props = new SVNProperties13(new HashMap()); wcPropsCache.put(entryName, props); } return props; } private Map readAllWCProperties() throws SVNException { Map wcPropsCache = getWCPropertiesStorage(true); wcPropsCache.clear(); File propertiesFile = getAdminFile("all-wcprops"); if (!propertiesFile.exists()) { return wcPropsCache; } FSFile wcpropsFile = null; try { wcpropsFile = new FSFile(propertiesFile); Map wcProps = wcpropsFile.readProperties(false); SVNVersionedProperties entryWCProps = new SVNProperties13(wcProps); wcPropsCache.put(getThisDirName(), entryWCProps); String name = null; StringBuffer buffer = new StringBuffer(); while(true) { try { name = wcpropsFile.readLine(buffer); } catch (SVNException e) { if (e.getErrorMessage().getErrorCode() == SVNErrorCode.STREAM_UNEXPECTED_EOF && buffer.length() > 0) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_CORRUPT, "Missing end of line in wcprops file for ''{0}''", getRoot()); SVNErrorManager.error(err, e); } break; } wcProps = wcpropsFile.readProperties(false); entryWCProps = new SVNProperties13(wcProps); wcPropsCache.put(name, entryWCProps); buffer.delete(0, buffer.length()); } } catch (SVNException svne) { SVNErrorMessage err = svne.getErrorMessage().wrap("Failed to load properties from disk"); SVNErrorManager.error(err); } finally { wcpropsFile.close(); } return wcPropsCache; } private Map readBaseProperties(String name) throws SVNException { File propertiesFile = getBasePropertiesFile(name, false); SVNProperties props = new SVNProperties(propertiesFile, null); return props.asMap(); } private Map readRevertProperties(String name) throws SVNException { File propertiesFile = getRevertPropertiesFile(name, false); SVNProperties props = new SVNProperties(propertiesFile, null); return props.asMap(); } private Map readProperties(String name) throws SVNException { if (hasPropModifications(name)) { File propertiesFile = getPropertiesFile(name, false); SVNProperties props = new SVNProperties(propertiesFile, null); return props.asMap(); } Map basePropsCache = getBasePropertiesStorage(true); if (basePropsCache != null ) { SVNVersionedProperties baseProps = (SVNVersionedProperties) basePropsCache.get(name); if (baseProps != null) { return baseProps.asMap(); } } if (hasProperties(name)) { return readBaseProperties(name); } return new HashMap(); } public void saveVersionedProperties(SVNLog log, boolean close) throws SVNException { Map command = new HashMap(); Set processedEntries = new HashSet(); Map propsCache = getPropertiesStorage(false); if (propsCache != null && !propsCache.isEmpty()) { for(Iterator entries = propsCache.keySet().iterator(); entries.hasNext();) { String name = (String)entries.next(); SVNVersionedProperties props = (SVNVersionedProperties)propsCache.get(name); if (props.isModified()) { SVNVersionedProperties baseProps = getBaseProperties(name); SVNVersionedProperties propsDiff = baseProps.compareTo(props); String[] cachableProps = SVNAdminArea14.getCachableProperties(); command.put(SVNProperty.CACHABLE_PROPS, asString(cachableProps, " ")); Map propsMap = props.loadProperties(); LinkedList presentProps = new LinkedList(); for (int i = 0; i < cachableProps.length; i++) { if (propsMap.containsKey(cachableProps[i])) { presentProps.addLast(cachableProps[i]); } } if (presentProps.size() > 0) { String presentPropsString = asString((String[])presentProps.toArray(new String[presentProps.size()]), " "); command.put(SVNProperty.PRESENT_PROPS, presentPropsString); } else { command.put(SVNProperty.PRESENT_PROPS, ""); } command.put(SVNProperty.HAS_PROPS, SVNProperty.toString(!props.isEmpty())); boolean hasPropModifications = !propsDiff.isEmpty(); command.put(SVNProperty.HAS_PROP_MODS, SVNProperty.toString(hasPropModifications)); command.put(SVNLog.NAME_ATTR, name); log.addCommand(SVNLog.MODIFY_ENTRY, command, false); processedEntries.add(name); command.clear(); String dstPath = getThisDirName().equals(name) ? "dir-props" : "props/" + name + ".svn-work"; dstPath = getAdminDirectory().getName() + "/" + dstPath; if (hasPropModifications) { String tmpPath = "tmp/"; tmpPath += getThisDirName().equals(name) ? "dir-props" : "props/" + name + ".svn-work"; File tmpFile = getAdminFile(tmpPath); String srcPath = getAdminDirectory().getName() + "/" + tmpPath; SVNProperties tmpProps = new SVNProperties(tmpFile, srcPath); tmpProps.setProperties(props.asMap()); command.put(SVNLog.NAME_ATTR, srcPath); command.put(SVNLog.DEST_ATTR, dstPath); log.addCommand(SVNLog.MOVE, command, false); command.clear(); command.put(SVNLog.NAME_ATTR, dstPath); log.addCommand(SVNLog.READONLY, command, false); } else { command.put(SVNLog.NAME_ATTR, dstPath); log.addCommand(SVNLog.DELETE, command, false); } command.clear(); props.setModified(false); } } } Map basePropsCache = getBasePropertiesStorage(false); if (basePropsCache != null && !basePropsCache.isEmpty()) { for(Iterator entries = basePropsCache.keySet().iterator(); entries.hasNext();) { String name = (String)entries.next(); SVNVersionedProperties baseProps = (SVNVersionedProperties)basePropsCache.get(name); if (baseProps.isModified()) { String dstPath = getThisDirName().equals(name) ? "dir-prop-base" : "prop-base/" + name + ".svn-base"; dstPath = getAdminDirectory().getName() + "/" + dstPath; boolean isEntryProcessed = processedEntries.contains(name); if (!isEntryProcessed) { SVNVersionedProperties props = getProperties(name); String[] cachableProps = SVNAdminArea14.getCachableProperties(); command.put(SVNProperty.CACHABLE_PROPS, asString(cachableProps, " ")); Map propsMap = props.loadProperties(); LinkedList presentProps = new LinkedList(); for (int i = 0; i < cachableProps.length; i++) { if (propsMap.containsKey(cachableProps[i])) { presentProps.addLast(cachableProps[i]); } } if (presentProps.size() > 0) { String presentPropsString = asString((String[])presentProps.toArray(new String[presentProps.size()]), " "); command.put(SVNProperty.PRESENT_PROPS, presentPropsString); } else { command.put(SVNProperty.PRESENT_PROPS, ""); } command.put(SVNProperty.HAS_PROPS, SVNProperty.toString(!props.isEmpty())); SVNVersionedProperties propsDiff = baseProps.compareTo(props); boolean hasPropModifications = !propsDiff.isEmpty(); command.put(SVNProperty.HAS_PROP_MODS, SVNProperty.toString(hasPropModifications)); command.put(SVNLog.NAME_ATTR, name); log.addCommand(SVNLog.MODIFY_ENTRY, command, false); command.clear(); if (!hasPropModifications) { String workingPropsPath = getThisDirName().equals(name) ? "dir-props" : "props/" + name + ".svn-work"; workingPropsPath = getAdminDirectory().getName() + "/" + workingPropsPath; command.put(SVNLog.NAME_ATTR, workingPropsPath); log.addCommand(SVNLog.DELETE, command, false); command.clear(); } } if (baseProps.isEmpty()) { command.put(SVNLog.NAME_ATTR, dstPath); log.addCommand(SVNLog.DELETE, command, false); } else { String tmpPath = "tmp/"; tmpPath += getThisDirName().equals(name) ? "dir-prop-base" : "prop-base/" + name + ".svn-base"; File tmpFile = getAdminFile(tmpPath); String srcPath = getAdminDirectory().getName() + "/" + tmpPath; SVNProperties tmpProps = new SVNProperties(tmpFile, srcPath); tmpProps.setProperties(baseProps.asMap()); command.put(SVNLog.NAME_ATTR, srcPath); command.put(SVNLog.DEST_ATTR, dstPath); log.addCommand(SVNLog.MOVE, command, false); command.clear(); command.put(SVNLog.NAME_ATTR, dstPath); log.addCommand(SVNLog.READONLY, command, false); } baseProps.setModified(false); } } } if (close) { closeVersionedProperties(); } } public void saveEntries(boolean close) throws SVNException { if (myEntries != null) { if (!isLocked()) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_NOT_LOCKED, "No write-lock in ''{0}''", getRoot()); SVNErrorManager.error(err); } SVNEntry rootEntry = (SVNEntry) myEntries.get(getThisDirName()); if (rootEntry == null) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_NOT_FOUND, "No default entry in directory ''{0}''", getRoot()); SVNErrorManager.error(err); } String reposURL = rootEntry.getRepositoryRoot(); String url = rootEntry.getURL(); if (reposURL != null && !SVNPathUtil.isAncestor(reposURL, url)) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNKNOWN, "Entry ''{0}'' has inconsistent repository root and url", getThisDirName()); SVNErrorManager.error(err); } File tmpFile = new File(getAdminDirectory(), "tmp/entries"); Writer os = null; try { os = new OutputStreamWriter(SVNFileUtil.openFileForWriting(tmpFile), "UTF-8"); writeEntries(os); } catch (IOException e) { SVNFileUtil.closeFile(os); SVNFileUtil.deleteFile(tmpFile); SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, "Cannot wrtie entries file ''{0}'': {1}", new Object[] {myEntriesFile, e.getLocalizedMessage()}); SVNErrorManager.error(err, e); } finally { SVNFileUtil.closeFile(os); } SVNFileUtil.rename(tmpFile, myEntriesFile); SVNFileUtil.setReadonly(myEntriesFile, true); if (close) { closeEntries(); } } } protected Map fetchEntries() throws SVNException { if (!myEntriesFile.exists()) { return null; } Map entries = new HashMap(); BufferedReader reader = null; try { reader = new BufferedReader(new InputStreamReader(SVNFileUtil.openFileForReading(myEntriesFile), "UTF-8")); //skip format line reader.readLine(); int entryNumber = 1; while(true){ try { SVNEntry entry = readEntry(reader, entryNumber); if (entry == null) { break; } entries.put(entry.getName(), entry); } catch (SVNException svne) { SVNErrorMessage err = svne.getErrorMessage().wrap("Error at entry {0} in entries file for ''{1}'':", new Object[]{new Integer(entryNumber), getRoot()}); SVNErrorManager.error(err); } ++entryNumber; } } catch (IOException e) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, "Cannot read entries file ''{0}'': {1}", new Object[] {myEntriesFile, e.getMessage()}); SVNErrorManager.error(err, e); } finally { SVNFileUtil.closeFile(reader); } SVNEntry defaultEntry = (SVNEntry)entries.get(getThisDirName()); if (defaultEntry == null) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_NOT_FOUND, "Missing default entry"); SVNErrorManager.error(err); } Map defaultEntryAttrs = defaultEntry.asMap(); if (defaultEntryAttrs.get(SVNProperty.REVISION) == null) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_MISSING_REVISION, "Default entry has no revision number"); SVNErrorManager.error(err); } if (defaultEntryAttrs.get(SVNProperty.URL) == null) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_MISSING_URL, "Default entry is missing URL"); SVNErrorManager.error(err); } for (Iterator entriesIter = entries.keySet().iterator(); entriesIter.hasNext();) { String name = (String)entriesIter.next(); SVNEntry entry = (SVNEntry)entries.get(name); if (getThisDirName().equals(name)) { continue; } Map entryAttributes = entry.asMap(); SVNNodeKind kind = SVNNodeKind.parseKind((String)entryAttributes.get(SVNProperty.KIND)); if (kind == SVNNodeKind.FILE) { if (entryAttributes.get(SVNProperty.REVISION) == null || Long.parseLong((String) entryAttributes.get(SVNProperty.REVISION), 10) < 0) { entryAttributes.put(SVNProperty.REVISION, defaultEntryAttrs.get(SVNProperty.REVISION)); } if (entryAttributes.get(SVNProperty.URL) == null) { String rootURL = (String)defaultEntryAttrs.get(SVNProperty.URL); String url = SVNPathUtil.append(rootURL, SVNEncodingUtil.uriEncode(name)); entryAttributes.put(SVNProperty.URL, url); } if (entryAttributes.get(SVNProperty.REPOS) == null) { entryAttributes.put(SVNProperty.REPOS, defaultEntryAttrs.get(SVNProperty.REPOS)); } if (entryAttributes.get(SVNProperty.UUID) == null) { String schedule = (String)entryAttributes.get(SVNProperty.SCHEDULE); if (!(SVNProperty.SCHEDULE_ADD.equals(schedule) || SVNProperty.SCHEDULE_REPLACE.equals(schedule))) { entryAttributes.put(SVNProperty.UUID, defaultEntryAttrs.get(SVNProperty.UUID)); } } if (entryAttributes.get(SVNProperty.CACHABLE_PROPS) == null) { entryAttributes.put(SVNProperty.CACHABLE_PROPS, defaultEntryAttrs.get(SVNProperty.CACHABLE_PROPS)); } } } return entries; } private SVNEntry readEntry(BufferedReader reader, int entryNumber) throws IOException, SVNException { String line = reader.readLine(); if (line == null && entryNumber > 1) { return null; } String name = parseString(line); name = name != null ? name : getThisDirName(); Map entryAttrs = new HashMap(); entryAttrs.put(SVNProperty.NAME, name); SVNEntry entry = new SVNEntry(entryAttrs, this, name); line = reader.readLine(); String kind = parseValue(line); if (kind != null) { SVNNodeKind parsedKind = SVNNodeKind.parseKind(kind); if (parsedKind != SVNNodeKind.UNKNOWN && parsedKind != SVNNodeKind.NONE) { entryAttrs.put(SVNProperty.KIND, kind); } else { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.NODE_UNKNOWN_KIND, "Entry ''{0}'' has invalid node kind", name); SVNErrorManager.error(err); } } else { entryAttrs.put(SVNProperty.KIND, SVNNodeKind.NONE.toString()); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String revision = parseValue(line); if (revision != null) { entryAttrs.put(SVNProperty.REVISION, revision); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String url = parseString(line); if (url != null) { entryAttrs.put(SVNProperty.URL, url); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String reposRoot = parseString(line); if (reposRoot != null && url != null && !SVNPathUtil.isAncestor(reposRoot, url)) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_CORRUPT, "Entry for ''{0}'' has invalid repository root", name); SVNErrorManager.error(err); } else if (reposRoot != null) { entryAttrs.put(SVNProperty.REPOS, reposRoot); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String schedule = parseValue(line); if (schedule != null) { if (SVNProperty.SCHEDULE_ADD.equals(schedule) || SVNProperty.SCHEDULE_DELETE.equals(schedule) || SVNProperty.SCHEDULE_REPLACE.equals(schedule)) { entryAttrs.put(SVNProperty.SCHEDULE, schedule); } else { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_ATTRIBUTE_INVALID, "Entry ''{0}'' has invalid ''{1}'' value", new Object[]{name, SVNProperty.SCHEDULE}); SVNErrorManager.error(err); } } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String timestamp = parseValue(line); if (timestamp != null) { entryAttrs.put(SVNProperty.TEXT_TIME, timestamp); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String checksum = parseString(line); if (checksum != null) { entryAttrs.put(SVNProperty.CHECKSUM, checksum); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String committedDate = parseValue(line); if (committedDate != null) { entryAttrs.put(SVNProperty.COMMITTED_DATE, committedDate); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String committedRevision = parseValue(line); if (committedRevision != null) { entryAttrs.put(SVNProperty.COMMITTED_REVISION, committedRevision); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String committedAuthor = parseString(line); if (committedAuthor != null) { entryAttrs.put(SVNProperty.LAST_AUTHOR, committedAuthor); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } boolean hasProps = parseBoolean(line, SVNProperty.HAS_PROPS); if (hasProps) { entryAttrs.put(SVNProperty.HAS_PROPS, SVNProperty.toString(hasProps)); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } boolean hasPropMods = parseBoolean(line, SVNProperty.HAS_PROP_MODS); if (hasPropMods) { entryAttrs.put(SVNProperty.HAS_PROP_MODS, SVNProperty.toString(hasPropMods)); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String cachablePropsStr = parseValue(line); if (cachablePropsStr != null) { String[] cachableProps = fromString(cachablePropsStr, " "); entryAttrs.put(SVNProperty.CACHABLE_PROPS, cachableProps); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String presentPropsStr = parseValue(line); if (presentPropsStr != null) { String[] presentProps = fromString(presentPropsStr, " "); entryAttrs.put(SVNProperty.PRESENT_PROPS, presentProps); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String prejFile = parseString(line); if (prejFile != null) { entryAttrs.put(SVNProperty.PROP_REJECT_FILE, prejFile); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String conflictOldFile = parseString(line); if (conflictOldFile != null) { entryAttrs.put(SVNProperty.CONFLICT_OLD, conflictOldFile); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String conflictNewFile = parseString(line); if (conflictNewFile != null) { entryAttrs.put(SVNProperty.CONFLICT_NEW, conflictNewFile); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String conflictWorkFile = parseString(line); if (conflictWorkFile != null) { entryAttrs.put(SVNProperty.CONFLICT_WRK, conflictWorkFile); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } boolean isCopied = parseBoolean(line, ATTRIBUTE_COPIED); if (isCopied) { entryAttrs.put(SVNProperty.COPIED, SVNProperty.toString(isCopied)); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String copyfromURL = parseString(line); if (copyfromURL != null) { entryAttrs.put(SVNProperty.COPYFROM_URL, copyfromURL); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String copyfromRevision = parseValue(line); if (copyfromRevision != null) { entryAttrs.put(SVNProperty.COPYFROM_REVISION, copyfromRevision); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } boolean isDeleted = parseBoolean(line, ATTRIBUTE_DELETED); if (isDeleted) { entryAttrs.put(SVNProperty.DELETED, SVNProperty.toString(isDeleted)); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } boolean isAbsent = parseBoolean(line, ATTRIBUTE_ABSENT); if (isAbsent) { entryAttrs.put(SVNProperty.ABSENT, SVNProperty.toString(isAbsent)); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } boolean isIncomplete = parseBoolean(line, ATTRIBUTE_INCOMPLETE); if (isIncomplete) { entryAttrs.put(SVNProperty.INCOMPLETE, SVNProperty.toString(isIncomplete)); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String uuid = parseString(line); if (uuid != null) { entryAttrs.put(SVNProperty.UUID, uuid); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String lockToken = parseString(line); if (lockToken != null) { entryAttrs.put(SVNProperty.LOCK_TOKEN, lockToken); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String lockOwner = parseString(line); if (lockOwner != null) { entryAttrs.put(SVNProperty.LOCK_OWNER, lockOwner); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String lockComment = parseString(line); if (lockComment != null) { entryAttrs.put(SVNProperty.LOCK_COMMENT, lockComment); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String lockCreationDate = parseValue(line); if (lockCreationDate != null) { entryAttrs.put(SVNProperty.LOCK_CREATION_DATE, lockCreationDate); } line = reader.readLine(); if (line == null || line.length() != 1) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_CORRUPT, "Missing entry terminator"); SVNErrorManager.error(err); } else if (line.length() == 1 && line.charAt(0) != '\f') { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_CORRUPT, "Invalid entry terminator"); SVNErrorManager.error(err); } return entry; } private boolean isEntryFinished(String line) { return line != null && line.length() > 0 && line.charAt(0) == '\f'; } private boolean parseBoolean(String line, String field) throws SVNException { line = parseValue(line); if (line != null) { if (!line.equals(field)) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_CORRUPT, "Invalid value for field ''{0}''", field); SVNErrorManager.error(err); } return true; } return false; } private String parseString(String line) throws SVNException { if (line == null) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_CORRUPT, "Unexpected end of entry"); SVNErrorManager.error(err); } else if ("".equals(line)) { return null; } int fromIndex = 0; int ind = -1; StringBuffer buffer = null; String escapedString = null; while ((ind = line.indexOf('\\', fromIndex)) != -1) { if (line.length() < ind + 4 || line.charAt(ind + 1) != 'x' || !SVNEncodingUtil.isHexDigit(line.charAt(ind + 2)) || !SVNEncodingUtil.isHexDigit(line.charAt(ind + 3))) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_CORRUPT, "Invalid escape sequence"); SVNErrorManager.error(err); } if (buffer == null) { buffer = new StringBuffer(); } escapedString = line.substring(ind + 2, ind + 4); int escapedByte = Integer.parseInt(escapedString, 16); if (ind > fromIndex) { buffer.append(line.substring(fromIndex, ind)); buffer.append((char)(escapedByte & 0xFF)); } else { buffer.append((char)(escapedByte & 0xFF)); } fromIndex = ind + 4; } if (buffer != null) { if (fromIndex < line.length()) { buffer.append(line.substring(fromIndex)); } return buffer.toString(); } return line; } private String parseValue(String line) throws SVNException { if (line == null) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_CORRUPT, "Unexpected end of entry"); SVNErrorManager.error(err); } else if ("".equals(line)) { return null; } return line; } public String getThisDirName() { return THIS_DIR; } protected void writeEntries(Writer writer) throws IOException { SVNEntry rootEntry = (SVNEntry)myEntries.get(getThisDirName()); writer.write(getFormatVersion() + "\n"); writeEntry(writer, getThisDirName(), rootEntry.asMap(), null); List names = new ArrayList(myEntries.keySet()); Collections.sort(names); for (Iterator entries = names.iterator(); entries.hasNext();) { String name = (String)entries.next(); SVNEntry entry = (SVNEntry)myEntries.get(name); if (getThisDirName().equals(name)) { continue; } Map entryAttributes = entry.asMap(); Map defaultEntryAttrs = rootEntry.asMap(); SVNNodeKind kind = SVNNodeKind.parseKind((String)entryAttributes.get(SVNProperty.KIND)); if (kind == SVNNodeKind.FILE) { if (entryAttributes.get(SVNProperty.REVISION) == null || Long.parseLong((String) entryAttributes.get(SVNProperty.REVISION), 10) < 0) { entryAttributes.put(SVNProperty.REVISION, defaultEntryAttrs.get(SVNProperty.REVISION)); } if (entryAttributes.get(SVNProperty.URL) == null) { String rootURL = (String)defaultEntryAttrs.get(SVNProperty.URL); String url = SVNPathUtil.append(rootURL, SVNEncodingUtil.uriEncode(name)); entryAttributes.put(SVNProperty.URL, url); } if (entryAttributes.get(SVNProperty.REPOS) == null) { entryAttributes.put(SVNProperty.REPOS, defaultEntryAttrs.get(SVNProperty.REPOS)); } if (entryAttributes.get(SVNProperty.UUID) == null) { String schedule = (String)entryAttributes.get(SVNProperty.SCHEDULE); if (!(SVNProperty.SCHEDULE_ADD.equals(schedule) || SVNProperty.SCHEDULE_REPLACE.equals(schedule))) { entryAttributes.put(SVNProperty.UUID, defaultEntryAttrs.get(SVNProperty.UUID)); } } if (entryAttributes.get(SVNProperty.CACHABLE_PROPS) == null) { entryAttributes.put(SVNProperty.CACHABLE_PROPS, defaultEntryAttrs.get(SVNProperty.CACHABLE_PROPS)); } } writeEntry(writer, name, entryAttributes, rootEntry.asMap()); } } private void writeEntry(Writer writer, String name, Map entry, Map rootEntry) throws IOException { boolean isThisDir = getThisDirName().equals(name); boolean isSubDir = !isThisDir && SVNProperty.KIND_DIR.equals(entry.get(SVNProperty.KIND)); int emptyFields = 0; if (!writeString(writer, name, emptyFields)) { ++emptyFields; } String kind = (String)entry.get(SVNProperty.KIND); if (writeValue(writer, kind, emptyFields)){ emptyFields = 0; } else { ++emptyFields; } String revision = null; if (isThisDir){ revision = (String)entry.get(SVNProperty.REVISION); } else if (!isSubDir){ revision = (String)entry.get(SVNProperty.REVISION); if (revision != null && revision.equals(rootEntry.get(SVNProperty.REVISION))) { revision = null; } } if (writeRevision(writer, revision, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } String url = null; if (isThisDir) { url = (String)entry.get(SVNProperty.URL); } else if (!isSubDir) { url = (String)entry.get(SVNProperty.URL); String expectedURL = SVNPathUtil.append((String)rootEntry.get(SVNProperty.URL), name); if (url != null && url.equals(expectedURL)) { url = null; } } if (writeString(writer, url, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } String root = null; if (isThisDir) { root = (String)entry.get(SVNProperty.REPOS); } else if (!isSubDir) { String thisDirRoot = (String)rootEntry.get(SVNProperty.REPOS); root = (String)entry.get(SVNProperty.REPOS); if (root != null && root.equals(thisDirRoot)) { root = null; } } if (writeString(writer, root, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } String schedule = (String)entry.get(SVNProperty.SCHEDULE); if (schedule != null && (!SVNProperty.SCHEDULE_ADD.equals(schedule) && !SVNProperty.SCHEDULE_DELETE.equals(schedule) && !SVNProperty.SCHEDULE_REPLACE.equals(schedule))) { schedule = null; } if (writeValue(writer, schedule, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } String textTime = (String)entry.get(SVNProperty.TEXT_TIME); if (writeValue(writer, textTime, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } String checksum = (String)entry.get(SVNProperty.CHECKSUM); if (writeValue(writer, checksum, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } String committedDate = (String)entry.get(SVNProperty.COMMITTED_DATE); if (writeValue(writer, committedDate, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } String committedRevision = (String)entry.get(SVNProperty.COMMITTED_REVISION); if (writeRevision(writer, committedRevision, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } String committedAuthor = (String)entry.get(SVNProperty.LAST_AUTHOR); if (writeString(writer, committedAuthor, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } String hasProps = (String)entry.get(SVNProperty.HAS_PROPS); if (SVNProperty.booleanValue(hasProps)) { writeValue(writer, SVNProperty.HAS_PROPS, emptyFields); emptyFields = 0; } else { ++emptyFields; } String hasPropMods = (String)entry.get(SVNProperty.HAS_PROP_MODS); if (SVNProperty.booleanValue(hasPropMods)) { writeValue(writer, SVNProperty.HAS_PROP_MODS, emptyFields); emptyFields = 0; } else { ++emptyFields; } String cachableProps = asString((String[])entry.get(SVNProperty.CACHABLE_PROPS), " "); if (!isThisDir) { String thisDirCachableProps = asString((String[])rootEntry.get(SVNProperty.CACHABLE_PROPS), " "); if (thisDirCachableProps != null && cachableProps != null && thisDirCachableProps.equals(cachableProps)) { cachableProps = null; } } if (writeValue(writer, cachableProps, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } String presentProps = asString((String[])entry.get(SVNProperty.PRESENT_PROPS), " "); if (writeValue(writer, presentProps, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } String propRejectFile = (String)entry.get(SVNProperty.PROP_REJECT_FILE); if (writeString(writer, propRejectFile, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } String conflictOldFile = (String)entry.get(SVNProperty.CONFLICT_OLD); if (writeString(writer, conflictOldFile, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } String conflictNewFile = (String)entry.get(SVNProperty.CONFLICT_NEW); if (writeString(writer, conflictNewFile, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } String conflictWrkFile = (String)entry.get(SVNProperty.CONFLICT_WRK); if (writeString(writer, conflictWrkFile, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } String copiedAttr = (String)entry.get(SVNProperty.COPIED); if (SVNProperty.booleanValue(copiedAttr)) { writeValue(writer, ATTRIBUTE_COPIED, emptyFields); emptyFields = 0; } else { ++emptyFields; } String copyfromURL = (String)entry.get(SVNProperty.COPYFROM_URL); if (writeString(writer, copyfromURL, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } String copyfromRevision = (String)entry.get(SVNProperty.COPYFROM_REVISION); if (writeRevision(writer, copyfromRevision, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } String deletedAttr = (String)entry.get(SVNProperty.DELETED); if (SVNProperty.booleanValue(deletedAttr)) { writeValue(writer, ATTRIBUTE_DELETED, emptyFields); emptyFields = 0; } else { ++emptyFields; } String absentAttr = (String)entry.get(SVNProperty.ABSENT); if (SVNProperty.booleanValue(absentAttr)) { writeValue(writer, ATTRIBUTE_ABSENT, emptyFields); emptyFields = 0; } else { ++emptyFields; } String incompleteAttr = (String)entry.get(SVNProperty.INCOMPLETE); if (SVNProperty.booleanValue(incompleteAttr)) { writeValue(writer, ATTRIBUTE_INCOMPLETE, emptyFields); emptyFields = 0; } else { ++emptyFields; } String uuid = (String)entry.get(SVNProperty.UUID); if (!isThisDir) { String thisDirUUID = (String)rootEntry.get(SVNProperty.UUID); if (thisDirUUID != null && uuid != null && thisDirUUID.equals(uuid)) { uuid = null; } } if (writeValue(writer, uuid, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } String lockToken = (String)entry.get(SVNProperty.LOCK_TOKEN); if (writeString(writer, lockToken, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } String lockOwner = (String)entry.get(SVNProperty.LOCK_OWNER); if (writeString(writer, lockOwner, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } String lockComment = (String)entry.get(SVNProperty.LOCK_COMMENT); if (writeString(writer, lockComment, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } String lockCreationDate = (String)entry.get(SVNProperty.LOCK_CREATION_DATE); writeValue(writer, lockCreationDate, emptyFields); writer.write("\f\n"); writer.flush(); } private boolean writeString(Writer writer, String str, int emptyFields) throws IOException { if (str != null && str.length() > 0) { for (int i = 0; i < emptyFields; i++) { writer.write('\n'); } for (int i = 0; i < str.length(); i++) { char ch = str.charAt(i); if (SVNEncodingUtil.isASCIIControlChar(ch) || ch == '\\') { writer.write("\\x"); writer.write(SVNFormatUtil.getHexNumberFromByte((byte)ch)); } else { writer.write(ch); } } writer.write('\n'); return true; } return false; } private boolean writeValue(Writer writer, String val, int emptyFields) throws IOException { if (val != null && val.length() > 0) { for (int i = 0; i < emptyFields; i++) { writer.write('\n'); } writer.write(val); writer.write('\n'); return true; } return false; } private boolean writeRevision(Writer writer, String rev, int emptyFields) throws IOException { if (rev != null && rev.length() > 0 && Long.parseLong(rev) >= 0) { for (int i = 0; i < emptyFields; i++) { writer.write('\n'); } writer.write(rev); writer.write('\n'); return true; } return false; } public boolean hasPropModifications(String name) throws SVNException { SVNEntry entry = getEntry(name, true); if (entry != null) { Map entryAttrs = entry.asMap(); return SVNProperty.booleanValue((String)entryAttrs.get(SVNProperty.HAS_PROP_MODS)); } return false; } public boolean hasProperties(String name) throws SVNException { SVNEntry entry = getEntry(name, true); if (entry != null) { Map entryAttrs = entry.asMap(); return SVNProperty.booleanValue((String)entryAttrs.get(SVNProperty.HAS_PROPS)); } return false; } public boolean lock() throws SVNException { return lock(false); } public boolean lock(boolean stealLock) throws SVNException { if (!isVersioned()) { return false; } if (stealLock && myLockFile.isFile()) { setLocked(true); return true; } boolean created = false; try { created = myLockFile.createNewFile(); } catch (IOException e) { SVNErrorCode code = e.getMessage().indexOf("denied") >= 0 ? SVNErrorCode.WC_LOCKED : SVNErrorCode.WC_NOT_LOCKED; SVNErrorMessage err = SVNErrorMessage.create(code, "Cannot lock working copy ''{0}'': {1}", new Object[] {getRoot(), e.getLocalizedMessage()}); SVNErrorManager.error(err, e); } if (created) { setLocked(true); return created; } if (myLockFile.isFile()) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_LOCKED, "Working copy ''{0}'' locked; try performing ''cleanup''", getRoot()); SVNErrorManager.error(err); } else { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_NOT_LOCKED, "Cannot lock working copy ''{0}''", getRoot()); SVNErrorManager.error(err); } return false; } private void createFormatFile(File formatFile, boolean createMyself) throws SVNException { OutputStream os = null; try { formatFile = createMyself ? getAdminFile("format") : formatFile; os = SVNFileUtil.openFileForWriting(formatFile); os.write(String.valueOf(WC_FORMAT).getBytes("UTF-8")); os.write('\n'); } catch (IOException e) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e.getLocalizedMessage()); SVNErrorManager.error(err, e); } finally { SVNFileUtil.closeFile(os); } } public SVNAdminArea createVersionedDirectory(File dir, String url, String rootURL, String uuid, long revNumber, boolean createMyself) throws SVNException { dir = createMyself ? getRoot() : dir; dir.mkdirs(); File adminDir = createMyself ? getAdminDirectory() : new File(dir, SVNFileUtil.getAdminDirectoryName()); adminDir.mkdir(); SVNFileUtil.setHidden(adminDir, true); // lock dir. File lockFile = createMyself ? myLockFile : new File(adminDir, "lock"); SVNFileUtil.createEmptyFile(lockFile); File[] tmp = { createMyself ? getAdminFile("tmp") : new File(adminDir, "tmp"), createMyself ? getAdminFile("tmp" + File.separatorChar + "props") : new File(adminDir, "tmp" + File.separatorChar + "props"), createMyself ? getAdminFile("tmp" + File.separatorChar + "prop-base") : new File(adminDir, "tmp" + File.separatorChar + "prop-base"), createMyself ? getAdminFile("tmp" + File.separatorChar + "text-base") : new File(adminDir, "tmp" + File.separatorChar + "text-base"), createMyself ? getAdminFile("props") : new File(adminDir, "props"), createMyself ? getAdminFile("prop-base") : new File(adminDir, "prop-base"), createMyself ? getAdminFile("text-base") : new File(adminDir, "text-base") }; for (int i = 0; i < tmp.length; i++) { tmp[i].mkdir(); } // for backward compatibility createFormatFile(createMyself ? null : new File(adminDir, "format"), createMyself); SVNAdminArea adminArea = createMyself ? this : new SVNAdminArea14(dir); adminArea.setLocked(true); SVNEntry rootEntry = adminArea.getEntry(adminArea.getThisDirName(), true); if (rootEntry == null) { rootEntry = adminArea.addEntry(adminArea.getThisDirName()); } if (url != null) { rootEntry.setURL(url); } rootEntry.setRepositoryRoot(rootURL); rootEntry.setRevision(revNumber); rootEntry.setKind(SVNNodeKind.DIR); if (uuid != null) { rootEntry.setUUID(uuid); } if (revNumber > 0) { rootEntry.setIncomplete(true); } rootEntry.setCachableProperties(ourCachableProperties); try { adminArea.saveEntries(false); } catch (SVNException svne) { SVNErrorMessage err = svne.getErrorMessage().wrap("Error writing entries file for ''{0}''", dir); SVNErrorManager.error(err, svne); } // unlock dir. SVNFileUtil.deleteFile(lockFile); return adminArea; } public SVNAdminArea upgradeFormat(SVNAdminArea adminArea) throws SVNException { File logFile = adminArea.getAdminFile("log"); SVNFileType type = SVNFileType.getType(logFile); if (type == SVNFileType.FILE) { SVNDebugLog.getDefaultLog().info("Upgrade failed: found a log file at '" + logFile + "'"); return adminArea; } SVNLog log = getLog(); Map command = new HashMap(); command.put(SVNLog.FORMAT_ATTR, String.valueOf(getFormatVersion())); log.addCommand(SVNLog.UPGRADE_FORMAT, command, false); command.clear(); setWCAccess(adminArea.getWCAccess()); Iterator entries = adminArea.entries(true); myEntries = new HashMap(); Map basePropsCache = getBasePropertiesStorage(true); Map propsCache = getPropertiesStorage(true); for (; entries.hasNext();) { SVNEntry entry = (SVNEntry) entries.next(); SVNEntry newEntry = new SVNEntry(new HashMap(entry.asMap()), this, entry.getName()); myEntries.put(entry.getName(), newEntry); if (entry.getKind() != SVNNodeKind.FILE && !adminArea.getThisDirName().equals(entry.getName())) { continue; } SVNVersionedProperties srcBaseProps = adminArea.getBaseProperties(entry.getName()); Map basePropsHolder = srcBaseProps.asMap(); SVNVersionedProperties dstBaseProps = new SVNProperties13(basePropsHolder); basePropsCache.put(entry.getName(), dstBaseProps); dstBaseProps.setModified(true); SVNVersionedProperties srcProps = adminArea.getProperties(entry.getName()); SVNVersionedProperties dstProps = new SVNProperties14(srcProps.asMap(), this, entry.getName()){ protected Map loadProperties() throws SVNException { return getPropertiesMap(); } }; propsCache.put(entry.getName(), dstProps); dstProps.setModified(true); command.put(SVNLog.NAME_ATTR, entry.getName()); command.put(SVNProperty.shortPropertyName(SVNProperty.PROP_TIME), SVNTimeUtil.formatDate(new Date(0), true)); log.addCommand(SVNLog.MODIFY_ENTRY, command, false); command.clear(); SVNVersionedProperties wcProps = adminArea.getWCProperties(entry.getName()); log.logChangedWCProperties(entry.getName(), wcProps.asMap()); } saveVersionedProperties(log, true); log.save(); SVNFileUtil.deleteFile(getAdminFile("README.txt")); SVNFileUtil.deleteFile(getAdminFile("empty-file")); SVNFileUtil.deleteAll(getAdminFile("wcprops"), true); SVNFileUtil.deleteAll(getAdminFile("tmp/wcprops"), true); SVNFileUtil.deleteAll(getAdminFile("dir-wcprops"), true); runLogs(); return this; } public void postUpgradeFormat(int format) throws SVNException { if (format == WC_FORMAT) { createFormatFile(null, true); return; } SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNKNOWN, "Unexpected format number:\n" + " expected: {0,number,integer}\n" + " actual: {1,number,integer}", new Object[]{new Integer(WC_FORMAT), new Integer(format)}); SVNErrorManager.error(err); } public void postCommit(String fileName, long revisionNumber, boolean implicit, SVNErrorCode errorCode) throws SVNException { SVNEntry entry = getEntry(fileName, true); if (entry == null || (!getThisDirName().equals(fileName) && entry.getKind() != SVNNodeKind.FILE)) { SVNErrorMessage err = SVNErrorMessage.create(errorCode, "Log command for directory ''{0}'' is mislocated", getRoot()); SVNErrorManager.error(err); } if (!implicit && entry.isScheduledForDeletion()) { if (getThisDirName().equals(fileName)) { entry.setRevision(revisionNumber); entry.setKind(SVNNodeKind.DIR); File killMe = getAdminFile("KILLME"); if (killMe.getParentFile().isDirectory()) { try { killMe.createNewFile(); } catch (IOException e) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, "Cannot create file ''{0}'': {1}", new Object[] {killMe, e.getLocalizedMessage()}); SVNErrorManager.error(err, e); } } } else { removeFromRevisionControl(fileName, false, false); SVNEntry parentEntry = getEntry(getThisDirName(), true); if (revisionNumber > parentEntry.getRevision()) { SVNEntry fileEntry = addEntry(fileName); fileEntry.setKind(SVNNodeKind.FILE); fileEntry.setDeleted(true); fileEntry.setRevision(revisionNumber); } } return; } if (!implicit && entry.isScheduledForReplacement() && getThisDirName().equals(fileName)) { for (Iterator ents = entries(true); ents.hasNext();) { SVNEntry currentEntry = (SVNEntry) ents.next(); if (!currentEntry.isScheduledForDeletion()) { continue; } if (currentEntry.getKind() == SVNNodeKind.FILE || currentEntry.getKind() == SVNNodeKind.DIR) { removeFromRevisionControl(currentEntry.getName(), false, false); } } } long textTime = 0; if (!implicit && !getThisDirName().equals(fileName)) { File tmpFile = getBaseFile(fileName, true); SVNFileType fileType = SVNFileType.getType(tmpFile); if (fileType == SVNFileType.FILE || fileType == SVNFileType.SYMLINK) { boolean modified = false; File workingFile = getFile(fileName); long tmpTimestamp = tmpFile.lastModified(); long wkTimestamp = workingFile.lastModified(); if (tmpTimestamp != wkTimestamp) { // check if wc file is not modified File tmpFile2 = SVNFileUtil.createUniqueFile(tmpFile.getParentFile(), fileName, ".tmp"); try { String tmpFile2Path = SVNFileUtil.getBasePath(tmpFile2); SVNTranslator.translate(this, fileName, fileName, tmpFile2Path, false); modified = !SVNFileUtil.compareFiles(tmpFile, tmpFile2, null); } catch (SVNException svne) { SVNErrorMessage err = SVNErrorMessage.create(errorCode, "Error comparing ''{0}'' and ''{1}''", new Object[] {workingFile, tmpFile}); SVNErrorManager.error(err, svne); } finally { tmpFile2.delete(); } } textTime = modified ? tmpTimestamp : wkTimestamp; } } if (!implicit && entry.isScheduledForReplacement()) { SVNFileUtil.deleteFile(getBasePropertiesFile(fileName, false)); } boolean setReadWrite = false; boolean setNotExecutable = false; SVNVersionedProperties baseProps = getBaseProperties(fileName); SVNVersionedProperties wcProps = getProperties(fileName); //TODO: to work properly we must create a tmp working props file //instead of tmp base props one File tmpPropsFile = getPropertiesFile(fileName, true); File wcPropsFile = getPropertiesFile(fileName, false); File basePropertiesFile = getBasePropertiesFile(fileName, false); SVNFileType tmpPropsType = SVNFileType.getType(tmpPropsFile); // tmp may be missing when there were no prop change at all! if (tmpPropsType == SVNFileType.FILE) { if (!getThisDirName().equals(fileName)) { SVNVersionedProperties propDiff = baseProps.compareTo(wcProps); setReadWrite = propDiff != null && propDiff.containsProperty(SVNProperty.NEEDS_LOCK) && propDiff.getPropertyValue(SVNProperty.NEEDS_LOCK) == null; setNotExecutable = propDiff != null && propDiff.containsProperty(SVNProperty.EXECUTABLE) && propDiff.getPropertyValue(SVNProperty.EXECUTABLE) == null; } try { if (!tmpPropsFile.exists() || tmpPropsFile.length() <= 4) { SVNFileUtil.deleteFile(basePropertiesFile); } else { SVNFileUtil.copyFile(tmpPropsFile, basePropertiesFile, true); SVNFileUtil.setReadonly(basePropertiesFile, true); } } finally { SVNFileUtil.deleteFile(tmpPropsFile); } } if (!getThisDirName().equals(fileName) && !implicit) { File tmpFile = getBaseFile(fileName, true); File baseFile = getBaseFile(fileName, false); File wcFile = getFile(fileName); File tmpFile2 = null; try { tmpFile2 = SVNFileUtil.createUniqueFile(tmpFile.getParentFile(), fileName, ".tmp"); boolean overwritten = false; SVNFileType fileType = SVNFileType.getType(tmpFile); boolean special = getProperties(fileName).getPropertyValue(SVNProperty.SPECIAL) != null; if (SVNFileUtil.isWindows || !special) { if (fileType == SVNFileType.FILE) { SVNTranslator.translate(this, fileName, SVNFileUtil.getBasePath(tmpFile), SVNFileUtil.getBasePath(tmpFile2), true); } else { SVNTranslator.translate(this, fileName, fileName, SVNFileUtil.getBasePath(tmpFile2), true); } if (!SVNFileUtil.compareFiles(tmpFile2, wcFile, null)) { SVNFileUtil.copyFile(tmpFile2, wcFile, true); overwritten = true; } } boolean needsReadonly = getProperties(fileName).getPropertyValue(SVNProperty.NEEDS_LOCK) != null && entry.getLockToken() == null; boolean needsExecutable = getProperties(fileName).getPropertyValue(SVNProperty.EXECUTABLE) != null; if (needsReadonly) { SVNFileUtil.setReadonly(wcFile, true); overwritten = true; } if (needsExecutable) { SVNFileUtil.setExecutable(wcFile, true); overwritten = true; } if (fileType == SVNFileType.FILE) { SVNFileUtil.rename(tmpFile, baseFile); } if (setReadWrite) { SVNFileUtil.setReadonly(wcFile, false); overwritten = true; } if (setNotExecutable) { SVNFileUtil.setExecutable(wcFile, false); overwritten = true; } if (overwritten) { textTime = wcFile.lastModified(); } } catch (SVNException svne) { SVNErrorMessage err = SVNErrorMessage.create(errorCode, "Error replacing text-base of ''{0}''", fileName); SVNErrorManager.error(err, svne); } finally { tmpFile2.delete(); tmpFile.delete(); } } // update entry Map entryAttrs = new HashMap(); entryAttrs.put(SVNProperty.shortPropertyName(SVNProperty.REVISION), SVNProperty.toString(revisionNumber)); entryAttrs.put(SVNProperty.shortPropertyName(SVNProperty.KIND), getThisDirName().equals(fileName) ? SVNProperty.KIND_DIR : SVNProperty.KIND_FILE); if (!implicit) { entryAttrs.put(SVNProperty.shortPropertyName(SVNProperty.SCHEDULE), null); } entryAttrs.put(SVNProperty.shortPropertyName(SVNProperty.COPIED), SVNProperty.toString(false)); entryAttrs.put(SVNProperty.shortPropertyName(SVNProperty.DELETED), SVNProperty.toString(false)); if (textTime != 0 && !implicit) { entryAttrs.put(SVNProperty.shortPropertyName(SVNProperty.TEXT_TIME), SVNTimeUtil.formatDate(new Date(textTime))); } entryAttrs.put(SVNProperty.shortPropertyName(SVNProperty.CONFLICT_NEW), null); entryAttrs.put(SVNProperty.shortPropertyName(SVNProperty.CONFLICT_OLD), null); entryAttrs.put(SVNProperty.shortPropertyName(SVNProperty.CONFLICT_WRK), null); entryAttrs.put(SVNProperty.shortPropertyName(SVNProperty.PROP_REJECT_FILE), null); entryAttrs.put(SVNProperty.shortPropertyName(SVNProperty.COPYFROM_REVISION), null); entryAttrs.put(SVNProperty.shortPropertyName(SVNProperty.COPYFROM_URL), null); entryAttrs.put(SVNProperty.shortPropertyName(SVNProperty.HAS_PROP_MODS), SVNProperty.toString(false)); try { modifyEntry(fileName, entryAttrs, false, true); } catch (SVNException svne) { SVNErrorMessage err = SVNErrorMessage.create(errorCode, "Error modifying entry of ''{0}''", fileName); SVNErrorManager.error(err, svne); } SVNFileUtil.deleteFile(wcPropsFile); if (!getThisDirName().equals(fileName)) { return; } // update entry in parent. File dirFile = getRoot(); if (getWCAccess().isWCRoot(getRoot())) { return; } boolean unassociated = false; SVNAdminArea parentArea = null; try { parentArea = getWCAccess().retrieve(dirFile.getParentFile()); } catch (SVNException svne) { if (svne.getErrorMessage().getErrorCode() == SVNErrorCode.WC_NOT_LOCKED) { parentArea = getWCAccess().open(dirFile.getParentFile(), true, false, 0); unassociated = true; } throw svne; } SVNEntry entryInParent = parentArea.getEntry(dirFile.getName(), false); if (entryInParent != null) { entryAttrs.clear(); if (!implicit) { entryAttrs.put(SVNProperty.shortPropertyName(SVNProperty.SCHEDULE), null); } entryAttrs.put(SVNProperty.shortPropertyName(SVNProperty.COPIED), SVNProperty.toString(false)); entryAttrs.put(SVNProperty.shortPropertyName(SVNProperty.COPYFROM_REVISION), null); entryAttrs.put(SVNProperty.shortPropertyName(SVNProperty.COPYFROM_URL), null); entryAttrs.put(SVNProperty.shortPropertyName(SVNProperty.DELETED), SVNProperty.toString(false)); try { parentArea.modifyEntry(entryInParent.getName(), entryAttrs, true, true); } catch (SVNException svne) { SVNErrorMessage err = SVNErrorMessage.create(errorCode, "Error modifying entry of ''{0}''", fileName); SVNErrorManager.error(err, svne); } } parentArea.saveEntries(false); if (unassociated) { getWCAccess().closeAdminArea(dirFile.getParentFile()); } } public boolean unlock() throws SVNException { if (!myLockFile.exists()) { return true; } // only if there are not locks or killme files. boolean killMe = getAdminFile("KILLME").exists(); if (killMe) { return false; } File[] logs = SVNFileListUtil.listFiles(getAdminDirectory()); for (int i = 0; logs != null && i < logs.length; i++) { File log = logs[i]; if ("log".equals(log.getName()) || log.getName().startsWith("log.")) { return false; } } boolean deleted = myLockFile.delete(); if (!deleted) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_LOCKED, "Failed to unlock working copy ''{0}''", getRoot()); SVNErrorManager.error(err); } return deleted; } public boolean isVersioned() { if (getAdminDirectory().isDirectory() && myEntriesFile.canRead()) { try { if (getEntry("", false) != null) { return true; } } catch (SVNException e) { // } } return false; } public boolean isLocked() throws SVNException { if (!myWasLocked) { return false; } SVNFileType type = SVNFileType.getType(myLockFile); if (type == SVNFileType.FILE) { return true; } else if (type == SVNFileType.NONE) { return false; } SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_LOCKED, "Lock file ''{0}'' is not a regular file", myLockFile); SVNErrorManager.error(err); return false; } protected int getFormatVersion() { return WC_FORMAT; } protected boolean isEntryPropertyApplicable(String name) { return true; } }