class BufferMgr {

	public boolean redo() {
		synchronized (snapshotLock) {
			synchronized (this) {

				if (lockCount != 0) {
					throw new AssertException(
						"Can't redo with locked buffers (" + lockCount + " locks found)");
				}

				int ix = redoCheckpointHeads.size() - 1;
				if (ix < 0) {
					return false;
				}

				modifiedSinceSnapshot = true;

				// Restore checkpoint from redo stack
				// Process all nodes within the checkpoint list
				BufferNode cpHead = redoCheckpointHeads.remove(ix);
				BufferNode node = cpHead.nextInCheckpoint;
				while (node.id != TAIL) {

					// Get node
					BufferNode head = (BufferNode) bufferTable.get(node.id);
					if (head == null) {

						// Node was allocated within this checkpoint - reallocate it
						if (!indexProvider.allocateIndex(node.id)) {
							throw new AssertException();
						}
						if (node.empty) {
							indexProvider.freeIndex(node.id);
						}

						node.clearSnapshotTaken();

						// Create new buffer version list and add buffer version
						head = createNewBufferList(node.id, node);
					}
					else {
						// Node was modified within this checkpoint - insert version
						BufferNode curVer = head.nextVersion;
						if (node.empty) {
							// Node was removed within checkpoint - free it if appropriate
							// Double check previous checkpoint - make sure it is not empty
							if (!curVer.empty) {
								indexProvider.freeIndex(node.id);
							}
						}
						else if (curVer.empty) {
							// Node was re-allocated within checkpoint - reallocate it
							if (!indexProvider.allocateIndex(node.id)) {
								throw new AssertException();
							}
						}
						node.clearSnapshotTaken();
						node.addToVersion(head);
					}

					node = node.nextInCheckpoint;
				}

				// Move checkpoint to undo list
				checkpointHeads.add(cpHead);

				// Set current checkpoint
				currentCheckpoint = cpHead.checkpoint;
				currentCheckpointHead = null;

				return true;
			}
		}
	}

	public  BufferMgr();
	public  BufferMgr(int requestedBufferSize, long approxCacheSize, int maxUndos);
	public  BufferMgr(BufferFile sourceFile);
	public  BufferMgr(BufferFile sourceFile, long approxCacheSize, int maxUndos);
	private  BufferMgr(BufferFile sourceFile, int requestedbufferSize, long approxCacheSize,
			int maxUndos);

}