001: /*
002: * ====================================================================
003: * Copyright (c) 2004-2008 TMate Software Ltd. All rights reserved.
004: *
005: * This software is licensed as described in the file COPYING, which
006: * you should have received as part of this distribution. The terms
007: * are also available at http://svnkit.com/license.html
008: * If newer versions of this license are posted there, you may use a
009: * newer version instead, at your option.
010: * ====================================================================
011: */
012: package org.tmatesoft.svn.core.internal.io.fs;
013:
014: import java.io.InputStream;
015: import java.util.HashMap;
016: import java.util.Iterator;
017: import java.util.LinkedList;
018: import java.util.Map;
019: import java.util.TreeMap;
020:
021: import org.tmatesoft.svn.core.SVNErrorCode;
022: import org.tmatesoft.svn.core.SVNErrorMessage;
023: import org.tmatesoft.svn.core.SVNException;
024: import org.tmatesoft.svn.core.SVNNodeKind;
025: import org.tmatesoft.svn.core.internal.delta.SVNDeltaCombiner;
026: import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
027: import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
028:
029: /**
030: * @version 1.1.1
031: * @author TMate Software Ltd.
032: */
033: public abstract class FSRoot {
034:
035: private RevisionCache myRevNodesCache;
036: private FSFS myFSFS;
037: protected FSRevisionNode myRootRevisionNode;
038:
039: protected FSRoot(FSFS owner) {
040: myFSFS = owner;
041: }
042:
043: public FSFS getOwner() {
044: return myFSFS;
045: }
046:
047: public FSRevisionNode getRevisionNode(String path)
048: throws SVNException {
049: String canonPath = path;
050: FSRevisionNode node = fetchRevNodeFromCache(canonPath);
051: if (node == null) {
052: FSParentPath parentPath = openPath(path, true, false);
053: node = parentPath.getRevNode();
054: }
055: return node;
056: }
057:
058: public abstract FSRevisionNode getRootRevisionNode()
059: throws SVNException;
060:
061: public abstract Map getChangedPaths() throws SVNException;
062:
063: public abstract FSCopyInheritance getCopyInheritance(
064: FSParentPath child) throws SVNException;
065:
066: public FSParentPath openPath(String path,
067: boolean lastEntryMustExist, boolean storeParents)
068: throws SVNException {
069: String canonPath = path;
070: FSRevisionNode here = getRootRevisionNode();
071: String pathSoFar = "/";
072:
073: FSParentPath parentPath = new FSParentPath(here, null, null);
074: parentPath.setCopyStyle(FSCopyInheritance.COPY_ID_INHERIT_SELF);
075:
076: // skip the leading '/'
077: String rest = canonPath.substring(1);
078:
079: while (true) {
080: String entry = SVNPathUtil.head(rest);
081: String next = SVNPathUtil.removeHead(rest);
082: pathSoFar = SVNPathUtil.concatToAbs(pathSoFar, entry);
083: FSRevisionNode child = null;
084: if (entry == null || "".equals(entry)) {
085: child = here;
086: } else {
087: FSRevisionNode cachedRevNode = fetchRevNodeFromCache(pathSoFar);
088: if (cachedRevNode != null) {
089: child = cachedRevNode;
090: } else {
091: try {
092: child = here.getChildDirNode(entry, getOwner());
093: } catch (SVNException svne) {
094: if (svne.getErrorMessage().getErrorCode() == SVNErrorCode.FS_NOT_FOUND) {
095: if (!lastEntryMustExist
096: && (next == null || "".equals(next))) {
097: return new FSParentPath(null, entry,
098: parentPath);
099: }
100: SVNErrorManager.error(FSErrors
101: .errorNotFound(this , path), svne);
102: }
103: throw svne;
104: }
105: }
106:
107: parentPath.setParentPath(child, entry,
108: storeParents ? new FSParentPath(parentPath)
109: : null);
110:
111: if (storeParents) {
112: FSCopyInheritance copyInheritance = getCopyInheritance(parentPath);
113: if (copyInheritance != null) {
114: parentPath.setCopyStyle(copyInheritance
115: .getStyle());
116: parentPath.setCopySourcePath(copyInheritance
117: .getCopySourcePath());
118: }
119: }
120:
121: if (cachedRevNode == null) {
122: putRevNodeToCache(pathSoFar, child);
123: }
124: }
125: if (next == null || "".equals(next)) {
126: break;
127: }
128:
129: if (child.getType() != SVNNodeKind.DIR) {
130: SVNErrorMessage err = FSErrors.errorNotDirectory(
131: pathSoFar, getOwner());
132: SVNErrorManager.error(err.wrap(
133: "Failure opening ''{0}''", path));
134: }
135: rest = next;
136: here = child;
137: }
138: return parentPath;
139: }
140:
141: public SVNNodeKind checkNodeKind(String path) throws SVNException {
142: FSRevisionNode revNode = null;
143: try {
144: revNode = getRevisionNode(path);
145: } catch (SVNException svne) {
146: if (svne.getErrorMessage().getErrorCode() == SVNErrorCode.FS_NOT_FOUND) {
147: return SVNNodeKind.NONE;
148: }
149: throw svne;
150: }
151: return revNode.getType();
152: }
153:
154: public void putRevNodeToCache(String path, FSRevisionNode node)
155: throws SVNException {
156: if (!path.startsWith("/")) {
157: SVNErrorMessage err = SVNErrorMessage.create(
158: SVNErrorCode.UNKNOWN, "Invalid path ''{0}''", path);
159: SVNErrorManager.error(err);
160: }
161: if (myRevNodesCache == null) {
162: myRevNodesCache = new RevisionCache(100);
163: }
164: myRevNodesCache.put(path, node);
165: }
166:
167: public void removeRevNodeFromCache(String path) throws SVNException {
168: if (!path.startsWith("/")) {
169: SVNErrorMessage err = SVNErrorMessage.create(
170: SVNErrorCode.UNKNOWN, "Invalid path ''{0}''", path);
171: SVNErrorManager.error(err);
172: }
173: if (myRevNodesCache == null) {
174: return;
175: }
176: myRevNodesCache.delete(path);
177: }
178:
179: protected FSRevisionNode fetchRevNodeFromCache(String path)
180: throws SVNException {
181: if (myRevNodesCache == null) {
182: return null;
183: }
184: if (!path.startsWith("/")) {
185: SVNErrorMessage err = SVNErrorMessage.create(
186: SVNErrorCode.UNKNOWN, "Invalid path ''{0}''", path);
187: SVNErrorManager.error(err);
188: }
189: return (FSRevisionNode) myRevNodesCache.fetch(path);
190: }
191:
192: private void foldChange(Map mapChanges, FSPathChange change)
193: throws SVNException {
194: if (change == null) {
195: return;
196: }
197: mapChanges = mapChanges != null ? mapChanges : new HashMap();
198: FSPathChange newChange = null;
199: String copyfromPath = null;
200: long copyfromRevision = FSRepository.SVN_INVALID_REVNUM;
201:
202: FSPathChange oldChange = (FSPathChange) mapChanges.get(change
203: .getPath());
204: if (oldChange != null) {
205: copyfromPath = oldChange.getCopyPath();
206: copyfromRevision = oldChange.getCopyRevision();
207:
208: if ((change.getRevNodeId() == null)
209: && (FSPathChangeKind.FS_PATH_CHANGE_RESET != change
210: .getChangeKind())) {
211: SVNErrorMessage err = SVNErrorMessage.create(
212: SVNErrorCode.FS_CORRUPT,
213: "Missing required node revision ID");
214: SVNErrorManager.error(err);
215: }
216: if ((change.getRevNodeId() != null)
217: && (!oldChange.getRevNodeId().equals(
218: change.getRevNodeId()))
219: && (oldChange.getChangeKind() != FSPathChangeKind.FS_PATH_CHANGE_DELETE)) {
220: SVNErrorMessage err = SVNErrorMessage
221: .create(SVNErrorCode.FS_CORRUPT,
222: "Invalid change ordering: new node revision ID without delete");
223: SVNErrorManager.error(err);
224: }
225: if (FSPathChangeKind.FS_PATH_CHANGE_DELETE == oldChange
226: .getChangeKind()
227: && !(FSPathChangeKind.FS_PATH_CHANGE_REPLACE == change
228: .getChangeKind()
229: || FSPathChangeKind.FS_PATH_CHANGE_RESET == change
230: .getChangeKind() || FSPathChangeKind.FS_PATH_CHANGE_ADD == change
231: .getChangeKind())) {
232: SVNErrorMessage err = SVNErrorMessage
233: .create(SVNErrorCode.FS_CORRUPT,
234: "Invalid change ordering: non-add change on deleted path");
235: SVNErrorManager.error(err);
236: }
237: if (FSPathChangeKind.FS_PATH_CHANGE_MODIFY == change
238: .getChangeKind()) {
239: if (change.isTextModified()) {
240: oldChange.setTextModified(true);
241: }
242: if (change.arePropertiesModified()) {
243: oldChange.setPropertiesModified(true);
244: }
245: } else if (FSPathChangeKind.FS_PATH_CHANGE_ADD == change
246: .getChangeKind()
247: || FSPathChangeKind.FS_PATH_CHANGE_REPLACE == change
248: .getChangeKind()) {
249: oldChange
250: .setChangeKind(FSPathChangeKind.FS_PATH_CHANGE_REPLACE);
251: oldChange.setRevNodeId(change.getRevNodeId());
252: oldChange.setTextModified(change.isTextModified());
253: oldChange.setPropertiesModified(change
254: .arePropertiesModified());
255: if (change.getCopyPath() != null) {
256: copyfromPath = change.getCopyPath();
257: copyfromRevision = change.getCopyRevision();
258: }
259: } else if (FSPathChangeKind.FS_PATH_CHANGE_DELETE == change
260: .getChangeKind()) {
261: if (FSPathChangeKind.FS_PATH_CHANGE_ADD == oldChange
262: .getChangeKind()) {
263: oldChange = null;
264: mapChanges.remove(change.getPath());
265: } else {
266: oldChange
267: .setChangeKind(FSPathChangeKind.FS_PATH_CHANGE_DELETE);
268: oldChange.setPropertiesModified(change
269: .arePropertiesModified());
270: oldChange.setTextModified(change.isTextModified());
271: }
272:
273: copyfromPath = null;
274: copyfromRevision = FSRepository.SVN_INVALID_REVNUM;
275:
276: } else if (FSPathChangeKind.FS_PATH_CHANGE_RESET == change
277: .getChangeKind()) {
278: oldChange = null;
279: copyfromPath = null;
280: copyfromRevision = FSRepository.SVN_INVALID_REVNUM;
281: mapChanges.remove(change.getPath());
282: }
283:
284: newChange = oldChange;
285: } else {
286: copyfromPath = change.getCopyPath();
287: copyfromRevision = change.getCopyRevision();
288: newChange = change;
289: }
290:
291: if (newChange != null) {
292: newChange.setCopyPath(copyfromPath);
293: newChange.setCopyRevision(copyfromRevision);
294: mapChanges.put(change.getPath(), newChange);
295: }
296: }
297:
298: protected Map fetchAllChanges(FSFile changesFile, boolean prefolded)
299: throws SVNException {
300: Map changedPaths = new HashMap();
301: FSPathChange change = readChange(changesFile);
302: while (change != null) {
303: foldChange(changedPaths, change);
304: if ((FSPathChangeKind.FS_PATH_CHANGE_DELETE == change
305: .getChangeKind() || FSPathChangeKind.FS_PATH_CHANGE_REPLACE == change
306: .getChangeKind())
307: && !prefolded) {
308: for (Iterator curIter = changedPaths.keySet()
309: .iterator(); curIter.hasNext();) {
310: String hashKeyPath = (String) curIter.next();
311: if (change.getPath().equals(hashKeyPath)) {
312: continue;
313: }
314: if (SVNPathUtil.pathIsChild(change.getPath(),
315: hashKeyPath) != null) {
316: curIter.remove();
317: }
318: }
319: }
320: change = readChange(changesFile);
321: }
322: return changedPaths;
323: }
324:
325: public Map detectChanged() throws SVNException {
326: Map changes = getChangedPaths();
327: if (changes.size() == 0) {
328: return changes;
329: }
330:
331: for (Iterator paths = changes.keySet().iterator(); paths
332: .hasNext();) {
333: String changedPath = (String) paths.next();
334: FSPathChange change = (FSPathChange) changes
335: .get(changedPath);
336: if (change.getChangeKind() == FSPathChangeKind.FS_PATH_CHANGE_RESET) {
337: paths.remove();
338: }
339: }
340: return changes;
341: }
342:
343: private FSPathChange readChange(FSFile raReader)
344: throws SVNException {
345: String changeLine = null;
346: try {
347: changeLine = raReader.readLine(4096);
348: } catch (SVNException svne) {
349: if (svne.getErrorMessage().getErrorCode() == SVNErrorCode.STREAM_UNEXPECTED_EOF) {
350: return null;
351: }
352: throw svne;
353: }
354: if (changeLine == null || changeLine.length() == 0) {
355: return null;
356: }
357: String copyfromLine = raReader.readLine(4096);
358: return FSPathChange.fromString(changeLine, copyfromLine);
359: }
360:
361: public InputStream getFileStreamForPath(SVNDeltaCombiner combiner,
362: String path) throws SVNException {
363: FSRevisionNode fileNode = getRevisionNode(path);
364: return FSInputStream.createDeltaStream(combiner, fileNode,
365: getOwner());
366: }
367:
368: private static final class RevisionCache {
369:
370: private LinkedList myKeys;
371: private Map myCache;
372: private int mySizeLimit;
373:
374: public RevisionCache(int limit) {
375: mySizeLimit = limit;
376: myKeys = new LinkedList();
377: myCache = new TreeMap();
378: }
379:
380: public void put(Object key, Object value) {
381: if (mySizeLimit <= 0) {
382: return;
383: }
384: if (myKeys.size() == mySizeLimit) {
385: Object cachedKey = myKeys.removeLast();
386: myCache.remove(cachedKey);
387: }
388: myKeys.addFirst(key);
389: myCache.put(key, value);
390: }
391:
392: public void delete(Object key) {
393: myKeys.remove(key);
394: myCache.remove(key);
395: }
396:
397: public Object fetch(Object key) {
398: int ind = myKeys.indexOf(key);
399: if (ind != -1) {
400: if (ind != 0) {
401: Object cachedKey = myKeys.remove(ind);
402: myKeys.addFirst(cachedKey);
403: }
404: return myCache.get(key);
405: }
406: return null;
407: }
408:
409: public void clear() {
410: myKeys.clear();
411: myCache.clear();
412: }
413: }
414:
415: }
|