class BufferMgr {

	public void save(String comment, DBChangeSet changeSet, TaskMonitor monitor)
			throws IOException, CancelledException {

		synchronized (snapshotLock) {
			synchronized (this) {
				if (!(sourceFile instanceof ManagedBufferFile)) {
					throw new IOException("Save not allowed");
				}

				if (corruptedState) {
					throw new IOException("Corrupted BufferMgr state");
				}

				if (lockCount != 0) {
					throw new IOException("Attempted save while buffers are locked");
				}

				if (monitor == null) {
					monitor = TaskMonitorAdapter.DUMMY_MONITOR;
				}

				boolean oldCancelState = monitor.isCancelEnabled();

				ManagedBufferFile outFile = null;
				monitor.setMessage("Waiting for pre-save to complete...");
				if (sourceFile instanceof LocalManagedBufferFile) {
					// Use monitor for local saves
					outFile = ((LocalManagedBufferFile) sourceFile).getSaveFile(monitor);
				}
				else {
					// Monitor not supported for remote saves
					monitor.setCancelEnabled(false);
					outFile = ((ManagedBufferFile) sourceFile).getSaveFile();
					monitor.setCancelEnabled(oldCancelState & !monitor.isCancelled());
				}
				if (outFile == null) {
					throw new IOException("Save not allowed");
				}

				boolean success = false;
				try {

					if (comment != null) {
						outFile.setVersionComment(comment);
					}

					doSave(outFile, monitor);
					monitor.setCancelEnabled(false);

					if (changeSet != null) {
						BufferFile changeFile =
							((ManagedBufferFile) sourceFile).getSaveChangeDataFile();
						if (changeFile != null) {
							monitor.setMessage("Saving change data...");
							DBHandle cfh = new DBHandle(outFile.getBufferSize());
							changeSet.write(cfh, false);
							cfh.saveAs(changeFile, true, null);
							cfh.close();
						}
					}
					monitor.setMessage("Completing file save...");
					success = true;
				}
				finally {
					((ManagedBufferFile) sourceFile).saveCompleted(success);
					monitor.setCancelEnabled(oldCancelState & !monitor.isCancelled());
				}

				setSourceFile(outFile);
			}
		}
	}

	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);

}