Abstract enhanced solver.
Inheritance: ISolver
        private void RunSolverToDeleteLocalCacheBeforeContinue(
            AbstractEnhancedSolver solver,
            ContentChangeType localContent = ContentChangeType.NONE,
            ContentChangeType remoteContent = ContentChangeType.NONE) {
            this.cacheFile.Setup(f => f.Exists).Returns(false);

            Mock<MemoryStream> stream = new Mock<MemoryStream>();
            stream.SetupAllProperties();
            stream.Setup(f => f.CanWrite).Returns(true); // required for System.Security.Cryptography.CryptoStream

            this.localFileLength = 0;
            stream.Setup(f => f.Write(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>())).Callback((byte[] buffer, int offset, int count) => this.localFileLength += count);

            this.cacheFile.Setup(f => f.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)).Returns(() => {
                this.cacheFile.Setup(f => f.Exists).Returns(true);
                return stream.Object;
            });

            Mock<IDocument> remoteObject = MockOfIDocumentUtil.CreateRemoteDocumentMock(null, this.objectId, this.objectName, this.parentId, this.fileContent.Length, this.fileContent, this.changeToken);
            remoteObject.Setup(f => f.LastModificationDate).Returns((DateTime?)this.creationDate);

            solver.Solve(this.localFile.Object, remoteObject.Object, localContent, remoteContent);

            Assert.That(this.localFileLength, Is.EqualTo(this.fileContent.Length));
            this.cacheFile.Verify(f => f.Open(It.IsAny<FileMode>(), It.IsAny<FileAccess>(), It.IsAny<FileShare>()), Times.Exactly(2));   // first open in SetupToAbortThePreviousDownload, second open to download
            this.localFile.VerifySet(d => d.LastWriteTimeUtc = It.Is<DateTime>(date => date.Equals(this.creationDate)), Times.Once());
            this.storage.VerifySavedMappedObject(MappedObjectType.File, this.objectId, this.objectName, this.parentId, this.changeToken, true, this.creationDate, this.creationDate, this.fileHash, this.fileContent.Length);
            this.transmissionStorage.Verify(f => f.GetObjectByRemoteObjectId(this.objectId), Times.Exactly(2));
            this.transmissionStorage.Verify(f => f.SaveObject(It.IsAny<IFileTransmissionObject>()), Times.AtLeastOnce());
            this.transmissionStorage.Verify(f => f.RemoveObjectByRemoteObjectId(this.objectId), Times.Once());
        }
        private void RunSolverToChangeLocalCacheBeforeContinue(
            AbstractEnhancedSolver solver,
            ContentChangeType localContent = ContentChangeType.NONE,
            ContentChangeType remoteContent = ContentChangeType.NONE) {
            this.SetupToChangeLocalCache();

            Mock<IDocument> remoteObject = MockOfIDocumentUtil.CreateRemoteDocumentMock(null, this.objectId, this.objectName, this.parentId, this.fileContent.Length, this.fileContent, this.changeToken);
            remoteObject.Setup(f => f.LastModificationDate).Returns((DateTime?)this.creationDate);

            solver.Solve(this.localFile.Object, remoteObject.Object, localContent, remoteContent);

            Assert.That(this.localFileLength, Is.EqualTo(this.fileContent.Length));
            this.cacheFile.Verify(f => f.Open(It.IsAny<FileMode>(), It.IsAny<FileAccess>(), It.IsAny<FileShare>()), Times.Exactly(3)); // first open in SetupToAbortThePreviousDownload, second open to validate checksum, third open to download
            this.cacheFile.Verify(f => f.Delete(), Times.Once());
            this.localFile.VerifySet(d => d.LastWriteTimeUtc = It.Is<DateTime>(date => date.Equals(this.creationDate)), Times.Once());
            this.storage.VerifySavedMappedObject(MappedObjectType.File, this.objectId, this.objectName, this.parentId, this.changeToken, true, this.creationDate, this.creationDate, this.fileHash, this.fileContent.Length);
            this.transmissionStorage.Verify(f => f.GetObjectByRemoteObjectId(this.objectId), Times.Exactly(2));
            this.transmissionStorage.Verify(f => f.SaveObject(It.IsAny<IFileTransmissionObject>()), Times.AtLeastOnce());
            this.transmissionStorage.Verify(f => f.RemoveObjectByRemoteObjectId(this.objectId), Times.Once());
        }
        private void RunSolverToAbortDownload(
            AbstractEnhancedSolver solver,
            ContentChangeType localContent = ContentChangeType.NONE,
            ContentChangeType remoteContent = ContentChangeType.NONE) {
            var stream = new Mock<MemoryStream>();
            stream.SetupAllProperties();
            stream.Setup(f => f.CanWrite).Returns(true); // required for System.Security.Cryptography.CryptoStream

            this.localFileLength = 0;
            stream.Setup(f => f.Write(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>())).Callback((byte[] buffer, int offset, int count) => {
                if (this.localFileLength > 0) {
                    foreach (Transmission transmission in this.transmissionManager.ActiveTransmissions) {
                        transmission.Abort();
                    }
                }

                this.localFileLength += count;
            });
            stream.Setup(f => f.Length).Returns(() => { return this.localFileLength; });

            this.cacheFile.Setup(f => f.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)).Returns(() => {
                this.cacheFile.Setup(f => f.Exists).Returns(true);
                return stream.Object;
            });

            var remoteDocument = MockOfIDocumentUtil.CreateRemoteDocumentMock(null, this.objectId, this.objectName, this.parentId, this.fileContent.Length, this.fileContent, this.changeToken);
            remoteDocument.Setup(f => f.LastModificationDate).Returns((DateTime?)this.creationDate);

            Assert.Throws<AbortException>(() => solver.Solve(this.localFile.Object, remoteDocument.Object, localContent, remoteContent));

            this.cacheFile.Verify(f => f.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None), Times.Once());
            this.cacheFile.VerifySet(f => f.Uuid = It.Is<Guid?>(uuid => uuid != null && !uuid.Equals(Guid.Empty)), Times.Never());
            this.cacheFile.Verify(f => f.MoveTo(this.localPath), Times.Never());
            this.localFile.VerifySet(d => d.LastWriteTimeUtc = It.Is<DateTime>(date => date.Equals(this.creationDate)), Times.Never());
            this.transmissionStorage.Verify(f => f.GetObjectByRemoteObjectId(It.IsAny<string>()), Times.Once());
            this.transmissionStorage.Verify(f => f.SaveObject(It.IsAny<IFileTransmissionObject>()), Times.AtLeastOnce());
            this.transmissionStorage.Verify(f => f.RemoveObjectByRemoteObjectId(It.IsAny<string>()), Times.Never());
            this.storage.Verify(f => f.SaveMappedObject(It.IsAny<IMappedObject>()), Times.Never());
        }
        private void RunSolverToContinueDownload(
            AbstractEnhancedSolver solver,
            ContentChangeType localContent = ContentChangeType.NONE,
            ContentChangeType remoteContent = ContentChangeType.NONE) {
            Mock<MemoryStream> stream = new Mock<MemoryStream>();
            stream.SetupAllProperties();
            stream.Setup(f => f.CanWrite).Returns(true); // required for System.Security.Cryptography.CryptoStream
            stream.Setup(f => f.Write(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>())).Callback((byte[] buffer, int offset, int count) => this.localFileLength += count);
            stream.Setup(f => f.Length).Returns(() => { return this.localFileLength; });

            long lengthRead = 0;
            stream.Setup(f => f.Seek(It.IsAny<long>(), It.IsAny<SeekOrigin>())).Callback((long offset, SeekOrigin loc) => lengthRead = offset);
            stream.Setup(f => f.Read(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>())).Returns((byte[] buffer, int offset, int count) => {
                if (lengthRead >= this.localFileLength) {
                    return 0;
                }

                int countRead = count;
                if (countRead > (this.localFileLength - lengthRead)) {
                    countRead = (int)(this.localFileLength - lengthRead);
                }

                Array.Copy(this.fileContent, lengthRead, buffer, offset, countRead);
                lengthRead += countRead;
                stream.Object.Position = lengthRead;
                return countRead;
            });

            this.cacheFile.Setup(f => f.Open(It.IsAny<FileMode>(), It.IsAny<FileAccess>(), It.IsAny<FileShare>())).Returns(stream.Object);
            this.cacheFile.Setup(f => f.Length).Returns(() => { return this.localFileLength; });

            var remoteDocument = MockOfIDocumentUtil.CreateRemoteDocumentMock(null, this.objectId, this.objectName, this.parentId, this.fileContent.Length, this.fileContent, this.changeToken);
            remoteDocument.Setup(f => f.LastModificationDate).Returns((DateTime?)this.creationDate);

            solver.Solve(this.localFile.Object, remoteDocument.Object, localContent, remoteContent);

            Assert.That(this.localFileLength, Is.EqualTo(this.fileContent.Length));
            stream.Verify(f => f.Seek(0, SeekOrigin.Begin), Times.Once());
            this.cacheFile.Verify(f => f.Open(It.IsAny<FileMode>(), It.IsAny<FileAccess>(), It.IsAny<FileShare>()), Times.Exactly(3)); // first open in SetupToAbortThePreviousDownload, second open to validate checksum, third open to download
            this.localFile.VerifySet(d => d.LastWriteTimeUtc = It.Is<DateTime>(date => date.Equals(this.creationDate)), Times.Once());
            this.storage.VerifySavedMappedObject(MappedObjectType.File, this.objectId, this.objectName, this.parentId, this.changeToken, true, this.creationDate, this.creationDate, this.fileHash, this.fileContent.Length);
            this.transmissionStorage.Verify(f => f.GetObjectByRemoteObjectId(this.objectId), Times.Exactly(2));
            this.transmissionStorage.Verify(f => f.SaveObject(It.IsAny<IFileTransmissionObject>()), Times.AtLeastOnce());
            this.transmissionStorage.Verify(f => f.RemoveObjectByRemoteObjectId(this.objectId), Times.Once());
        }
        /// <summary>
        /// Solve the specified situation by using the storage, localFile and remoteId.
        /// Uploads the file content if content has been changed. Otherwise simply saves the
        /// last modification date.
        /// </summary>
        /// <param name="localFileSystemInfo">Local filesystem info instance.</param>
        /// <param name="remoteId">Remote identifier or object.</param>
        /// <param name="localContent">Hint if the local content has been changed.</param>
        /// <param name="remoteContent">Information if the remote content has been changed.</param>
        public override void Solve(
            IFileSystemInfo localFileSystemInfo,
            IObjectId remoteId,
            ContentChangeType localContent  = ContentChangeType.NONE,
            ContentChangeType remoteContent = ContentChangeType.NONE)
        {
            if (!localFileSystemInfo.Exists)
            {
                throw new ArgumentException("Given local path does not exists: " + localFileSystemInfo.FullName);
            }

            // Match local changes to remote changes and updated them remotely
            IMappedObject mappedObject = null;

            try {
                Guid?guid = localFileSystemInfo.Uuid;
                if (guid != null)
                {
                    mappedObject = this.Storage.GetObjectByGuid((Guid)guid);
                }
            } catch (Exception) {
            }

            if (mappedObject == null)
            {
                mappedObject = this.Storage.GetObjectByLocalPath(localFileSystemInfo);
            }

            if (mappedObject == null)
            {
                throw new ArgumentException(string.Format("Could not find db entry for {0} => invoke crawl sync", localFileSystemInfo.FullName));
            }

            if (mappedObject.LastChangeToken != (remoteId as ICmisObjectProperties).ChangeToken)
            {
                throw new ArgumentException(string.Format("remote {1} {0} has also been changed since last sync => invoke crawl sync", remoteId.Id, remoteId is IDocument ? "document" : "folder"));
            }

            IFileInfo localFile = localFileSystemInfo as IFileInfo;

            if (localFile != null && localFile.IsContentChangedTo(mappedObject, scanOnlyIfModificationDateDiffers: true))
            {
                Logger.Debug(string.Format("\"{0}\" is different from {1}", localFile.FullName, mappedObject.ToString()));
                OperationsLogger.Debug(string.Format("Local file \"{0}\" has been changed", localFile.FullName));
                var doc = remoteId as IDocument;
                try {
                    mappedObject.LastChecksum = AbstractEnhancedSolver.UploadFile(localFile, doc, this.transmissionManager);
                } catch (Exception ex) {
                    if (ex.InnerException is CmisPermissionDeniedException)
                    {
                        OperationsLogger.Warn(string.Format("Local changed file \"{0}\" has not been uploaded: PermissionDenied", localFile.FullName));
                        return;
                    }
                    else if (ex.InnerException is CmisStorageException)
                    {
                        OperationsLogger.Warn(string.Format("Local changed file \"{0}\" has not been uploaded: StorageException", localFile.FullName), ex);
                        return;
                    }

                    throw;
                }

                mappedObject.LastRemoteWriteTimeUtc = doc.LastModificationDate;
                mappedObject.LastLocalWriteTimeUtc  = localFile.LastWriteTimeUtc;
                mappedObject.LastContentSize        = localFile.Length;

                OperationsLogger.Info(string.Format("Local changed file \"{0}\" has been uploaded", localFile.FullName));
            }

            if (this.ServerCanModifyDateTimes)
            {
                try {
                    if (remoteId is IDocument)
                    {
                        var doc = remoteId as IDocument;
                        doc.UpdateLastWriteTimeUtc(localFileSystemInfo.LastWriteTimeUtc);
                        mappedObject.LastRemoteWriteTimeUtc = doc.LastModificationDate ?? localFileSystemInfo.LastWriteTimeUtc;
                    }
                    else if (remoteId is IFolder)
                    {
                        var folder = remoteId as IFolder;
                        folder.UpdateLastWriteTimeUtc(localFileSystemInfo.LastWriteTimeUtc);
                        mappedObject.LastRemoteWriteTimeUtc = folder.LastModificationDate ?? localFileSystemInfo.LastWriteTimeUtc;
                    }
                } catch (CmisPermissionDeniedException) {
                    Logger.Debug(string.Format("Locally changed modification date \"{0}\"is not uploaded to the server: PermissionDenied", localFileSystemInfo.LastWriteTimeUtc));
                }
            }

            mappedObject.LastChangeToken       = (remoteId as ICmisObjectProperties).ChangeToken;
            mappedObject.LastLocalWriteTimeUtc = localFileSystemInfo.LastWriteTimeUtc;
            this.Storage.SaveMappedObject(mappedObject);
        }