#include "cfstorage.h" #include "cfexception.h" #include "compoundfile.h" #include "directoryentry.h" #include "RBTree/rbtreeexception.h" #include "RBTree/irbnode.h" using namespace CFCPP; using RedBlackTree::RBTree; CFStorage::CFStorage(CompoundFile *compFile, const std::weak_ptr &dirEntry) : CFItem(compFile) { setDirEntry(dirEntry); } std::shared_ptr CFStorage::getChildren() { if (children == nullptr) { children = LoadChildren(this->dirEntry.lock()->getSid()); if (children == nullptr) { children = CompoundFile::CreateNewTree(); } } return children; } std::shared_ptr CFStorage::AddStream(const std::wstring& streamName) { CheckDisposed(); if (streamName.empty()) throw CFException("Stream name cannot be null or empty"); std::shared_ptr dirEntry = DirectoryEntry::TryNew(streamName, StgType::StgStream, compoundFile->GetDirectories()); try { getChildren()->Insert(dirEntry); this->dirEntry.lock()->setChild(std::dynamic_pointer_cast(getChildren()->getRoot())->getSid()); } catch (RedBlackTree::RBTreeException &rbex) { compoundFile->ResetDirectoryEntry(dirEntry->getSid()); throw CFDuplicatedItemException(L"An entry with name '" + streamName + L"' is already present in storage '" + Name() + L"' "); } return std::shared_ptr(new CFStream(compoundFile, dirEntry)); } std::shared_ptr CFStorage::GetStream(const std::wstring& streamName) { CheckDisposed(); std::shared_ptr temp = DirectoryEntry::Mock(streamName, StgType::StgStream); RedBlackTree::PIRBNode outDirEntry; if (getChildren()->TryLookup(temp, outDirEntry) && ((std::static_pointer_cast(outDirEntry))->getStgType() == StgType::StgStream)) { return std::shared_ptr(new CFStream(compoundFile, std::static_pointer_cast(outDirEntry))); } else { throw CFItemNotFound(L"Cannot find item [" + streamName + L"] within the current storage"); } } bool CFStorage::TryGetStream(const std::wstring& streamName, std::shared_ptr& cfStream) { bool result = false; cfStream.reset(); try { CheckDisposed(); std::shared_ptr tmp = DirectoryEntry::Mock(streamName, StgType::StgStream); RedBlackTree::PIRBNode outDirEntry; if (getChildren()->TryLookup(tmp, outDirEntry) && ((std::static_pointer_cast(outDirEntry))->getStgType() == StgType::StgStream)) { cfStream = std::shared_ptr(new CFStream(compoundFile, std::static_pointer_cast(outDirEntry))); result = true; } } catch (CFDisposedException& ex) { result = false; } return result; } std::shared_ptr CFStorage::GetStorage(const std::wstring &storageName) { CheckDisposed(); std::shared_ptr pattern = DirectoryEntry::Mock(storageName, StgType::StgInvalid); RedBlackTree::PIRBNode outDe; if (getChildren()->TryLookup(pattern, outDe) && std::static_pointer_cast(outDe)->getStgType() == StgType::StgStorage) { return std::shared_ptr(new CFStorage(compoundFile, std::dynamic_pointer_cast(outDe))); } else { throw CFItemNotFound(L"Cannot find item [" + storageName + L"] within the current storage"); } } std::shared_ptr CFStorage::TryGetStorage(const std::wstring &storageName) { CheckDisposed(); std::shared_ptr pattern = DirectoryEntry::Mock(storageName, StgType::StgInvalid); RedBlackTree::PIRBNode outDe; if (getChildren()->TryLookup(pattern, outDe) && std::static_pointer_cast(outDe)->getStgType() == StgType::StgStorage) { return std::shared_ptr(new CFStorage(compoundFile, std::dynamic_pointer_cast(outDe))); } else { return {}; } } bool CFStorage::TryGetStorage(const std::wstring &storageName, std::shared_ptr& cfStorage) { bool result = false; cfStorage.reset(); try { CheckDisposed(); std::shared_ptr pattern = DirectoryEntry::Mock(storageName, StgType::StgInvalid); RedBlackTree::PIRBNode outDirEntry; if (getChildren()->TryLookup(pattern, outDirEntry) && std::static_pointer_cast(outDirEntry)->getStgType() == StgType::StgStorage) { cfStorage.reset(new CFStorage(compoundFile, std::dynamic_pointer_cast(outDirEntry))); result = true; } } catch (CFDisposedException& ex) { result = false; } return result; } std::shared_ptr CFStorage::AddStorage(const std::wstring &storageName) { CheckDisposed(); if (storageName.empty()) throw CFException("Stream name cannot be empty"); std::shared_ptr cfo = DirectoryEntry::New(storageName, StgType::StgStorage, compoundFile->GetDirectories()); try { getChildren()->Insert(cfo); } catch (RedBlackTree::RBTreeDuplicatedItemException& ex) { compoundFile->ResetDirectoryEntry(cfo->getSid()); cfo.reset(); throw CFDuplicatedItemException(L"An entry with name '" + storageName + L"' is already had in storage '" + Name() + L"' "); } std::shared_ptr childrenRoot = std::dynamic_pointer_cast(getChildren()->getRoot()); dirEntry.lock()->setChild(childrenRoot->getSid()); return std::shared_ptr(new CFStorage(compoundFile, cfo)); } void CFStorage::VisitEntries(RedBlackTree::Action > action, bool recursive) { CheckDisposed(); if (action != nullptr) { SVector subStorages; RedBlackTree::Action internalAction = [&] (RedBlackTree::PIRBNode targetNode) { auto d = std::dynamic_pointer_cast(targetNode); if (d->getStgType() == StgType::StgStream) { std::shared_ptr pstream (new CFStream(compoundFile, d)); action(std::static_pointer_cast(pstream)); } else { std::shared_ptr pstorage(new CFStorage(compoundFile, d)); action(std::static_pointer_cast(pstorage)); } if (d->getChild() != DirectoryEntry::NOSTREAM) subStorages.push_back(targetNode); }; getChildren()->VisitTreeNodes(internalAction); if (recursive && subStorages.size() > 0) { for (const auto& node : *subStorages) { auto dirEntry = std::dynamic_pointer_cast(node); CFStorage(compoundFile, dirEntry).VisitEntries(action, recursive); } } } } void CFStorage::Delete(const std::wstring &entryName) { CheckDisposed(); auto temp = DirectoryEntry::Mock(entryName, StgType::StgInvalid); RedBlackTree::PIRBNode foundObj; getChildren()->TryLookup(temp, foundObj); if (foundObj == nullptr) throw CFItemNotFound(L"Entry named [" + entryName + L"] was not found"); if (std::dynamic_pointer_cast(foundObj)->getStgType() == StgType::StgRoot) throw CFException("Root storage cannot be removed"); RedBlackTree::PIRBNode altDel; switch (std::dynamic_pointer_cast(foundObj)->getStgType()) { case StgType::StgStorage: { std::shared_ptr temp(new CFStorage(compoundFile, std::dynamic_pointer_cast(foundObj))); auto storageChild = temp->getChildren(); for (auto iter = storageChild->begin(); iter != storageChild->end(); ++iter) { auto de = *iter; auto ded = std::dynamic_pointer_cast(de); temp->Delete(ded->GetEntryName()); } if (getChildren()->getRoot() != nullptr) { dirEntry.lock()->setChild(std::dynamic_pointer_cast(getChildren()->getRoot())->getSid()); } else { dirEntry.lock()->setChild(DirectoryEntry::NOSTREAM); } getChildren()->Delete(foundObj, altDel); if (altDel != nullptr) { foundObj = altDel; } compoundFile->InvalidateDirectoryEntry(std::dynamic_pointer_cast(foundObj)->getSid()); break; } case StgType::StgStream: { compoundFile->FreeAssociatedData(std::dynamic_pointer_cast(foundObj)->getSid()); getChildren()->Delete(foundObj, altDel); if (getChildren()->getRoot() != nullptr) { dirEntry.lock()->setChild(std::dynamic_pointer_cast(getChildren()->getRoot())->getSid()); } else { dirEntry.lock()->setChild(DirectoryEntry::NOSTREAM); } if (altDel != nullptr) { foundObj = altDel; } compoundFile->InvalidateDirectoryEntry(std::dynamic_pointer_cast(foundObj)->getSid()); } default: break; } } void CFStorage::RenameItem(const std::wstring &oldItemName, const std::wstring &newItemName) { auto pattern = DirectoryEntry::Mock(oldItemName, StgType::StgInvalid); RedBlackTree::PIRBNode item; if (getChildren()->TryLookup(pattern, item)) { std::dynamic_pointer_cast(item)->SetEntryName(newItemName); } else { throw CFItemNotFound(L"Item " + oldItemName + L" not found in Storage"); } children.reset(); children = LoadChildren(dirEntry.lock()->getSid()); if (children == nullptr) { children = compoundFile->CreateNewTree(); } } std::shared_ptr CFStorage::LoadChildren(int SID) { std::shared_ptr childrenTree = compoundFile->GetChildrenTree(SID); if (childrenTree->getRoot() != nullptr) { dirEntry.lock()->setChild((std::static_pointer_cast(childrenTree->getRoot())->getSid())); } else { dirEntry.lock()->setChild(DirectoryEntry::NOSTREAM); } return childrenTree; }