File indexing completed on 2024-04-28 04:40:01
0001 #include "deepfiletree.h" 0002 #include <QUrl> 0003 #include <QFileInfo> 0004 #include <QDir> 0005 #include <QVector> 0006 #include <QVariant> 0007 #include <QXmlQuery> 0008 #include <QDomElement> 0009 #include "pole.h" 0010 #include <QDebug> 0011 0012 using namespace std; 0013 0014 /** This XML model spans the file system, OLE streams in OLE files and data structures in some 0015 * OLE streams. 0016 **/ 0017 0018 namespace { 0019 class ElementNode { 0020 public: 0021 // if parent, firstSibling, next or previous are -1, they corresponding node does not exist 0022 // if the value is -2, the corresponding node has not been searched yet 0023 int parentId; 0024 int firstSiblingId; 0025 int nextId; 0026 int previousId; 0027 0028 ElementNode() :parentId(-2), firstSiblingId(-2), nextId(-2), previousId(-2) {} 0029 }; 0030 0031 class OleStreamElementNode : public ElementNode { 0032 public: 0033 string streamname; 0034 int size; 0035 bool isdir; 0036 }; 0037 0038 class XmlElementNode : public ElementNode { 0039 public: 0040 QDomElement element; 0041 }; 0042 0043 enum Type { 0044 File, 0045 OLEStream, 0046 XmlElement 0047 }; 0048 0049 const int typeSize = 3; 0050 const int indexSize = sizeof(int)*8 - typeSize; 0051 0052 Type getType(int id) { 0053 return (Type)(id >> indexSize); 0054 } 0055 // last bits are used for the type 0056 int getIndex(int id) { 0057 return (id << typeSize) >> typeSize; 0058 } 0059 int getId(Type t, int index) { 0060 return (t << indexSize) | index; 0061 } 0062 0063 enum Kind { 0064 Element, 0065 AttributeName, 0066 AttributeSize 0067 }; 0068 0069 } 0070 0071 class DeepFileTree::Private { 0072 public: 0073 QXmlNamePool namepool; 0074 QList<ElementNode> fileinfonodes; 0075 QList<QFileInfo> fileinfos; 0076 QList<OleStreamElementNode> olestreams; 0077 QList<XmlElementNode> xmlelements; 0078 QDomDocument doc; 0079 const QXmlName directory; 0080 const QXmlName file; 0081 const QXmlName olestream; 0082 const QXmlName olestreamdir; 0083 const QXmlName name; 0084 const QXmlName size; 0085 0086 Private(const QXmlNamePool& pool) :namepool(pool), 0087 directory(QXmlName(namepool, QLatin1String("directory"))), 0088 file(QXmlName(namepool, QLatin1String("file"))), 0089 olestream(QXmlName(namepool, QLatin1String("olestream"))), 0090 olestreamdir(QXmlName(namepool, QLatin1String("olestreamdir"))), 0091 name(QXmlName(namepool, QLatin1String("name"))), 0092 size(QXmlName(namepool, QLatin1String("size"))) { 0093 } 0094 0095 ElementNode& getElementNode(int id) { 0096 int index = getIndex(id); 0097 switch (getType(id)) { 0098 case File: return fileinfonodes[index]; 0099 case OLEStream: return olestreams[index]; 0100 case XmlElement: 0101 default: 0102 return xmlelements[index]; 0103 } 0104 } 0105 QFileInfo* getFileInfo(int id) { 0106 return (getType(id) == File) ?&fileinfos[getIndex(id)] :0; 0107 } 0108 0109 int getFileIndex(const QFileInfo& fileinfo) { 0110 // find the parent 0111 QDir dir = fileinfo.dir(); 0112 QFileInfo parentInfo(dir.path()); 0113 if (fileinfo == parentInfo) { // parent does not exist 0114 ElementNode node; 0115 node.parentId = node.nextId = node.previousId = -1; 0116 fileinfos.append(fileinfo); 0117 fileinfonodes.append(node); 0118 return fileinfonodes.count() - 1; 0119 } 0120 0121 int parentIndex = getFileIndex(parentInfo); 0122 if (parentIndex == -1) return -1; 0123 ElementNode& parent = fileinfonodes[parentIndex]; 0124 if (parent.firstSiblingId != -2) return -1; 0125 0126 // the parent has not initialized its siblings yet 0127 const int indexOf = fileinfos.indexOf(parentInfo); 0128 if (indexOf != -1) return indexOf; 0129 0130 int offset = fileinfos.count(); 0131 loadFileSiblings(dir, parentIndex, parent); 0132 return fileinfos.indexOf(fileinfo, offset); 0133 } 0134 0135 void loadFileSiblings(const QDir& dir, int parentIndex, ElementNode& parent) { 0136 QFileInfoList list = dir.entryInfoList(QStringList(), 0137 QDir::AllEntries|QDir::AllDirs|QDir::NoDotAndDotDot|QDir::Hidden, QDir::Name); 0138 int offset = fileinfos.count(); 0139 fileinfos.append(list); 0140 parent.firstSiblingId = (list.count()) ?getId(File, offset) :-1; 0141 for (int i=0; i<list.size(); ++i) { 0142 ElementNode n; 0143 n.parentId = getId(File, parentIndex); 0144 n.previousId = (i) ?getId(File, offset + i - 1) :-1; 0145 n.nextId = (i == list.size()-1) ?-1 :getId(File, offset + i + 1); 0146 fileinfonodes.append(n); 0147 } 0148 } 0149 0150 void loadStreamSiblings(const QFileInfo& info, int parentIndex, ElementNode& parent) { 0151 // if (info.suffix().toLower() != "ppt") return; 0152 POLE::Storage storage(info.absoluteFilePath().toLocal8Bit()); 0153 if (!storage.open()) return; 0154 int offset = olestreams.count(); 0155 int count = 0; 0156 list<string> entries = storage.entries(); 0157 for (list<string>::const_iterator i=entries.begin(); i!=entries.end(); ++i) { 0158 if (!storage.isDirectory(*i)) { 0159 POLE::Stream stream(&storage, "/"+*i); 0160 OleStreamElementNode n; 0161 n.streamname = *i; 0162 n.size = stream.size(); 0163 n.isdir = false; 0164 n.parentId = getId(File, parentIndex); 0165 n.previousId = (count) ?getId(OLEStream, offset + count - 1) :-1; 0166 n.nextId = getId(OLEStream, offset + count + 1); 0167 olestreams.append(n); 0168 count++; 0169 } 0170 } 0171 if (count) { 0172 parent.firstSiblingId = getId(OLEStream, offset); 0173 olestreams[olestreams.count()-1].nextId = -1; 0174 } 0175 } 0176 0177 QDomElement createTestElement() { 0178 QDomElement e(doc.createElement("TESTING")); 0179 e.setAttribute("e", "E"); 0180 QDomElement a(doc.createElement("suba")); 0181 a.setAttribute("a", "A"); 0182 QDomElement b(doc.createElement("subb")); 0183 b.setAttribute("b", "B"); 0184 b.setAttribute("c", "C"); 0185 e.appendChild(a); 0186 e.appendChild(b); 0187 return e; 0188 } 0189 0190 void loadStreamAsXmlElements(int parentId, ElementNode& parent) { 0191 parent.firstSiblingId = getId(XmlElement, xmlelements.count()); 0192 QDomElement e = createTestElement(); 0193 XmlElementNode& en = addXmlElement(e); 0194 en.parentId = parentId; 0195 en.nextId = -1; 0196 en.previousId = -1; 0197 } 0198 0199 XmlElementNode& addXmlElement(const QDomElement& e) { 0200 int eindex = xmlelements.count(); 0201 xmlelements.append(XmlElementNode()); 0202 XmlElementNode& xe = xmlelements[eindex]; 0203 xe.element = e; 0204 0205 int eid = getId(XmlElement, eindex); 0206 int count = 0; 0207 QDomElement ce = e.firstChildElement(); 0208 while (!ce.isNull()) { 0209 count++; 0210 XmlElementNode& cen = addXmlElement(ce); 0211 cen.parentId = eid; 0212 cen.previousId = (count) ?eid + count - 1:-1; 0213 ce = ce.nextSiblingElement(); 0214 cen.nextId = (ce.isNull()) ?-1 :eid + count + 1; 0215 } 0216 xe.firstSiblingId = (count > 0) ?eid + 1 :-1; 0217 return xe; 0218 } 0219 }; 0220 0221 DeepFileTree::DeepFileTree(const QXmlNamePool& pool) :d(new Private(pool)) { 0222 } 0223 DeepFileTree::~DeepFileTree() { 0224 delete d; 0225 } 0226 0227 QXmlNodeModelIndex 0228 DeepFileTree::toNodeIndex(const QFileInfo& fileinfo) const { 0229 int indexOf = d->fileinfos.indexOf(fileinfo); 0230 if (indexOf == -1) { 0231 indexOf = d->getFileIndex(fileinfo); 0232 if (indexOf == -1) return QXmlNodeModelIndex(); 0233 } 0234 return createIndex(indexOf, Element); 0235 } 0236 0237 QVector<QXmlNodeModelIndex> 0238 DeepFileTree::attributes(const QXmlNodeModelIndex& element) const { 0239 QVector<QXmlNodeModelIndex> v; 0240 Type type = getType(element.data()); 0241 switch (type) { 0242 case File: { 0243 v.append(createIndex(element.data(), AttributeName)); 0244 const QFileInfo* info = d->getFileInfo(element.data()); 0245 if (info->isFile()) { 0246 v.append(createIndex(element.data(), AttributeSize)); 0247 } 0248 break; 0249 } 0250 case OLEStream: { 0251 v.append(createIndex(element.data(), AttributeName)); 0252 const OleStreamElementNode& e = (const OleStreamElementNode&)d->getElementNode(element.data()); 0253 if (!e.isdir) { 0254 v.append(createIndex(element.data(), AttributeSize)); 0255 } 0256 break; 0257 } 0258 case XmlElement: { 0259 QDomElement e = d->xmlelements[getIndex(element.data())].element; 0260 QDomNamedNodeMap attributes = e.attributes(); 0261 for (uint i=0; i<attributes.length(); ++i) { 0262 v.append(createIndex(element.data(), i+1)); 0263 } 0264 } 0265 } 0266 return v; 0267 } 0268 0269 QXmlNodeModelIndex 0270 DeepFileTree::nextFromSimpleAxis(SimpleAxis axis, const QXmlNodeModelIndex& origin) const { 0271 const QFileInfo* info = d->getFileInfo(origin.data()); 0272 ElementNode& node = d->getElementNode(origin.data()); 0273 0274 int data = -1; 0275 switch (axis) { 0276 case Parent: 0277 data = node.parentId; break; 0278 0279 case FirstChild: 0280 if (node.firstSiblingId == -2) { 0281 if (info && info->isDir()) { 0282 d->loadFileSiblings(info->filePath(), origin.data(), node); 0283 } else if (info && info->isFile()) { 0284 d->loadStreamSiblings(info->filePath(), origin.data(), node); 0285 } else if (getType(origin.data()) == OLEStream) { 0286 d->loadStreamAsXmlElements(origin.data(), node); 0287 } 0288 if (node.firstSiblingId == -2) { 0289 node.firstSiblingId = -1; 0290 } 0291 } 0292 data = node.firstSiblingId; break; 0293 0294 case PreviousSibling: 0295 data = node.previousId; break; 0296 0297 case NextSibling: 0298 data = node.nextId; break; 0299 }; 0300 return (data == -1) ?QXmlNodeModelIndex() :createIndex(data, 0); 0301 } 0302 0303 QUrl 0304 DeepFileTree::baseUri(const QXmlNodeModelIndex& n) const { 0305 return documentUri(n); 0306 } 0307 QXmlNodeModelIndex::DocumentOrder 0308 DeepFileTree::compareOrder(const QXmlNodeModelIndex& ni1, const QXmlNodeModelIndex& ni2) const { 0309 if (ni1.data() < ni2.data()) return QXmlNodeModelIndex::Precedes; 0310 return (ni1.data() == ni2.data()) ?QXmlNodeModelIndex::Is :QXmlNodeModelIndex::Follows; 0311 } 0312 QUrl 0313 DeepFileTree::documentUri(const QXmlNodeModelIndex& n) const { 0314 // TODO: find root and get uri from that 0315 const QFileInfo& fileinfo = d->fileinfos[n.data()]; 0316 return QUrl::fromLocalFile(fileinfo.filePath()); 0317 } 0318 QXmlNodeModelIndex 0319 DeepFileTree::elementById(const QXmlName& id) const { 0320 return QXmlNodeModelIndex(); 0321 } 0322 QXmlNodeModelIndex::NodeKind 0323 DeepFileTree::kind(const QXmlNodeModelIndex& ni) const { 0324 return ni.additionalData() == Element ? QXmlNodeModelIndex::Element : QXmlNodeModelIndex::Attribute; 0325 } 0326 QXmlName 0327 DeepFileTree::name(const QXmlNodeModelIndex& ni) const { 0328 QVector<QXmlNodeModelIndex> v; 0329 Type type = getType(ni.data()); 0330 switch (type) { 0331 case File: 0332 switch ((Kind)ni.additionalData()) { 0333 case Element: return d->getFileInfo(ni.data())->isFile() ?d->file :d->directory; 0334 case AttributeName: return d->name; 0335 case AttributeSize: return d->size; 0336 } 0337 case OLEStream: 0338 switch ((Kind)ni.additionalData()) { 0339 case Element: { 0340 const OleStreamElementNode& e 0341 = (const OleStreamElementNode&)d->getElementNode(ni.data()); 0342 return (e.isdir) ?d->olestreamdir :d->olestream; 0343 } 0344 case AttributeName: return d->name; 0345 case AttributeSize: return d->size; 0346 } 0347 case XmlElement: 0348 default: { 0349 QDomElement e = d->xmlelements[getIndex(ni.data())].element; 0350 if (ni.additionalData() == 0) { 0351 return QXmlName(d->namepool, e.nodeName()); 0352 } else { 0353 return QXmlName(d->namepool, e.attributes().item(ni.additionalData()-1).nodeName()); 0354 } 0355 } 0356 } 0357 } 0358 QVector<QXmlName> 0359 DeepFileTree::namespaceBindings(const QXmlNodeModelIndex& n) const { 0360 return QVector<QXmlName>(); 0361 } 0362 QVector<QXmlNodeModelIndex> 0363 DeepFileTree::nodesByIdref(const QXmlName& idref) const { 0364 return QVector<QXmlNodeModelIndex>(); 0365 } 0366 QXmlNodeModelIndex 0367 DeepFileTree::root(const QXmlNodeModelIndex& n) const { 0368 int index = n.data(); 0369 ElementNode& e = d->getElementNode(n.data()); 0370 while (e.parentId != -1) { 0371 index = e.parentId; 0372 e = d->getElementNode(index); 0373 } 0374 return createIndex(index, 0); 0375 } 0376 QString 0377 DeepFileTree::stringValue(const QXmlNodeModelIndex& n) const { 0378 return typedValue(n).toString(); 0379 } 0380 QVariant 0381 DeepFileTree::typedValue(const QXmlNodeModelIndex& n) const { 0382 Type type = getType(n.data()); 0383 switch (type) { 0384 case File: { 0385 const QFileInfo* info = d->getFileInfo(n.data()); 0386 switch ((Kind)n.additionalData()) { 0387 case AttributeName: return info->fileName(); 0388 case AttributeSize: return info->size(); 0389 default:; 0390 } 0391 } 0392 case OLEStream: { 0393 const OleStreamElementNode& e 0394 = (const OleStreamElementNode&)d->getElementNode(n.data()); 0395 switch ((Kind)n.additionalData()) { 0396 case AttributeName: return e.streamname.c_str(); 0397 case AttributeSize: return e.size; 0398 default:; 0399 } 0400 } 0401 case XmlElement: { 0402 if (n.additionalData() > 0) { 0403 QDomElement e = d->xmlelements[getIndex(n.data())].element; 0404 return e.attributes().item(n.additionalData()-1).nodeValue(); 0405 } 0406 } 0407 } 0408 return QVariant(); 0409 }