• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdeedu
  • Sitemap
  • Contact Us
 

step/stepcore

xmlfile.cc

Go to the documentation of this file.
00001 /* This file is part of StepCore library.
00002    Copyright (C) 2007 Vladimir Kuznetsov <ks.vladimir@gmail.com>
00003 
00004    StepCore library is free software; you can redistribute it and/or modify
00005    it under the terms of the GNU General Public License as published by
00006    the Free Software Foundation; either version 2 of the License, or
00007    (at your option) any later version.
00008 
00009    StepCore library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012    GNU General Public License for more details.
00013 
00014    You should have received a copy of the GNU General Public License
00015    along with StepCore; if not, write to the Free Software
00016    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00017 */
00018 
00019 #include "xmlfile.h"
00020 
00021 #ifdef STEPCORE_WITH_QT
00022 
00023 #include "object.h"
00024 #include "world.h"
00025 #include "solver.h"
00026 #include "collisionsolver.h"
00027 #include "constraintsolver.h"
00028 #include "factory.h"
00029 
00030 #include <QTextStream>
00031 #include <QMetaProperty>
00032 #include <QXmlDefaultHandler>
00033 
00034 namespace StepCore {
00035 
00036 const char* XmlFile::DOCKTYPE = "StepCoreXML";
00037 const char* XmlFile::NAMESPACE_URI = "http://edu.kde.org/step/StepCoreXML";
00038 const char* XmlFile::VERSION = "1.0";
00039 
00040 namespace {
00041 
00042 class StepStreamWriter
00043 {
00044 public:
00045     StepStreamWriter(QIODevice* device): _device(device) {}
00046     bool writeWorld(const World* world);
00047 
00048 protected:
00049     QString escapeText(const QString& str);
00050     void saveProperties(const Object* obj, int first, int indent);
00051     void saveObject(const QString& tag, const Object* obj, int indent);
00052 
00053     QIODevice*   _device;
00054     QTextStream* _stream;
00055     QHash<const Object*, int> _ids;
00056     static const int INDENT = 4;
00057 };
00058 
00059 QString StepStreamWriter::escapeText(const QString& str)
00060 {
00061     QString result = str;
00062     result.replace('&', "&amp;");
00063     result.replace('<', "&lt;");
00064     result.replace('>', "&gt;");
00065     result.replace('\"', "&quot;");
00066     return result;
00067 }
00068 
00069 void StepStreamWriter::saveProperties(const Object* obj, int first, int indent)
00070 {
00071     const MetaObject* metaObject = obj->metaObject();
00072     for(int i = first; i < metaObject->propertyCount(); ++i) {
00073         const MetaProperty* p = metaObject->property(i);
00074         if(p->isStored()) {
00075             *_stream << QString(indent*INDENT, ' ')
00076                      << "<" << p->name() << ">";
00077 
00078             if(p->userTypeId() == qMetaTypeId<Object*>())
00079                 *_stream << _ids.value(p->readVariant(obj).value<Object*>(), -1);
00080             else *_stream << escapeText(p->readString(obj));
00081 
00082             *_stream << "</" << p->name() << ">\n";
00083         }
00084     }
00085 }
00086 
00087 void StepStreamWriter::saveObject(const QString& tag, const Object* obj, int indent)
00088 {
00089     Q_ASSERT(obj != NULL);
00090 
00091     *_stream << QString(indent*INDENT, ' ') << "<" << tag
00092              << " class=\"" << QString(obj->metaObject()->className())
00093              << "\" id=\"" << _ids.value(obj, -1) << "\">\n";
00094 
00095     saveProperties(obj, 0, indent+1);
00096 
00097     if(obj->metaObject()->inherits<Item>()) {
00098         const ObjectErrors* objErrors = static_cast<const Item*>(obj)->tryGetObjectErrors();
00099         if(objErrors) saveProperties(objErrors, 1, indent+1);
00100     }
00101 
00102     if(obj->metaObject()->inherits<ItemGroup>()) {
00103         const ItemGroup* group = static_cast<const ItemGroup*>(obj);
00104         *_stream << "\n";
00105         ItemList::const_iterator end = group->items().end();
00106         for(ItemList::const_iterator it = group->items().begin(); it != end; ++it) {
00107             saveObject("item", *it, indent+1);
00108             *_stream << "\n";
00109         }
00110     }
00111 
00112     *_stream << QString(indent*INDENT, ' ') << "</" << tag << ">\n";
00113 }
00114 
00115 bool StepStreamWriter::writeWorld(const World* world)
00116 {
00117     Q_ASSERT(_device->isOpen() && _device->isWritable());
00118     _stream = new QTextStream(_device);
00119 
00120     int maxid = -1;
00121     _ids.insert(NULL, ++maxid);
00122     _ids.insert(world, ++maxid);
00123 
00124     ItemList items = world->allItems();
00125     const ItemList::const_iterator end0 = items.end();
00126     for(ItemList::const_iterator it = items.begin(); it != end0; ++it)
00127         _ids.insert(*it, ++maxid);
00128 
00129     if(world->solver()) _ids.insert(world->solver(), ++maxid);
00130     if(world->collisionSolver()) _ids.insert(world->collisionSolver(), ++maxid);
00131     if(world->constraintSolver()) _ids.insert(world->constraintSolver(), ++maxid);
00132 
00133     *_stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
00134              << "<!DOCTYPE " << XmlFile::DOCKTYPE << ">\n"
00135              << "<world xmlns=\"" << XmlFile::NAMESPACE_URI << "\""
00136              << " version=\"" << XmlFile::VERSION << "\" id=\"1\">\n";
00137 
00138     saveProperties(world, 0, 1);
00139     *_stream << "\n";
00140 
00141     ItemList::const_iterator end = world->items().end();
00142     for(ItemList::const_iterator it = world->items().begin(); it != end; ++it) {
00143         saveObject("item", *it, 1);
00144         *_stream << "\n";
00145     }
00146 
00147     if(world->solver()) {
00148         saveObject("solver", world->solver(), 1);
00149         *_stream << "\n";
00150     }
00151 
00152     if(world->collisionSolver()) {
00153         saveObject("collisionSolver", world->collisionSolver(), 1);
00154         *_stream << "\n";
00155     }
00156 
00157     if(world->constraintSolver()) {
00158         saveObject("constraintSolver", world->constraintSolver(), 1);
00159         *_stream << "\n";
00160     }
00161 
00162     *_stream << "</world>\n";
00163 
00164     delete _stream;
00165     return true;
00166 }
00167 
00168 class XmlFileHandler: public QXmlDefaultHandler
00169 {
00170 public:
00171     XmlFileHandler(World* world, const Factory* factory);
00172 
00173     bool startElement(const QString &namespaceURI, const QString &localName,
00174                       const QString &qName, const QXmlAttributes &attributes);
00175     bool endElement(const QString &namespaceURI, const QString &localName,
00176                     const QString &qName);
00177     bool characters(const QString &str);
00178     bool fatalError(const QXmlParseException &exception);
00179     bool endDocument();
00180     QString errorString() const;
00181 
00182 protected:
00183     bool addId(Object* obj, const QString& id);
00184 
00185     enum { START, ITEM, PROPERTY, END } _state;
00186     World*         _world;
00187     const Factory* _factory;
00188 
00189     ItemGroup* _parent;
00190     Object*    _object;
00191     ObjectErrors*       _objectErrors;
00192     const MetaProperty* _property;
00193 
00194     typedef QPair<QPair<Object*, const MetaProperty*>, int> Link;
00195     QHash<int, Object*> _ids;
00196     QList<Link> _links;
00197 
00198     QString _text;
00199     QString _errorString;
00200 };
00201 
00202 XmlFileHandler::XmlFileHandler(World* world, const Factory* factory)
00203     : _state(START), _world(world), _factory(factory),
00204       _parent(NULL), _object(NULL), _objectErrors(NULL), _property(NULL)
00205 {
00206 }
00207 
00208 bool XmlFileHandler::addId(Object* obj, const QString& id)
00209 {
00210     /*
00211 #ifdef __GNUC__
00212 #warning Temporary code
00213 #endif
00214     if(id.isEmpty()) return true;
00215     */
00216     int n = id.trimmed().toInt();
00217     if(!n) {
00218         _errorString = QObject::tr("Wrong ID attribute value for %1")
00219                         .arg(obj->metaObject()->className());
00220         return false;
00221     }
00222     if(_ids.contains(n)) {
00223         _errorString = QObject::tr("Non-unique ID attribute value for %1")
00224                         .arg(obj->metaObject()->className());
00225         return false;
00226     }
00227 
00228     _ids.insert(n, _object);
00229     return true;
00230 }
00231 
00232 bool XmlFileHandler::startElement(const QString &namespaceURI, const QString &,
00233                   const QString &qName, const QXmlAttributes &attributes)
00234 {
00235     if(namespaceURI != XmlFile::NAMESPACE_URI) return true; // XXX: is it correct behaviour ?
00236 
00237     switch(_state) {
00238     case START:
00239         if(qName == "world") {
00240             _parent = NULL;
00241             _object = _world;
00242             _state = ITEM;
00243 
00244             if(!addId(_object, attributes.value("id"))) return false;
00245 
00246         } else {
00247             _errorString = QObject::tr("The file is not a StepCoreXML file.");
00248             return false;
00249         }
00250         break;
00251 
00252     case ITEM:
00253         if(qName == "item" && _object->metaObject()->inherits<ItemGroup>()) {
00254             _parent = static_cast<ItemGroup*>(_object);
00255 
00256             QString className = attributes.value("class");
00257             Item* item = _factory->newItem(className);
00258             if(item == NULL) {
00259                 _errorString = QObject::tr("Unknown item type \"%1\"").arg(className);
00260                 return false;
00261             }
00262 
00263             _parent->addItem(item);
00264             _object = item;
00265 
00266             if(!addId(_object, attributes.value("id"))) return false;
00267 
00268             break;
00269 
00270         } else if(_object == _world && qName == "solver") {
00271             Solver* solver = _factory->newSolver(attributes.value("class")); 
00272             if(solver == NULL) {
00273                 _errorString = QObject::tr("Unknown solver type \"%1\"").arg(attributes.value("class"));
00274                 return false;
00275             }
00276 
00277             _world->setSolver(solver);
00278             _object = solver;
00279             _parent = _world;
00280 
00281             if(!addId(_object, attributes.value("id"))) return false;
00282 
00283             break;
00284 
00285         } else if(_object == _world && qName == "collisionSolver") {
00286             CollisionSolver* collisionSolver = _factory->newCollisionSolver(attributes.value("class")); 
00287             if(collisionSolver == NULL) {
00288                 _errorString = QObject::tr("Unknown collisionSolver type \"%1\"").arg(attributes.value("class"));
00289                 return false;
00290             }
00291 
00292             _world->setCollisionSolver(collisionSolver);
00293             _object = collisionSolver;
00294             _parent = _world;
00295 
00296             if(!addId(_object, attributes.value("id"))) return false;
00297 
00298             break;
00299 
00300         } else if(_object == _world && qName == "constraintSolver") {
00301             ConstraintSolver* constraintSolver = _factory->newConstraintSolver(attributes.value("class")); 
00302             if(constraintSolver == NULL) {
00303                 _errorString = QObject::tr("Unknown constraintSolver type \"%1\"").arg(attributes.value("class"));
00304                 return false;
00305             }
00306 
00307             _world->setConstraintSolver(constraintSolver);
00308             _object = constraintSolver;
00309             _parent = _world;
00310 
00311             if(!addId(_object, attributes.value("id"))) return false;
00312 
00313             break;
00314 
00315         }
00316 
00317 
00318         _property = _object->metaObject()->property(qName);
00319         if(!_property && _object->metaObject()->inherits<Item>()) {
00320             const MetaObject* objErrors = _factory->metaObject(_object->metaObject()->className()+"Errors");
00321             if(objErrors) {
00322                 _property = objErrors->property(qName);
00323                 if(_property && _property->isStored())
00324                     _objectErrors = static_cast<Item*>(_object)->objectErrors();
00325             }
00326         }
00327 
00328         if(!_property || !_property->isStored()) {
00329             _errorString = QObject::tr("Item \"%1\" has no stored property named \"%2\"")
00330                                 .arg(QString(_object->metaObject()->className())).arg(qName);
00331             return false;
00332         }
00333 
00334         _text.clear();
00335         _state = PROPERTY;
00336         break;
00337 
00338     case PROPERTY:
00339     default:
00340         _errorString = QObject::tr("Unexpected tag \"%1\"").arg(qName);
00341         return false;
00342     }
00343 
00344     return true;
00345 }
00346 
00347 bool XmlFileHandler::endElement(const QString &namespaceURI, const QString &,
00348                 const QString &qName)
00349 {
00350     if(namespaceURI != XmlFile::NAMESPACE_URI) return true; // XXX: is it correct behaviour ?
00351 
00352     switch(_state) {
00353     case PROPERTY:
00354         if(_property->userTypeId() == qMetaTypeId<Object*>()) {
00355             /*
00356 #ifdef __GNUC__
00357 #warning Temporary code
00358 #endif
00359             if(!_text.trimmed()[0].isDigit()) {
00360                 Object* o = _world->object(_text);
00361                 qDebug("deprecated link to %s (%p)", _text.toLatin1().constData(), o);
00362                 QVariant obj = QVariant::fromValue(o);
00363                 _property->writeVariant(_object, obj);
00364             } else*/ {
00365                 int n = _text.trimmed().toInt();
00366                 _links.push_back(qMakePair(qMakePair(
00367                         static_cast<Object*>(_objectErrors ? _objectErrors : _object),
00368                         _property), n));
00369             }
00370         }
00371         else if(!_property->writeString(_objectErrors ? _objectErrors : _object, _text)) {
00372             _errorString = QObject::tr("Property \"%1\" of \"%2\" has illegal value")
00373                                 .arg(qName, _object->metaObject()->className());
00374             return false;
00375         }
00376         _objectErrors = NULL;
00377         _state = ITEM;
00378         break;
00379 
00380     case ITEM:
00381         if(_parent == NULL) {
00382             STEPCORE_ASSERT_NOABORT(_object == _world);
00383             _state = END;
00384         } else {
00385             STEPCORE_ASSERT_NOABORT(_parent->metaObject()->inherits<Item>());
00386             Item* item = static_cast<Item*>(_parent);
00387             _object = _parent;
00388             _parent = item->group();
00389         }
00390         break;
00391 
00392     default:
00393         STEPCORE_ASSERT_NOABORT(false);
00394     }
00395 
00396     return true;
00397 }
00398 
00399 bool XmlFileHandler::characters(const QString &str)
00400 {
00401     if(_state == PROPERTY) _text += str;
00402     return true;
00403 }
00404 
00405 bool XmlFileHandler::endDocument()
00406 {
00407     if(_state != END) {
00408         _errorString = QObject::tr("\"world\" tag not found");
00409         return false;
00410     }
00411 
00412     // Connect links
00413     foreach(const Link& link, _links) {
00414         QVariant target = QVariant::fromValue(_ids.value(link.second, NULL));
00415         if(!link.first.second->writeVariant(link.first.first, target)) {
00416             _errorString = QObject::tr("Property \"%1\" of \"%2\" has illegal value")
00417                     .arg(link.first.second->name(), link.first.first->metaObject()->className());
00418             return false;
00419         }
00420     }
00421 
00422     return true;
00423 }
00424 
00425 bool XmlFileHandler::fatalError(const QXmlParseException &exception)
00426 {
00427     _errorString = QObject::tr("Error parsing file at line %1: %2")
00428                         .arg(exception.lineNumber()).arg(exception.message());
00429     return false;
00430 }
00431 
00432 QString XmlFileHandler::errorString() const
00433 {
00434     return _errorString;
00435 }
00436 
00437 } // namespace
00438 
00439 bool XmlFile::save(const World* world)
00440 {
00441     if(!_device->isOpen() || !_device-> isWritable()) {
00442         _errorString = QObject::tr("File is not writable.");
00443         return false;
00444     }
00445 
00446     StepStreamWriter writer(_device);
00447     return writer.writeWorld(world);
00448 }
00449 
00450 bool XmlFile::load(World* world, const Factory* factory)
00451 {
00452     XmlFileHandler handler(world, factory);
00453     QXmlInputSource source(_device);
00454     QXmlSimpleReader reader;
00455 
00456     reader.setContentHandler(&handler);
00457     reader.setErrorHandler(&handler);
00458     if(reader.parse(source)) return true;
00459     _errorString = handler.errorString();
00460     return false;
00461 }
00462 
00463 } // namespace StepCore
00464 
00465 #endif //STEPCORE_WITH_QT
00466 

step/stepcore

Skip menu "step/stepcore"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdeedu

Skip menu "kdeedu"
  • kalzium
  • kanagram
  • kig
  •   lib
  • klettres
  • kstars
  • libkdeedu
  •   keduvocdocument
  •   docs
  •   src
  • parley
  •   stepcore
Generated for kdeedu by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal