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 }