Ver código fonte

recursive parsing

Kandrashin Denis 13 anos atrás
pai
commit
f769390f83
2 arquivos alterados com 209 adições e 103 exclusões
  1. 129 98
      source/fb2read.cpp
  2. 80 5
      source/fb2read.h

+ 129 - 98
source/fb2read.cpp

@@ -14,39 +14,135 @@ static QString Value(const QXmlAttributes &attributes, const QString &name)
     return QString();
 }
 
-Fb2Handler::SectionHash::SectionHash()
+//---------------------------------------------------------------------------
+//  Fb2Handler::RootHandler
+//---------------------------------------------------------------------------
+
+Fb2Handler::RootHandler::KeywordHash::KeywordHash()
 {
     insert("body", Body);
     insert("descriptin", Descr);
     insert("binary", Binary);
 }
 
-Fb2Handler::KeywordHash::KeywordHash()
+Fb2Handler::RootHandler::Keyword Fb2Handler::RootHandler::GetKeyword(const QString &name)
+{
+    static KeywordHash map;
+    KeywordHash::const_iterator i = map.find(name);
+    return i == map.end() ? None : i.value();
+}
+
+bool Fb2Handler::RootHandler::DoStart(const QString &name, const QXmlAttributes &attributes)
+{
+    switch (GetKeyword(name)) {
+        case Descr  : return m_owner.SetHandler(new DescrHandler(this));
+        case Body   : return m_owner.SetHandler(new BodyHandler(this));
+        case Binary : return m_owner.SetHandler(new ImageHandler(this, attributes));
+    }
+    return false;
+}
+
+//---------------------------------------------------------------------------
+//  Fb2Handler::DescrHandler
+//---------------------------------------------------------------------------
+
+bool Fb2Handler::DescrHandler::DoStart(const QString &name, const QXmlAttributes &attributes)
 {
+    return true;
+}
+
+bool Fb2Handler::DescrHandler::DoText(const QString &text)
+{
+    return true;
+}
+
+bool Fb2Handler::DescrHandler::DoEnd(const QString &name, bool & exit)
+{
+    exit = name == "description";
+    return true;
+}
+
+//---------------------------------------------------------------------------
+//  Fb2Handler::BodyHandler
+//---------------------------------------------------------------------------
+
+Fb2Handler::BodyHandler::KeywordHash::KeywordHash()
+{
+    insert("body",    Body);
     insert("image",   Image);
     insert("p",       Paragraph);
     insert("section", Section);
     insert("title",   Title);
 }
 
-Fb2Handler::DocSection Fb2Handler::GetSection(const QString &name)
+Fb2Handler::BodyHandler::Keyword Fb2Handler::BodyHandler::GetKeyword(const QString &name)
 {
-    static SectionHash map;
-    SectionHash::const_iterator i = map.find(name);
+    static KeywordHash map;
+    KeywordHash::const_iterator i = map.find(name);
     return i == map.end() ? None : i.value();
 }
 
-Fb2Handler::DocKeyword Fb2Handler::GetKeyword(const QString &name)
+bool Fb2Handler::BodyHandler::DoStart(const QString &name, const QXmlAttributes &attributes)
 {
-    static KeywordHash map;
-    KeywordHash::const_iterator i = map.find(name);
-    return i == map.end() ? Empty : i.value();
+    switch (GetKeyword(name)) {
+        case Image: {
+            QString image = Value(attributes, "href");
+            while (image.left(1) == "#") image.remove(0, 1);
+            if (!image.isEmpty()) m_cursor.insertImage(image);
+        } break;
+        case Paragraph: {
+            m_cursor.insertBlock();
+        } break;
+        case Section: {
+            m_frames << m_cursor.currentFrame();
+            QTextFrameFormat format;
+            format.setBorder(1);
+            format.setPadding(8);
+            m_cursor.insertFrame(format);
+        } break;
+    }
+}
+
+bool Fb2Handler::BodyHandler::DoText(const QString &text)
+{
+    m_cursor.insertText(text);
+    return true;
+}
+
+bool Fb2Handler::BodyHandler::DoEnd(const QString &name, bool & exit)
+{
+    switch (GetKeyword(name)) {
+        case Body: {
+            exit = true;
+        }; break;
+        case Section: {
+            if (!m_frames.isEmpty()) {
+                m_cursor.setPosition(m_frames.last()->lastPosition());
+                m_frames.removeLast();
+            }
+        }; break;
+    }
+    return true;
 }
 
+//---------------------------------------------------------------------------
+//  Fb2Handler::ImageHandler
+//---------------------------------------------------------------------------
+
+Fb2Handler::ImageHandler::ImageHandler(ContentHandler * parent, const QXmlAttributes &attributes)
+    : ContentHandler(parent), m_name(Value(attributes, "id"))
+{
+}
+
+//---------------------------------------------------------------------------
+//  Fb2Handler
+//---------------------------------------------------------------------------
+
 Fb2Handler::Fb2Handler(QTextEdit * editor)
     : m_editor(editor)
     , m_cursor(editor->textCursor())
     , m_section(None)
+    , m_handler(NULL)
 {
     m_cursor.beginEditBlock();
     editor->clear();
@@ -55,6 +151,7 @@ Fb2Handler::Fb2Handler(QTextEdit * editor)
 
 Fb2Handler::~Fb2Handler()
 {
+    if (m_handler) delete m_handler;
     m_cursor.endEditBlock();
 }
 
@@ -63,78 +160,39 @@ bool Fb2Handler::startElement(const QString & /* namespaceURI */,
                                const QString &qName,
                                const QXmlAttributes &attributes)
 {
-    m_text.clear();
-    m_name.clear();
-
     const QString name = qName.toLower();
-
-    switch (m_tags.count()) {
-        case 0: {
-            if (name != "fictionbook") {
-                m_error = QObject::tr("The file is not an FB2 file.");
-                return false;
-            };
-        } break;
-        case 1: {
-                m_section = GetSection(name);
-                switch (m_section) {
-                    case Binary: {
-                        m_name = Value(attributes, "id");
-                    } break;
-                };
-        } break;
-        default: {
-            switch (m_section) {
-                case Body: BodyNew(name, attributes); break;
-            }
-        } break;
+    if (m_handler) return m_handler->DoStart(name, attributes);
+
+    if (name == "fictionbook") {
+        m_handler = new RootHandler(*this);
+        return true;
+    } else {
+        m_error = QObject::tr("The file is not an FB2 file.");
+        return false;
     }
+}
 
-    m_tags << name;
+bool Fb2Handler::characters(const QString &str)
+{
+    return m_handler && m_handler->DoText(str);
+}
 
-    return true;
+Fb2Handler::ContentHandler * Fb2Handler::Up(Fb2Handler::ContentHandler * handler)
+{
+    if (!handler) return NULL;
+    ContentHandler * parent = handler->GetParent();
+    delete handler;
+    return parent;
 }
 
 bool Fb2Handler::endElement(const QString & /* namespaceURI */,
                              const QString & /* localName */,
                              const QString &qName)
 {
-    const QString name = qName.toLower();
-
-    switch (m_section) {
-        case Binary: {
-            QByteArray in; in.append(m_text);
-            QImage img = QImage::fromData(QByteArray::fromBase64(in));
-            if (!m_name.isEmpty()) m_editor->document()->addResource(QTextDocument::ImageResource, QUrl(m_name), img);
-        } break;
-    }
-
-    if (name == "section") {
-        if (!m_frames.isEmpty()) {
-            m_cursor.setPosition(m_frames.last()->lastPosition());
-            m_frames.removeLast();
-        }
-    }
-
-    int index = m_tags.lastIndexOf(name);
-    int count = m_tags.count();
-    for (int i = index; i < count; i++) m_tags.removeLast();
-    if (m_tags.count() < 2) m_section = None;
-
-    return true;
-}
-
-bool Fb2Handler::characters(const QString &str)
-{
-    switch (m_section) {
-        case Body: {
-            m_cursor.insertText(str);
-        } break;
-        default: {
-            m_text += str;
-        }
-    }
-    return true;
+    bool exit = false;
+    bool ok = m_handler && m_handler->DoEnd(qName.toLower(), exit);
+    if (exit) m_handler = Up(m_handler);
+    return ok;
 }
 
 bool Fb2Handler::fatalError(const QXmlParseException &exception)
@@ -155,33 +213,6 @@ QString Fb2Handler::errorString() const
     return m_error;
 }
 
-bool Fb2Handler::BodyNew(const QString &name, const QXmlAttributes &attributes)
-{
-    switch (GetKeyword(name)) {
-        case Paragraph: {
-            m_cursor.insertBlock();
-        } break;
-        case Section: {
-            m_frames << m_cursor.currentFrame();
-            QTextFrameFormat format;
-            format.setBorder(1);
-            format.setPadding(8);
-            m_cursor.insertFrame(format);
-        } break;
-        case Image: {
-            QString image = Value(attributes, "href");
-            while (image.left(1) == "#") image.remove(0, 1);
-            if (!image.isEmpty()) m_cursor.insertImage(image);
-        } break;
-    }
-    return true;
-}
-
-bool Fb2Handler::BodyEnd(const QString &name)
-{
-    return true;
-}
-
 /*
 
     QMessageBox::information(

+ 80 - 5
source/fb2read.h

@@ -36,15 +36,90 @@ private:
         Title,
     };
 
-    class SectionHash : public QHash<QString, DocSection> { public: SectionHash(); };
+    class ContentHandler
+    {
+    public:
+        ContentHandler(Fb2Handler & owner) : m_owner(owner), m_cursor(owner.GetCursor()), m_parent(NULL) {}
+        ContentHandler(ContentHandler * parent) : m_owner(parent->GetOwner()), m_cursor(m_owner.GetCursor()), m_parent(parent) {}
+        virtual bool DoStart(const QString &name, const QXmlAttributes &attributes) = 0;
+        virtual bool DoText(const QString &text) = 0;
+        virtual bool DoEnd(const QString &name, bool & exit) = 0;
+        Fb2Handler & GetOwner() { return m_owner; }
+        ContentHandler * GetParent() { return m_parent; }
+    protected:
+        Fb2Handler & m_owner;
+        QTextCursor & m_cursor;
+    private:
+        ContentHandler * m_parent;
+    };
+
+    class RootHandler : public ContentHandler
+    {
+    public:
+        RootHandler(Fb2Handler & owner) : ContentHandler(owner) {}
+        virtual bool DoStart(const QString &name, const QXmlAttributes &attributes);
+        virtual bool DoText(const QString &text) {}
+        virtual bool DoEnd(const QString &name, bool & exit) { exit = true; }
+    private:
+        enum Keyword {
+            None = 0,
+            Body,
+            Descr,
+            Binary,
+        };
+        class KeywordHash : public QHash<QString, Keyword> { public: KeywordHash(); };
+        static Keyword GetKeyword(const QString & name);
+    };
+
+    class DescrHandler : public ContentHandler
+    {
+    public:
+        DescrHandler(ContentHandler * parent) : ContentHandler(parent) {}
+        virtual bool DoStart(const QString &name, const QXmlAttributes &attributes);
+        virtual bool DoText(const QString &text);
+        virtual bool DoEnd(const QString &name, bool & exit);
+    };
 
-    class KeywordHash : public QHash<QString, DocKeyword> { public: KeywordHash(); };
+    class BodyHandler : public ContentHandler
+    {
+    public:
+        BodyHandler(ContentHandler * parent) : ContentHandler(parent) {}
+        virtual bool DoStart(const QString &name, const QXmlAttributes &attributes);
+        virtual bool DoText(const QString &text);
+        virtual bool DoEnd(const QString &name, bool & exit);
+    private:
+        enum Keyword {
+            None = 0,
+            Body,
+            Image,
+            Paragraph,
+            Section,
+            Title,
+        };
+        class KeywordHash : public QHash<QString, Keyword> { public: KeywordHash(); };
+        static Keyword GetKeyword(const QString & name);
+    private:
+        QList<QTextFrame*> m_frames;
+    };
 
-    static DocSection GetSection(const QString &name);
+    class ImageHandler : public ContentHandler
+    {
+    public:
+        ImageHandler(ContentHandler * parent, const QXmlAttributes &attributes);
+        virtual bool DoStart(const QString &name, const QXmlAttributes &attributes) { return true; }
+        virtual bool DoText(const QString &text) { m_text += text; return true; }
+        virtual bool DoEnd(const QString &name, bool & exit) { exit = true; return true; }
+    private:
+        QString m_name;
+        QString m_text;
+    };
 
-    static DocKeyword GetKeyword(const QString &name);
+public:
+    bool SetHandler(ContentHandler * handler) { m_handler = handler; return handler; }
+    QTextCursor & GetCursor() { return m_cursor; }
 
 private:
+    static ContentHandler * Up(ContentHandler * handler);
     bool BodyNew(const QString &name, const QXmlAttributes &attributes);
     bool BodyEnd(const QString &name);
 
@@ -56,7 +131,7 @@ private:
     QString m_error;
     QStringList m_tags;
     DocSection m_section;
-    QList<QTextFrame*> m_frames;
+    ContentHandler * m_handler;
 };
 
 #endif // FB2READ_H