/* * ==================================================================== * Copyright (c) 2004-2012 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 org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.internal.io.fs.FSFile; import org.tmatesoft.svn.core.internal.util.*; import org.tmatesoft.svn.core.internal.wc.*; import org.tmatesoft.svn.core.io.SVNRepository; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc.SVNTreeConflictDescription; import org.tmatesoft.svn.util.SVNDebugLog; import org.tmatesoft.svn.util.SVNLogType; import java.io.*; import java.util.*; /** * @version 1.3 * @author TMate Software Ltd. */ public class SVNAdminArea14 extends SVNAdminArea { public static final int WC_FORMAT = SVNAdminArea14Factory.WC_FORMAT; public static final String[] ourCachableProperties = new String[] { SVNProperty.SPECIAL, SVNProperty.EXTERNALS, SVNProperty.NEEDS_LOCK }; protected static final String ATTRIBUTE_COPIED = "copied"; protected static final String ATTRIBUTE_DELETED = "deleted"; protected static final String ATTRIBUTE_ABSENT = "absent"; protected static final String ATTRIBUTE_INCOMPLETE = "incomplete"; protected static final String ATTRIBUTE_HAS_PROPS = "has-props"; protected static final String ATTRIBUTE_HAS_PROP_MODS = "has-prop-mods"; protected static final String KILL_ADM_ONLY = "adm-only"; protected static final String THIS_DIR = ""; private static final Set INAPPLICABLE_PROPERTIES = new SVNHashSet(); static { INAPPLICABLE_PROPERTIES.add(SVNProperty.KEEP_LOCAL); INAPPLICABLE_PROPERTIES.add(SVNProperty.CHANGELIST); INAPPLICABLE_PROPERTIES.add(SVNProperty.WORKING_SIZE); INAPPLICABLE_PROPERTIES.add(SVNProperty.DEPTH); INAPPLICABLE_PROPERTIES.add(SVNProperty.PROP_TIME); INAPPLICABLE_PROPERTIES.add(SVNProperty.FILE_EXTERNAL_PATH); INAPPLICABLE_PROPERTIES.add(SVNProperty.FILE_EXTERNAL_REVISION); INAPPLICABLE_PROPERTIES.add(SVNProperty.FILE_EXTERNAL_PEG_REVISION); INAPPLICABLE_PROPERTIES.add(SVNProperty.TREE_CONFLICT_DATA); } private File myLockFile; private File myEntriesFile; private static boolean ourIsOptimizedWritingEnabled; public SVNAdminArea14(File dir) { super(dir); myLockFile = new File(getAdminDirectory(), "lock"); myEntriesFile = new File(getAdminDirectory(), "entries"); } public static void setOptimizedWritingEnabled(boolean enabled) { ourIsOptimizedWritingEnabled = enabled; } 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()) { SVNWCProperties.setProperties(props.asMap(), target, SVNWCProperties.SVN_HASH_TERMINATOR); } else { SVNWCProperties.setProperties(new SVNProperties(), target, SVNWCProperties.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'); SVNWCProperties.setProperties(props.asMap(), target, SVNWCProperties.SVN_HASH_TERMINATOR); } } } catch (IOException ioe) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, ioe.getLocalizedMessage()); SVNErrorManager.error(err, ioe, SVNLogType.WC); } 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; } SVNProperties baseProps = null; try { baseProps = readBaseProperties(name); } catch (SVNException svne) { SVNErrorMessage err = svne.getErrorMessage().wrap("Failed to load properties from disk"); SVNErrorManager.error(err, SVNLogType.DEFAULT); } 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; } SVNProperties revertProps = null; try { revertProps = readRevertProperties(name); } catch (SVNException svne) { SVNErrorMessage err = svne.getErrorMessage().wrap("Failed to load properties from disk"); SVNErrorManager.error(err, SVNLogType.DEFAULT); } 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 SVNProperties loadProperties() throws SVNException { SVNProperties props = getProperties(); if (props == null) { try { props = readProperties(entryName); } catch (SVNException svne) { SVNErrorMessage err = svne.getErrorMessage().wrap("Failed to load properties from disk"); SVNErrorManager.error(err, SVNLogType.DEFAULT); } props = props != null ? props : new SVNProperties(); 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 SVNProperties()); 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); SVNProperties wcProps = wcpropsFile.readProperties(false, true); 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, SVNLogType.WC); } break; } wcProps = wcpropsFile.readProperties(false, true); 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, SVNLogType.DEFAULT); } finally { wcpropsFile.close(); } return wcPropsCache; } protected SVNProperties readBaseProperties(String name) throws SVNException { File propertiesFile = getBasePropertiesFile(name, false); SVNWCProperties props = new SVNWCProperties(propertiesFile, null); return props.asMap(); } protected SVNProperties readRevertProperties(String name) throws SVNException { File propertiesFile = getRevertPropertiesFile(name, false); SVNWCProperties props = new SVNWCProperties(propertiesFile, null); return props.asMap(); } protected SVNProperties readProperties(String name) throws SVNException { if (hasPropModifications(name)) { File propertiesFile = getPropertiesFile(name, false); SVNWCProperties props = new SVNWCProperties(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 SVNProperties(); } public void saveVersionedProperties(SVNLog log, boolean close) throws SVNException { SVNProperties command = new SVNProperties(); Set processedEntries = new SVNHashSet(); 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.shortPropertyName(SVNProperty.CACHABLE_PROPS), asString(cachableProps, " ")); SVNProperties propsMap = props.loadProperties(); LinkedList presentProps = new LinkedList(); for (int i = 0; i < cachableProps.length; i++) { if (propsMap.containsName(cachableProps[i])) { presentProps.addLast(cachableProps[i]); } } if (presentProps.size() > 0) { String presentPropsString = asString((String[])presentProps.toArray(new String[presentProps.size()]), " "); command.put(SVNProperty.shortPropertyName(SVNProperty.PRESENT_PROPS), presentPropsString); } else { command.put(SVNProperty.shortPropertyName(SVNProperty.PRESENT_PROPS), ""); } command.put(SVNProperty.shortPropertyName(SVNProperty.HAS_PROPS), SVNProperty.toString(!props.isEmpty())); boolean hasPropModifications = !propsDiff.isEmpty(); command.put(SVNProperty.shortPropertyName(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; SVNWCProperties tmpProps = new SVNWCProperties(tmpFile, srcPath); if (!props.isEmpty()) { tmpProps.setProperties(props.asMap()); } else { SVNFileUtil.createEmptyFile(tmpFile); } 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.shortPropertyName(SVNProperty.CACHABLE_PROPS), asString(cachableProps, " ")); SVNProperties propsMap = props.loadProperties(); LinkedList presentProps = new LinkedList(); for (int i = 0; i < cachableProps.length; i++) { if (propsMap.containsName(cachableProps[i])) { presentProps.addLast(cachableProps[i]); } } if (presentProps.size() > 0) { String presentPropsString = asString((String[])presentProps.toArray(new String[presentProps.size()]), " "); command.put(SVNProperty.shortPropertyName(SVNProperty.PRESENT_PROPS), presentPropsString); } else { command.put(SVNProperty.shortPropertyName(SVNProperty.PRESENT_PROPS), ""); } command.put(SVNProperty.shortPropertyName(SVNProperty.HAS_PROPS), SVNProperty.toString(!props.isEmpty())); SVNVersionedProperties propsDiff = baseProps.compareTo(props); boolean hasPropModifications = !propsDiff.isEmpty(); command.put(SVNProperty.shortPropertyName(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; SVNWCProperties tmpProps = new SVNWCProperties(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 installProperties(String name, SVNProperties baseProps, SVNProperties workingProps, SVNLog log, boolean writeBaseProps, boolean close) throws SVNException { SVNProperties command = new SVNProperties(); SVNNodeKind kind = name.equals(getThisDirName()) ? SVNNodeKind.DIR : SVNNodeKind.FILE; SVNProperties propDiff = baseProps.compareTo(workingProps); boolean hasPropMods = !propDiff.isEmpty(); command.put(SVNProperty.shortPropertyName(SVNProperty.HAS_PROP_MODS), SVNProperty.toString(hasPropMods)); command.put(SVNProperty.shortPropertyName(SVNProperty.HAS_PROPS), SVNProperty.toString(!workingProps.isEmpty())); String[] cachableProps = SVNAdminArea14.getCachableProperties(); command.put(SVNProperty.shortPropertyName(SVNProperty.CACHABLE_PROPS), asString(cachableProps, " ")); LinkedList presentProps = new LinkedList(); for (int i = 0; i < cachableProps.length; i++) { if (workingProps.containsName(cachableProps[i])) { presentProps.addLast(cachableProps[i]); } } if (presentProps.size() > 0) { String presentPropsString = asString((String[]) presentProps.toArray(new String[presentProps.size()]), " "); command.put(SVNProperty.shortPropertyName(SVNProperty.PRESENT_PROPS), presentPropsString); } else { command.put(SVNProperty.shortPropertyName(SVNProperty.PRESENT_PROPS), ""); } command.put(SVNLog.NAME_ATTR, name); log.addCommand(SVNLog.MODIFY_ENTRY, command, false); command.clear(); String dstPath = SVNAdminUtil.getPropPath(name, kind, false); if (hasPropMods) { String tmpPath = SVNAdminUtil.getPropPath(name, kind, true); File tmpFile = getFile(tmpPath); SVNWCProperties tmpProps = new SVNWCProperties(tmpFile, tmpPath); if (!workingProps.isEmpty()) { tmpProps.setProperties(workingProps); } else { SVNFileUtil.createEmptyFile(tmpFile); } command.put(SVNLog.NAME_ATTR, tmpPath); 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 { if (hasPropModifications(name)) { command.put(SVNLog.NAME_ATTR, dstPath); log.addCommand(SVNLog.DELETE, command, false); } } command.clear(); if (writeBaseProps) { String basePath = SVNAdminUtil.getPropBasePath(name, kind, false); if (!baseProps.isEmpty()) { String tmpPath = SVNAdminUtil.getPropBasePath(name, kind, true); File tmpFile = getFile(tmpPath); SVNWCProperties tmpProps = new SVNWCProperties(tmpFile, tmpPath); tmpProps.setProperties(baseProps); command.put(SVNLog.NAME_ATTR, tmpPath); command.put(SVNLog.DEST_ATTR, basePath); log.addCommand(SVNLog.MOVE, command, false); command.clear(); command.put(SVNLog.NAME_ATTR, basePath); log.addCommand(SVNLog.READONLY, command, false); } else { if (hasProperties(name)) { command.put(SVNLog.NAME_ATTR, basePath); log.addCommand(SVNLog.DELETE, command, false); } } } if (close) { closeVersionedProperties(); } } public void handleKillMe() throws SVNException { boolean killMe = isKillMe(); if (killMe) { String contents = SVNFileUtil.readFile(getAdminFile(ADM_KILLME)); boolean killAdmOnly = KILL_ADM_ONLY.equals(contents); SVNEntry entry = getEntry(getThisDirName(), false); long dirRevision = entry != null ? entry.getRevision() : -1; // deleted dir, files and entry in parent. File dir = getRoot(); SVNWCAccess access = getWCAccess(); boolean isWCRoot = access.isWCRoot(getRoot()); try { removeFromRevisionControl(getThisDirName(), !killAdmOnly, false); } catch (SVNException svne) { if (svne.getErrorMessage().getErrorCode() != SVNErrorCode.WC_LEFT_LOCAL_MOD) { throw svne; } } if (isWCRoot) { return; } // compare revision with parent's one SVNAdminArea parentArea = access.retrieve(dir.getParentFile()); SVNEntry parentEntry = parentArea.getEntry(parentArea.getThisDirName(), false); if (dirRevision > parentEntry.getRevision()) { SVNEntry entryInParent = parentArea.addEntry(dir.getName()); Map attributes = new SVNHashMap(); attributes.put(SVNProperty.DELETED, Boolean.TRUE.toString()); attributes.put(SVNProperty.KIND, SVNProperty.KIND_DIR); attributes.put(SVNProperty.REVISION, Long.toString(dirRevision)); parentArea.modifyEntry(entryInParent.getName(), attributes, true, false); } } } 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, SVNLogType.DEFAULT); } 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, SVNLogType.DEFAULT); } 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, SVNLogType.DEFAULT); } if (ourIsOptimizedWritingEnabled) { File tmpFile = new File(getAdminDirectory(), "tmp/entries"); boolean renamed = myEntriesFile.renameTo(tmpFile); if (!renamed) { myEntriesFile.delete(); tmpFile = null; } Writer os = null; try { os = new OutputStreamWriter(SVNFileUtil.openFileForWriting(myEntriesFile), "UTF-8"); writeEntries(os); } catch (IOException e) { SVNFileUtil.closeFile(os); if (tmpFile != null) { SVNFileUtil.rename(tmpFile, myEntriesFile); } SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, "Cannot write entries file ''{0}'': {1}", new Object[] {myEntriesFile, e.getLocalizedMessage()}); SVNErrorManager.error(err, e, SVNLogType.WC); } finally { SVNFileUtil.closeFile(os); SVNFileUtil.deleteFile(tmpFile); } SVNFileUtil.setReadonly(myEntriesFile, true); } else { 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 write entries file ''{0}'': {1}", new Object[] {myEntriesFile, e.getMessage()}); SVNErrorManager.error(err, e, SVNLogType.WC); } finally { SVNFileUtil.closeFile(os); } SVNFileUtil.setReadonly(tmpFile, true); SVNFileUtil.rename(tmpFile, myEntriesFile); } if (close) { closeEntries(); } } } protected Map fetchEntries() throws SVNException { if (!myEntriesFile.exists()) { return null; } Map entries = new SVNHashMap(); BufferedReader reader = null; try { reader = new BufferedReader(new InputStreamReader(SVNFileUtil.openFileForReading(myEntriesFile, SVNLogType.WC), "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, svne, SVNLogType.WC); } ++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, SVNLogType.WC); } 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, SVNLogType.WC); } if (defaultEntry.getRevision() < 0) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_MISSING_REVISION, "Default entry has no revision number"); SVNErrorManager.error(err, SVNLogType.WC); } if (defaultEntry.getURL() == null) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_MISSING_URL, "Default entry is missing URL"); SVNErrorManager.error(err, SVNLogType.WC); } for (Iterator entriesIter = entries.keySet().iterator(); entriesIter.hasNext();) { String name = (String)entriesIter.next(); SVNEntry entry = (SVNEntry)entries.get(name); if (getThisDirName().equals(name)) { continue; } SVNNodeKind kind = entry.getKind(); if (kind == SVNNodeKind.FILE) { if (entry.getRevision() < 0) { entry.setRevision(defaultEntry.getRevision()); } if (entry.getURL() == null) { entry.setParentURL(defaultEntry.getURL()); } if (entry.getRepositoryRoot() == null) { entry.setRepositoryRoot(defaultEntry.getRepositoryRoot()); } if (entry.getUUID() == null) { if (!(entry.isScheduledForAddition() || entry.isScheduledForReplacement())) { entry.setUUID(defaultEntry.getUUID()); } } if (entry.getCachableProperties() == null) { entry.setCachableProperties(defaultEntry.getCachableProperties()); } } } return entries; } protected 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(); name = (String) getObjectsPool().getObject(name); SVNEntry entry = new SVNEntry16(this, name); entry.setDepth(SVNDepth.INFINITY); line = reader.readLine(); String kind = parseValue(line); if (kind != null) { SVNNodeKind parsedKind = SVNNodeKind.parseKind(kind); if (parsedKind != SVNNodeKind.UNKNOWN && parsedKind != SVNNodeKind.NONE) { entry.setKind(parsedKind); } else { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.NODE_UNKNOWN_KIND, "Entry ''{0}'' has invalid node kind", name); SVNErrorManager.error(err, SVNLogType.WC); } } else { entry.setKind(SVNNodeKind.NONE); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String revision = parseValue(line); if (revision != null) { try { long rev = Long.parseLong(revision); entry.setRevision(rev); } catch (NumberFormatException nfe) { entry.setRevision(SVNRepository.INVALID_REVISION); } } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String url = parseString(line); if (url != null) { entry.setURL(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, SVNLogType.WC); } else if (reposRoot != null) { reposRoot = (String) getObjectsPool().getObject(reposRoot); entry.setRepositoryRoot(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)) { entry.setSchedule(schedule); } else { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_ATTRIBUTE_INVALID, "Entry ''{0}'' has invalid ''{1}'' value", new Object[]{name, SVNProperty.SCHEDULE}); SVNErrorManager.error(err, SVNLogType.WC); } } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String timestamp = parseValue(line); if (timestamp != null) { entry.setTextTime(timestamp); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String checksum = parseString(line); if (checksum != null) { entry.setChecksum(checksum); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String committedDate = parseValue(line); if (committedDate != null) { entry.setCommittedDate(committedDate); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String committedRevision = parseValue(line); if (committedRevision != null) { try { long rev = Long.parseLong(committedRevision); entry.setCommittedRevision(rev); } catch (NumberFormatException nfe) { entry.setCommittedRevision(SVNRepository.INVALID_REVISION); } } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String committedAuthor = parseString(line); if (committedAuthor != null) { committedAuthor = (String) getObjectsPool().getObject(committedAuthor); entry.setAuthor(committedAuthor); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } boolean hasProps = parseBoolean(line, ATTRIBUTE_HAS_PROPS); if (hasProps && entry instanceof SVNEntry16) { ((SVNEntry) entry).setHasProperties(hasProps); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } boolean hasPropMods = parseBoolean(line, ATTRIBUTE_HAS_PROP_MODS); if (hasPropMods && entry instanceof SVNEntry16) { ((SVNEntry) entry).setHasPropertiesModifications(hasPropMods); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String cachablePropsStr = parseValue(line); if (cachablePropsStr != null) { String[] cachableProps = fromString(cachablePropsStr, " "); if (Arrays.equals(cachableProps, getCachableProperties())) { cachableProps = getCachableProperties(); } entry.setCachableProperties(cachableProps); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String presentPropsStr = parseValue(line); if (presentPropsStr != null && entry instanceof SVNEntry16) { String[] presentProps = fromString(presentPropsStr, " "); ((SVNEntry) entry).setPresentProperties(presentProps); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String prejFile = parseString(line); if (prejFile != null) { entry.setPropRejectFile(prejFile); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String conflictOldFile = parseString(line); if (conflictOldFile != null) { entry.setConflictOld(conflictOldFile); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String conflictNewFile = parseString(line); if (conflictNewFile != null) { entry.setConflictNew(conflictNewFile); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String conflictWorkFile = parseString(line); if (conflictWorkFile != null) { entry.setConflictWorking(conflictWorkFile); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } boolean isCopied = parseBoolean(line, ATTRIBUTE_COPIED); if (isCopied) { entry.setCopied(isCopied); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String copyfromURL = parseString(line); if (copyfromURL != null) { entry.setCopyFromURL(copyfromURL); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String copyfromRevision = parseValue(line); if (copyfromRevision != null) { try { long rev = Long.parseLong(copyfromRevision); entry.setCopyFromRevision(rev); } catch (NumberFormatException nfe) { entry.setCopyFromRevision(SVNRepository.INVALID_REVISION); } } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } boolean isDeleted = parseBoolean(line, ATTRIBUTE_DELETED); if (isDeleted) { entry.setDeleted(isDeleted); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } boolean isAbsent = parseBoolean(line, ATTRIBUTE_ABSENT); if (isAbsent) { entry.setAbsent(isAbsent); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } boolean isIncomplete = parseBoolean(line, ATTRIBUTE_INCOMPLETE); if (isIncomplete) { entry.setIncomplete(isIncomplete); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String uuid = parseString(line); if (uuid != null) { uuid = (String) getObjectsPool().getObject(uuid); entry.setUUID(uuid); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String lockToken = parseString(line); if (lockToken != null) { entry.setLockToken(lockToken); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String lockOwner = parseString(line); if (lockOwner != null) { lockOwner = (String) getObjectsPool().getObject(lockOwner); entry.setLockOwner(lockOwner); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String lockComment = parseString(line); if (lockComment != null) { entry.setLockComment(lockComment); } line = reader.readLine(); if (isEntryFinished(line)) { return entry; } String lockCreationDate = parseValue(line); if (lockCreationDate != null) { entry.setLockCreationDate(lockCreationDate); } if (readExtraOptions(reader, entry)) { return entry; } do { line = reader.readLine(); if (line == null) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_CORRUPT, "Missing entry terminator"); SVNErrorManager.error(err, SVNLogType.WC); } else if (line.length() == 1 && line.charAt(0) == '\f') { break; } } while (line != null); return entry; } protected boolean isEntryFinished(String line) { return line != null && line.length() > 0 && line.charAt(0) == '\f'; } protected 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, SVNLogType.WC); } return true; } return false; } protected String parseString(String line) throws SVNException { if (line == null) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_CORRUPT, "Unexpected end of entry"); SVNErrorManager.error(err, SVNLogType.WC); } 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, SVNLogType.WC); } 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; } protected String parseValue(String line) throws SVNException { if (line == null) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_CORRUPT, "Unexpected end of entry"); SVNErrorManager.error(err, SVNLogType.WC); } else if ("".equals(line)) { return null; } return line; } public String getThisDirName() { return THIS_DIR; } protected boolean readExtraOptions(BufferedReader reader, SVNEntry entry) throws SVNException, IOException { return false; } protected void writeEntries(Writer writer) throws IOException, SVNException { SVNEntry rootEntry = (SVNEntry)myEntries.get(getThisDirName()); writer.write(getFormatVersion() + "\n"); writeEntry(writer, getThisDirName(), rootEntry, 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; } SVNNodeKind kind = entry.getKind(); if (kind == SVNNodeKind.FILE) { if (entry.getRevision() < 0) { entry.setRevision(rootEntry.getRevision()); } if (entry.getURL() == null) { entry.setParentURL(rootEntry.getURL()); } if (entry.getRepositoryRoot() == null) { entry.setRepositoryRoot(rootEntry.getRepositoryRoot()); } if (entry.getUUID() == null) { if (!(entry.isScheduledForAddition() || entry.isScheduledForReplacement())) { entry.setUUID(rootEntry.getUUID()); } } if (entry.getCachableProperties() == null) { entry.setCachableProperties(rootEntry.getCachableProperties()); } } writeEntry(writer, name, entry, rootEntry); } } private void writeEntry(Writer writer, String name, SVNEntry entry, SVNEntry rootEntry) throws IOException, SVNException { boolean isThisDir = getThisDirName().equals(name); boolean isSubDir = !isThisDir && entry.isDirectory(); int emptyFields = 0; if (!writeString(writer, name, emptyFields)) { ++emptyFields; } String kind = (String) entry.getKind().toString(); if (writeValue(writer, kind, emptyFields)){ emptyFields = 0; } else { ++emptyFields; } long revision = -1; if (isThisDir) { revision = entry.getRevision(); } else if (!isSubDir) { revision = entry.getRevision(); if (revision == rootEntry.getRevision()) { revision = -1; } } if (writeRevision(writer, revision, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } String url = null; if (isThisDir) { url = entry.getURL(); } else if (!isSubDir) { url = entry.getURL(); String expectedURL = SVNPathUtil.append(rootEntry.getURL(), SVNEncodingUtil.uriEncode(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.getRepositoryRoot(); } else if (!isSubDir) { String thisDirRoot = rootEntry.getRepositoryRoot(); root = (String)entry.getRepositoryRoot(); if (root != null && root.equals(thisDirRoot)) { root = null; } } if (writeString(writer, root, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } String schedule = entry.getSchedule(); 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.getTextTime(); if (writeTime(writer, textTime, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } String checksum = (String)entry.getChecksum(); if (writeValue(writer, checksum, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } String committedDate = (String)entry.getCommittedDate(); if (writeTime(writer, committedDate, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } long committedRevision = entry.getCommittedRevision(); if (writeRevision(writer, committedRevision, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } String committedAuthor = (String)entry.getAuthor(); if (writeString(writer, committedAuthor, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } boolean hasProps = entry.hasProperties(); if (hasProps) { writeValue(writer, ATTRIBUTE_HAS_PROPS, emptyFields); emptyFields = 0; } else { ++emptyFields; } boolean hasPropsMods = entry.hasPropertiesModifications(); if (hasPropsMods) { writeValue(writer, ATTRIBUTE_HAS_PROP_MODS, emptyFields); emptyFields = 0; } else { ++emptyFields; } String cachableProps = asString(entry.getCachableProperties(), " "); if (!isThisDir) { String thisDirCachableProps = asString(rootEntry.getCachableProperties(), " "); if (thisDirCachableProps != null && cachableProps != null && thisDirCachableProps.equals(cachableProps)) { cachableProps = null; } } if (writeValue(writer, cachableProps, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } String presentProps = asString(entry.getPresentProperties(), " "); if (writeValue(writer, presentProps, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } String propRejectFile = entry.getPropRejectFile(); if (writeString(writer, propRejectFile, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } String conflictOldFile = entry.getConflictOld(); if (writeString(writer, conflictOldFile, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } String conflictNewFile = entry.getConflictNew(); if (writeString(writer, conflictNewFile, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } String conflictWrkFile = entry.getConflictWorking(); if (writeString(writer, conflictWrkFile, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } boolean copiedAttr = entry.isCopied(); if (copiedAttr) { writeValue(writer, ATTRIBUTE_COPIED, emptyFields); emptyFields = 0; } else { ++emptyFields; } String copyfromURL = entry.getCopyFromURL(); if (writeString(writer, copyfromURL, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } long copyfromRevision = entry.getCopyFromRevision(); if (writeRevision(writer, copyfromRevision, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } boolean deletedAttr = entry.isDeleted(); if (deletedAttr) { writeValue(writer, ATTRIBUTE_DELETED, emptyFields); emptyFields = 0; } else { ++emptyFields; } boolean absentAttr = entry.isAbsent(); if (absentAttr) { writeValue(writer, ATTRIBUTE_ABSENT, emptyFields); emptyFields = 0; } else { ++emptyFields; } boolean incompleteAttr = entry.isIncomplete(); if (incompleteAttr) { writeValue(writer, ATTRIBUTE_INCOMPLETE, emptyFields); emptyFields = 0; } else { ++emptyFields; } String uuid = entry.getUUID(); if (!isThisDir) { String thisDirUUID = rootEntry.getUUID(); if (thisDirUUID != null && uuid != null && thisDirUUID.equals(uuid)) { uuid = null; } } if (writeValue(writer, uuid, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } String lockToken = entry.getLockToken(); if (writeString(writer, lockToken, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } String lockOwner = entry.getLockOwner(); if (writeString(writer, lockOwner, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } String lockComment = entry.getLockComment(); if (writeString(writer, lockComment, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } String lockCreationDate = entry.getLockCreationDate(); if (writeTime(writer, lockCreationDate, emptyFields)) { emptyFields = 0; } else { ++emptyFields; } writeExtraOptions(writer, name, entry, emptyFields); writer.write("\f\n"); writer.flush(); } protected int writeExtraOptions(Writer writer, String entryName, SVNEntry entry, int emptyFields) throws SVNException, IOException { return emptyFields; } protected 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; } protected 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; } protected boolean writeTime(Writer writer, String val, int emptyFields) throws IOException { if (val != null && val.length() > 0) { long time = SVNDate.parseDateAsMilliseconds(val); if (time > 0) { for (int i = 0; i < emptyFields; i++) { writer.write('\n'); } writer.write(val); writer.write('\n'); return true; } } return false; } protected boolean writeRevision(Writer writer, long revValue, int emptyFields) throws IOException { if (revValue >= 0) { for (int i = 0; i < emptyFields; i++) { writer.write('\n'); } writer.write(Long.toString(revValue)); writer.write('\n'); return true; } return false; } public boolean hasPropModifications(String name) throws SVNException { SVNEntry entry = getEntry(name, true); if (entry != null) { return entry.hasPropertiesModifications(); } return false; } public boolean hasProperties(String name) throws SVNException { SVNEntry entry = getEntry(name, true); if (entry != null) { return entry.hasProperties(); } 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 { // Stian was here. created = SVNFileUtil.createNewFile(myLockFile); } 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, SVNLogType.WC); } 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, SVNLogType.WC); } else { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_NOT_LOCKED, "Cannot lock working copy ''{0}''", getRoot()); SVNErrorManager.error(err, SVNLogType.WC); } return false; } public SVNAdminArea createVersionedDirectory(File dir, String url, String rootURL, String uuid, long revNumber, boolean createMyself, SVNDepth depth) 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 : createAdminAreaForDir(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); rootEntry.setDepth(depth); 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, SVNLogType.WC); } // unlock dir. SVNFileUtil.deleteFile(lockFile); return adminArea; } protected SVNVersionedProperties formatBaseProperties(SVNProperties srcProperties) { SVNProperties props = new SVNProperties(srcProperties); return new SVNProperties13(props); } protected SVNVersionedProperties formatProperties(SVNEntry entry, SVNProperties srcProperties) { SVNProperties props = new SVNProperties(srcProperties); return new SVNProperties14(props, this, entry.getName()) { protected SVNProperties loadProperties() throws SVNException { return getProperties(); } }; } private void makeKillMe(boolean killAdminOnly) throws SVNException { File killMe = getAdminFile(ADM_KILLME); if (killMe.getParentFile().isDirectory()) { SVNFileUtil.createFile(killMe, killAdminOnly ? KILL_ADM_ONLY : null, null); } } public void postCommit(String fileName, long revisionNumber, boolean implicit, boolean rerun, 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, SVNLogType.WC); } if (!implicit && entry.isScheduledForDeletion()) { if (getThisDirName().equals(fileName)) { entry.setRevision(revisionNumber); entry.setKind(SVNNodeKind.DIR); if (rerun) { File killMe = getAdminFile(ADM_KILLME); if (killMe.isFile()) { return; } } makeKillMe(entry.isKeepLocal()); } 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 fileLength = 0; if (!getThisDirName().equals(fileName)) { File workingFile = getFile(fileName); fileLength = SVNFileUtil.getFileLength(workingFile); } 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 = SVNFileUtil.getFileLastModified(tmpFile); long wkTimestamp = SVNFileUtil.getFileLastModified(workingFile); if (tmpTimestamp != wkTimestamp) { // check if wc file is not modified File tmpFile2 = SVNFileUtil.createUniqueFile(tmpFile.getParentFile(), fileName, ".tmp", true); 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, SVNLogType.WC); } 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", false); boolean overwritten = false; SVNFileType fileType = SVNFileType.getType(tmpFile); boolean special = getProperties(fileName).getPropertyValue(SVNProperty.SPECIAL) != null; if (!SVNFileUtil.symlinksSupported() || !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, 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 = SVNFileUtil.getFileLastModified(wcFile); fileLength = SVNFileUtil.getFileLength(wcFile); } } catch (SVNException svne) { SVNErrorMessage err = SVNErrorMessage.create(errorCode, "Error replacing text-base of ''{0}''", fileName); SVNErrorManager.error(err, svne, SVNLogType.WC); } finally { tmpFile2.delete(); tmpFile.delete(); } } // update entry Map entryAttrs = new SVNHashMap(); entryAttrs.put(SVNProperty.REVISION, SVNProperty.toString(revisionNumber)); entryAttrs.put(SVNProperty.KIND, getThisDirName().equals(fileName) ? SVNProperty.KIND_DIR : SVNProperty.KIND_FILE); if (!implicit) { entryAttrs.put(SVNProperty.SCHEDULE, null); } entryAttrs.put(SVNProperty.COPIED, SVNProperty.toString(false)); entryAttrs.put(SVNProperty.DELETED, SVNProperty.toString(false)); if (textTime != 0 && !implicit) { entryAttrs.put(SVNProperty.TEXT_TIME, SVNDate.formatDate(new Date(textTime))); } entryAttrs.put(SVNProperty.CONFLICT_NEW, null); entryAttrs.put(SVNProperty.CONFLICT_OLD, null); entryAttrs.put(SVNProperty.CONFLICT_WRK, null); entryAttrs.put(SVNProperty.PROP_REJECT_FILE, null); entryAttrs.put(SVNProperty.COPYFROM_REVISION, null); entryAttrs.put(SVNProperty.COPYFROM_URL, null); entryAttrs.put(SVNProperty.HAS_PROP_MODS, SVNProperty.toString(false)); entryAttrs.put(SVNProperty.WORKING_SIZE, Long.toString(fileLength)); try { modifyEntry(fileName, entryAttrs, false, true); } catch (SVNException svne) { SVNErrorMessage err = SVNErrorMessage.create(errorCode, "Error modifying entry of ''{0}''", fileName); SVNErrorManager.error(err, svne, SVNLogType.WC); } 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; } else { throw svne; } } SVNEntry entryInParent = parentArea.getEntry(dirFile.getName(), false); if (entryInParent != null) { entryAttrs.clear(); if (!implicit) { entryAttrs.put(SVNProperty.SCHEDULE, null); } entryAttrs.put(SVNProperty.COPIED, SVNProperty.toString(false)); entryAttrs.put(SVNProperty.COPYFROM_REVISION, null); entryAttrs.put(SVNProperty.COPYFROM_URL, null); entryAttrs.put(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, SVNLogType.WC); } } 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(ADM_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.")) { if (log.isFile() && log.exists()) { SVNDebugLog.getDefaultLog().logFiner(SVNLogType.WC, "unlock: log file: '" + log.getName() + "', listed, and exists."); return false; } SVNDebugLog.getDefaultLog().logFiner(SVNLogType.WC, "unlock: log file: '" + log.getName() + "', listed, but does not exist."); } } boolean deleted = SVNFileUtil.deleteFile(myLockFile); if (!deleted) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_LOCKED, "Failed to unlock working copy ''{0}''", getRoot()); SVNErrorManager.error(err, SVNLogType.WC); } 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, SVNLogType.WC); return false; } public boolean hasTreeConflict(String name) throws SVNException { return false; } public SVNTreeConflictDescription getTreeConflict(String name) throws SVNException { return null; } public void addTreeConflict(SVNTreeConflictDescription conflict) throws SVNException { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE, "This feature is not supported in version {0} of working copy format", String.valueOf(getFormatVersion())); SVNErrorManager.error(err, SVNLogType.WC); } public SVNTreeConflictDescription deleteTreeConflict(String name) throws SVNException { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE, "This feature is not supported in version {0} of working copy format", String.valueOf(getFormatVersion())); SVNErrorManager.error(err, SVNLogType.WC); return null; } public void setFileExternalLocation(String name, SVNURL url, SVNRevision pegRevision, SVNRevision revision, SVNURL reposRootURL) throws SVNException { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE, "This feature is not supported in version {0} of working copy format", String.valueOf(getFormatVersion())); SVNErrorManager.error(err, SVNLogType.WC); } public int getFormatVersion() { return WC_FORMAT; } protected SVNAdminArea createAdminAreaForDir(File dir) { return new SVNAdminArea14(dir); } protected boolean isEntryPropertyApplicable(String propName) { return propName != null && !INAPPLICABLE_PROPERTIES.contains(propName); } }