Skip to content
Browse files

Instance model (or at least something like it)

  • Loading branch information...
1 parent f33ab9b commit 00893b3cfc68f12c09e84643d255044a488b0eb6 @peterix peterix committed Jan 22, 2013
View
6 CMakeLists.txt
@@ -12,6 +12,8 @@ IF(${BIGENDIAN})
ADD_DEFINITIONS(-DMULTIMC_BIG_ENDIAN)
ENDIF(${BIGENDIAN})
+# First, include header overrides
+include_directories(hacks)
#### Find the required Qt parts ####
find_package(Qt5Widgets)
@@ -101,7 +103,7 @@ main.cpp
data/appsettings.cpp
data/inifile.cpp
data/instancebase.cpp
-data/instancelist.cpp
+data/instancemodel.cpp
data/settingsbase.cpp
data/stdinstance.cpp
@@ -123,7 +125,7 @@ gui/settingsdialog.h
data/appsettings.h
data/inifile.h
data/instancebase.h
-data/instancelist.h
+data/instancemodel.h
data/settingsbase.h
data/settingsmacros.h
data/settingsmacrosundef.h
View
17 data/inifile.cpp
@@ -49,12 +49,23 @@ bool INIFile::loadFile(QString fileName)
QStringList lines = in.readAll().split('\n');
for (int i = 0; i < lines.count(); i++)
{
+ QString & lineRaw = lines[i];
// Ignore comments.
- QString line = lines[i].left('#').trimmed();
+ QString line = lineRaw.left(lineRaw.indexOf('#')).trimmed();
- QString key = line.section('=', 0).trimmed();
- QVariant value(line.section('=', 1).trimmed());
+ int eqPos = line.indexOf('=');
+ if(eqPos == -1)
+ continue;
+ QString key = line.left(eqPos).trimmed();
+ QString valueStr = line.right(line.length() - eqPos - 1).trimmed();
+ QVariant value(valueStr);
+ /*
+ QString dbg = key;
+ dbg += " = ";
+ dbg += valueStr;
+ qDebug(dbg.toLocal8Bit());
+ */
this->operator [](key) = value;
}
View
64 data/instancebase.cpp
@@ -16,17 +16,35 @@
#include "instancebase.h"
#include <QFileInfo>
+#include <QDir>
#include "../util/pathutils.h"
InstanceBase::InstanceBase(QString dir, QObject *parent) :
QObject(parent),
rootDir(dir)
{
- QFileInfo cfgFile;
+ QFileInfo cfgFile(PathCombine(rootDir, "instance.cfg"));
if (cfgFile.exists())
- config.loadFile(PathCombine(rootDir, "instance.cfg"));
+ {
+ if(!config.loadFile(cfgFile.absoluteFilePath()))
+ {
+ QString debugmsg("Can't load instance config file for instance ");
+ debugmsg+= getInstID();
+ qDebug(debugmsg.toLocal8Bit());
+ }
+ }
+ else
+ {
+ QString debugmsg("Can't find instance config file for instance ");
+ debugmsg+= getInstID();
+ debugmsg += " : ";
+ debugmsg +=
+ debugmsg+=" ... is this an instance even?";
+ qDebug(debugmsg.toLocal8Bit());
+ }
+ currentGroup = nullptr;
}
QString InstanceBase::getRootDir() const
@@ -47,3 +65,45 @@ void InstanceBase::setInstName(QString name)
{
config.set("name", name);
}
+
+QString InstanceBase::getInstID() const
+{
+ return QDir(rootDir).dirName();
+}
+
+InstanceModelItem* InstanceBase::getParent() const
+{
+ return currentGroup;
+}
+
+QVariant InstanceBase::data ( int role ) const
+{
+ switch(role)
+ {
+ case Qt::DisplayRole:
+ return getInstName();
+ default:
+ return QVariant();
+ }
+}
+int InstanceBase::getRow() const
+{
+ return currentGroup->getIndexOf((InstanceBase*)this);
+}
+
+InstanceModelItem* InstanceBase::getChild ( int index ) const
+{
+ return nullptr;
+}
+InstanceModel* InstanceBase::getModel() const
+{
+ return currentGroup->getModel();
+}
+IMI_type InstanceBase::getModelItemType() const
+{
+ return IMI_Instance;
+}
+int InstanceBase::numChildren() const
+{
+ return 0;
+}
View
20 data/instancebase.h
@@ -20,9 +20,11 @@
#include <QString>
#include "../data/inifile.h"
+#include "instancemodel.h"
-class InstanceBase : public QObject
+class InstanceBase : public QObject, public InstanceModelItem
{
+ friend class InstanceGroup;
Q_OBJECT
public:
explicit InstanceBase(QString rootDir, QObject *parent = 0);
@@ -32,13 +34,25 @@ class InstanceBase : public QObject
QString getInstName() const;
void setInstName(QString name);
-protected:
+ QString getInstID() const;
+ virtual IMI_type getModelItemType() const;
+ virtual InstanceModelItem* getParent() const;
+ virtual int numChildren() const;
+ virtual InstanceModelItem* getChild ( int index ) const;
+ virtual InstanceModel* getModel() const;
+ virtual QVariant data ( int column ) const;
+ virtual int getRow() const;
private:
- QString rootDir;
+ void setGroup ( InstanceGroup* group )
+ {
+ currentGroup = group;
+ };
+ QString rootDir;
INIFile config;
+ InstanceGroup * currentGroup;
};
#endif // INSTANCEBASE_H
View
52 data/instancelist.cpp
@@ -1,52 +0,0 @@
-/* Copyright 2013 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "instancelist.h"
-
-#include <QDir>
-#include <QDirIterator>
-
-#include "stdinstance.h"
-
-#include "../util/pathutils.h"
-
-InstanceList::InstanceList() :
- QList()
-{
-
-}
-
-void InstanceList::addInstance(InstanceBase *inst)
-{
- append(inst);
-}
-
-void InstanceList::loadInstances(QString dir)
-{
- qDebug("Loading instances");
- QDir instDir(dir);
- QDirIterator iter(instDir);
-
- while (iter.hasNext())
- {
- QString subDir = iter.next();
- if (QFileInfo(PathCombine(subDir, "instance.cfg")).exists())
- {
- // TODO Differentiate between different instance types.
- InstanceBase* inst = new StdInstance(subDir);
- addInstance(inst);
- }
- }
-}
View
38 data/instancelist.h
@@ -1,38 +0,0 @@
-/* Copyright 2013 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef INSTANCELIST_H
-#define INSTANCELIST_H
-
-#include <QList>
-
-#include "instancebase.h"
-
-class InstanceList : public QList<InstanceBase*>
-{
-public:
- explicit InstanceList();
-
- void addInstance(InstanceBase *inst);
-
- void loadInstances(QString dir);
-
-signals:
-
-public slots:
-
-};
-
-#endif // INSTANCELIST_H
View
403 data/instancemodel.cpp
@@ -0,0 +1,403 @@
+/* Copyright 2013 MultiMC Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "instancemodel.h"
+
+#include <QString>
+#include <QDir>
+#include <QDirIterator>
+#include "stdinstance.h"
+
+#include "../util/pathutils.h"
+
+#include <boost/property_tree/json_parser.hpp>
+#include <boost/foreach.hpp>
+
+
+#define GROUP_FILE_FORMAT_VERSION 1
+
+InstanceModel::InstanceModel( QObject* parent ) :
+ QAbstractItemModel()
+{
+}
+
+InstanceModel::~InstanceModel()
+{
+ saveGroupInfo();
+ for(int i = 0; i < groups.size(); i++)
+ {
+ delete groups[i];
+ }
+}
+
+void InstanceModel::addInstance( InstanceBase* inst, const QString& groupName )
+{
+ auto group = getGroupByName(groupName);
+ group->addInstance(inst);
+}
+
+void InstanceGroup::addInstance ( InstanceBase* inst )
+{
+ instances.append(inst);
+ inst->setGroup(this);
+ // TODO: notify model.
+}
+
+
+void InstanceModel::initialLoad(QString dir)
+{
+ groupFile = dir + "/instgroups.json";
+ implicitGroup = new InstanceGroup("Ungrouped", this);
+ groups.append(implicitGroup);
+
+ // temporary map from instance ID to group name
+ QMap<QString, QString> groupMap;
+
+ using namespace boost::property_tree;
+ ptree pt;
+
+ try
+ {
+ read_json(groupFile.toStdString(), pt);
+
+ if (pt.get_optional<int>("formatVersion") != GROUP_FILE_FORMAT_VERSION)
+ {
+ // TODO: Discard old formats.
+ }
+
+ BOOST_FOREACH(const ptree::value_type& vp, pt.get_child("groups"))
+ {
+ ptree gPt = vp.second;
+ QString groupName = QString::fromUtf8(vp.first.c_str());
+
+ InstanceGroup *group = new InstanceGroup(groupName, this);
+ groups.push_back(group);
+
+ if (gPt.get_child_optional("hidden"))
+ group->setHidden(gPt.get<bool>("hidden"));
+
+ QVector<QString> groupInstances;
+ BOOST_FOREACH(const ptree::value_type& v, gPt.get_child("instances"))
+ {
+ QString key = QString::fromUtf8(v.second.data().c_str());
+ groupMap[key] = groupName;
+ }
+ }
+ }
+ catch (json_parser_error e)
+ {
+ qDebug("Failed to read group list. JSON parser error.");
+// wxLogError(_(),
+// e.line(), wxStr(e.message()).c_str());
+ }
+ catch (ptree_error e)
+ {
+ qDebug("Failed to read group list. Unknown ptree error.");
+ }
+
+ qDebug("Loading instances");
+ QDir instDir(dir);
+ QDirIterator iter(instDir);
+
+ while (iter.hasNext())
+ {
+ QString subDir = iter.next();
+ if (QFileInfo(PathCombine(subDir, "instance.cfg")).exists())
+ {
+ // TODO Differentiate between different instance types.
+ InstanceBase* inst = new StdInstance(subDir);
+ QString instID = inst->getInstID();
+ auto iter = groupMap.find(instID);
+ if(iter != groupMap.end())
+ {
+ addInstance(inst,iter.value());
+ }
+ else
+ {
+ addInstance(inst);
+ }
+ }
+ }
+}
+
+int InstanceModel::columnCount ( const QModelIndex& parent ) const
+{
+ // for now...
+ return 1;
+}
+
+QVariant InstanceModel::data ( const QModelIndex& index, int role ) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ if (role != Qt::DisplayRole)
+ return QVariant();
+
+ InstanceModelItem *item = static_cast<InstanceModelItem*>(index.internalPointer());
+
+ return item->data(index.column());
+}
+
+QModelIndex InstanceModel::index ( int row, int column, const QModelIndex& parent ) const
+{
+ if (!hasIndex(row, column, parent))
+ return QModelIndex();
+
+ InstanceModelItem *parentItem;
+
+ if (!parent.isValid())
+ parentItem = (InstanceModelItem *) this;
+ else
+ parentItem = static_cast<InstanceModelItem*>(parent.internalPointer());
+
+ InstanceModelItem *childItem = parentItem->getChild(row);
+ if (childItem)
+ return createIndex(row, column, childItem);
+ else
+ return QModelIndex();
+
+}
+
+QModelIndex InstanceModel::parent ( const QModelIndex& index ) const
+{
+ if (!index.isValid())
+ return QModelIndex();
+
+ InstanceModelItem *childItem = static_cast<InstanceModelItem*>(index.internalPointer());
+ InstanceModelItem *parentItem = childItem->getParent();
+
+ if (parentItem == this)
+ return QModelIndex();
+
+ return createIndex(parentItem->getRow(), 0, parentItem);
+}
+
+int InstanceModel::rowCount ( const QModelIndex& parent ) const
+{
+ InstanceModelItem *parentItem;
+ if (parent.column() > 0)
+ return 0;
+
+ if (!parent.isValid())
+ parentItem = (InstanceModelItem*) this;
+ else
+ parentItem = static_cast<InstanceModelItem*>(parent.internalPointer());
+
+ return parentItem->numChildren();
+}
+
+bool InstanceModel::saveGroupInfo() const
+{
+ /*
+ using namespace boost::property_tree;
+ ptree pt;
+
+ pt.put<int>("formatVersion", GROUP_FILE_FORMAT_VERSION);
+
+ try
+ {
+ typedef QMap<InstanceGroup *, QVector<InstanceBase*> > GroupListMap;
+
+ GroupListMap groupLists;
+ for (auto iter = instances.begin(); iter != instances.end(); iter++)
+ {
+ InstanceGroup *group = getInstanceGroup(*iter);
+
+ if (group != nullptr)
+ groupLists[group].push_back(*iter);
+ }
+
+ ptree groupsPtree;
+ for (auto iter = groupLists.begin(); iter != groupLists.end(); iter++)
+ {
+ auto group = iter.key();
+ auto & gList = iter.value();
+
+ ptree groupTree;
+
+ groupTree.put<bool>("hidden", group->isHidden());
+
+ ptree instList;
+ for (auto iter2 = gList.begin(); iter2 != gList.end(); iter2++)
+ {
+ std::string instID((*iter2)->getInstID().toUtf8());
+ instList.push_back(std::make_pair("", ptree(instID)));
+ }
+ groupTree.put_child("instances", instList);
+
+ groupsPtree.push_back(std::make_pair(std::string(group->getName().toUtf8()), groupTree));
+ }
+ pt.put_child("groups", groupsPtree);
+
+ write_json(groupFile.toStdString(), pt);
+ }
+ catch (json_parser_error e)
+ {
+// wxLogError(_("Failed to read group list.\nJSON parser error at line %i: %s"),
+// e.line(), wxStr(e.message()).c_str());
+ return false;
+ }
+ catch (ptree_error e)
+ {
+// wxLogError(_("Failed to save group list. Unknown ptree error."));
+ return false;
+ }
+
+ return true;
+ */
+ return false;
+}
+
+void InstanceModel::setInstanceGroup ( InstanceBase* inst, const QString& groupName )
+{
+ /*
+ InstanceGroup *prevGroup = getInstanceGroup(inst);
+
+ if (prevGroup != nullptr)
+ {
+ groupsMap.remove(inst);
+ }
+
+ if (!groupName.isEmpty())
+ {
+ InstanceGroup *newGroup = nullptr;
+
+ for (auto iter = root->groups.begin(); iter != root->groups.end(); iter++)
+ {
+ if ((*iter)->getName() == groupName)
+ {
+ newGroup = *iter;
+ }
+ }
+
+ if (newGroup == nullptr)
+ {
+ newGroup = new InstanceGroup(groupName, this);
+ root->groups.push_back(newGroup);
+ }
+
+ groupsMap[inst] = newGroup;
+ }
+
+ // TODO: propagate change, reflect in model, etc.
+ //InstanceGroupChanged(inst);
+ */
+}
+
+InstanceGroup* InstanceModel::getGroupByName ( const QString& name ) const
+{
+ for (auto iter = groups.begin(); iter != groups.end(); iter++)
+ {
+ if ((*iter)->getName() == name)
+ return *iter;
+ }
+ return nullptr;
+}
+/*
+void InstanceModel::setGroupFile ( QString filename )
+{
+ groupFile = filename;
+}*/
+
+int InstanceModel::numChildren() const
+{
+ return groups.count();
+}
+
+InstanceModelItem* InstanceModel::getChild ( int index ) const
+{
+ return groups[index];
+}
+
+QVariant InstanceModel::data ( int role ) const
+{
+ switch(role)
+ {
+ case Qt::DisplayRole:
+ return "name";
+ }
+ return QVariant();
+}
+
+
+InstanceGroup::InstanceGroup(const QString& name, InstanceModel *parent)
+{
+ this->name = name;
+ this->model = parent;
+ this->hidden = false;
+}
+
+InstanceGroup::~InstanceGroup()
+{
+ for(int i = 0; i < instances.size(); i++)
+ {
+ delete instances[i];
+ }
+}
+
+
+QString InstanceGroup::getName() const
+{
+ return name;
+}
+
+void InstanceGroup::setName(const QString& name)
+{
+ this->name = name;
+ //TODO: propagate change
+}
+
+InstanceModelItem* InstanceGroup::getParent() const
+{
+ return model;
+}
+
+bool InstanceGroup::isHidden() const
+{
+ return hidden;
+}
+
+void InstanceGroup::setHidden(bool hidden)
+{
+ this->hidden = hidden;
+ //TODO: propagate change
+}
+
+int InstanceGroup::getRow() const
+{
+ return model->getIndexOf( this);
+}
+
+InstanceModelItem* InstanceGroup::getChild ( int index ) const
+{
+ return instances[index];
+}
+
+int InstanceGroup::numChildren() const
+{
+ return instances.size();
+}
+
+QVariant InstanceGroup::data ( int role ) const
+{
+ switch(role)
+ {
+ case Qt::DisplayRole:
+ return name;
+ default:
+ return QVariant();
+ }
+}
View
137 data/instancemodel.h
@@ -0,0 +1,137 @@
+/* Copyright 2013 MultiMC Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INSTANCELIST_H
+#define INSTANCELIST_H
+
+#include <QList>
+#include <QMap>
+#include <QSet>
+#include <qabstractitemmodel.h>
+
+enum IMI_type
+{
+ IMI_Root,
+ IMI_Group,
+ IMI_Instance
+};
+
+class InstanceModel;
+class InstanceGroup;
+class InstanceBase;
+
+class InstanceModelItem
+{
+ public:
+ virtual IMI_type getModelItemType() const = 0;
+ virtual InstanceModelItem * getParent() const = 0;
+ virtual int numChildren() const = 0;
+ virtual InstanceModelItem * getChild(int index) const = 0;
+ virtual InstanceModel * getModel() const = 0;
+ virtual QVariant data(int role) const = 0;
+ virtual int getRow() const = 0;
+};
+
+class InstanceGroup : public InstanceModelItem
+{
+public:
+ InstanceGroup(const QString& name, InstanceModel * model);
+ ~InstanceGroup();
+
+ QString getName() const;
+ void setName(const QString& name);
+
+ bool isHidden() const;
+ void setHidden(bool hidden);
+
+ virtual IMI_type getModelItemType() const
+ {
+ return IMI_Group;
+ }
+ virtual InstanceModelItem* getParent() const;
+ virtual InstanceModelItem* getChild ( int index ) const;
+ virtual int numChildren() const;
+ virtual InstanceModel * getModel() const
+ {
+ return model;
+ };
+ virtual QVariant data ( int column ) const;
+ int getIndexOf(InstanceBase * inst)
+ {
+ return instances.indexOf(inst);
+ };
+ virtual int getRow() const;
+ void addInstance ( InstanceBase* inst );
+protected:
+ QString name;
+ InstanceModel * model;
+ QVector<InstanceBase*> instances;
+ bool hidden;
+ int row;
+};
+
+class InstanceModel : public QAbstractItemModel, public InstanceModelItem
+{
+public:
+ explicit InstanceModel(QObject *parent = 0);
+ ~InstanceModel();
+
+ virtual int columnCount ( const QModelIndex& parent = QModelIndex() ) const;
+ virtual QVariant data ( const QModelIndex& index, int role = Qt::DisplayRole ) const;
+ virtual QModelIndex index ( int row, int column, const QModelIndex& parent = QModelIndex() ) const;
+ virtual QModelIndex parent ( const QModelIndex& child ) const;
+ virtual int rowCount ( const QModelIndex& parent = QModelIndex() ) const;
+
+ void addInstance(InstanceBase *inst, const QString& groupName = "Ungrouped");
+ void setInstanceGroup(InstanceBase *inst, const QString & groupName);
+ InstanceGroup* getGroupByName(const QString & name) const;
+
+ void initialLoad(QString dir);
+ bool saveGroupInfo() const;
+
+ virtual IMI_type getModelItemType() const
+ {
+ return IMI_Root;
+ }
+ virtual InstanceModelItem * getParent() const
+ {
+ return nullptr;
+ };
+ virtual int numChildren() const;
+ virtual InstanceModelItem* getChild ( int index ) const;
+ virtual InstanceModel* getModel() const
+ {
+ return nullptr;
+ };
+ virtual QVariant data ( int column ) const;
+ int getIndexOf(const InstanceGroup * grp) const
+ {
+ return groups.indexOf((InstanceGroup *) grp);
+ };
+ virtual int getRow() const
+ {
+ return 0;
+ };
+signals:
+
+public slots:
+
+private:
+ QString groupFile;
+ QVector<InstanceGroup*> groups;
+ InstanceGroup * implicitGroup;
+};
+
+#endif // INSTANCELIST_H
View
5 gui/mainwindow.cpp
@@ -26,7 +26,8 @@ MainWindow::MainWindow(QWidget *parent) :
ui(new Ui::MainWindow)
{
ui->setupUi(this);
- instList.loadInstances("instances");
+ instList.initialLoad("instances");
+ ui->instanceView->setModel(&instList);
}
MainWindow::~MainWindow()
@@ -46,7 +47,7 @@ void MainWindow::on_actionViewInstanceFolder_triggered()
void MainWindow::on_actionRefresh_triggered()
{
- instList.loadInstances("instances");
+ instList.initialLoad("instances");
}
void MainWindow::on_actionViewCentralModsFolder_triggered()
View
4 gui/mainwindow.h
@@ -18,7 +18,7 @@
#include <QMainWindow>
-#include "../data/instancelist.h"
+#include "../data/instancemodel.h"
namespace Ui
{
@@ -55,7 +55,7 @@ private slots:
private:
Ui::MainWindow *ui;
- InstanceList instList;
+ InstanceModel instList;
};
#endif // MAINWINDOW_H
View
30 gui/mainwindow.ui
@@ -6,14 +6,38 @@
<rect>
<x>0</x>
<y>0</y>
- <width>600</width>
- <height>400</height>
+ <width>739</width>
+ <height>657</height>
</rect>
</property>
<property name="windowTitle">
<string>MultiMC 5</string>
</property>
- <widget class="QWidget" name="centralWidget"/>
+ <widget class="QWidget" name="centralWidget">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QTreeView" name="instanceView"/>
+ </item>
+ </layout>
+ </widget>
<widget class="QToolBar" name="mainToolBar">
<property name="movable">
<bool>false</bool>
View
333 hacks/boost/property_tree/detail/json_parser_read.hpp
@@ -0,0 +1,333 @@
+// ----------------------------------------------------------------------------
+// Copyright (C) 2002-2006 Marcin Kalicinski
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+// For more information, see www.boost.org
+// ----------------------------------------------------------------------------
+#ifndef BOOST_PROPERTY_TREE_DETAIL_JSON_PARSER_READ_HPP_INCLUDED
+#define BOOST_PROPERTY_TREE_DETAIL_JSON_PARSER_READ_HPP_INCLUDED
+
+//#define BOOST_SPIRIT_DEBUG
+
+#include <boost/property_tree/ptree.hpp>
+#include <boost/property_tree/detail/ptree_utils.hpp>
+#include <boost/property_tree/detail/json_parser_error.hpp>
+#include <boost/spirit/include/classic.hpp>
+#include <boost/limits.hpp>
+#include <string>
+#include <locale>
+#include <istream>
+#include <vector>
+#include <algorithm>
+
+namespace boost { namespace property_tree { namespace json_parser
+{
+
+ ///////////////////////////////////////////////////////////////////////
+ // Json parser context
+
+ template<class Ptree>
+ struct context
+ {
+
+ typedef typename Ptree::key_type::value_type Ch;
+ typedef std::basic_string<Ch> Str;
+ typedef typename std::vector<Ch>::iterator It;
+
+ Str string;
+ Str name;
+ Ptree root;
+ std::vector<Ptree *> stack;
+
+ struct a_object_s
+ {
+ context &c;
+ a_object_s(context &c): c(c) { }
+ void operator()(Ch) const
+ {
+ if (c.stack.empty())
+ c.stack.push_back(&c.root);
+ else
+ {
+ Ptree *parent = c.stack.back();
+ Ptree *child = &parent->push_back(std::make_pair(c.name, Ptree()))->second;
+ c.stack.push_back(child);
+ c.name.clear();
+ }
+ }
+ };
+
+ struct a_object_e
+ {
+ context &c;
+ a_object_e(context &c): c(c) { }
+ void operator()(Ch) const
+ {
+ BOOST_ASSERT(c.stack.size() >= 1);
+ c.stack.pop_back();
+ }
+ };
+
+ struct a_name
+ {
+ context &c;
+ a_name(context &c): c(c) { }
+ void operator()(It, It) const
+ {
+ c.name.swap(c.string);
+ c.string.clear();
+ }
+ };
+
+ struct a_string_val
+ {
+ context &c;
+ a_string_val(context &c): c(c) { }
+ void operator()(It, It) const
+ {
+ BOOST_ASSERT(c.stack.size() >= 1);
+ c.stack.back()->push_back(std::make_pair(c.name, Ptree(c.string)));
+ c.name.clear();
+ c.string.clear();
+ }
+ };
+
+ struct a_literal_val
+ {
+ context &c;
+ a_literal_val(context &c): c(c) { }
+ void operator()(It b, It e) const
+ {
+ BOOST_ASSERT(c.stack.size() >= 1);
+ c.stack.back()->push_back(std::make_pair(c.name,
+ Ptree(Str(b, e))));
+ c.name.clear();
+ c.string.clear();
+ }
+ };
+
+ struct a_char
+ {
+ context &c;
+ a_char(context &c): c(c) { }
+ void operator()(It b, It e) const
+ {
+ c.string += *b;
+ }
+ };
+
+ struct a_escape
+ {
+ context &c;
+ a_escape(context &c): c(c) { }
+ void operator()(Ch ch) const
+ {
+ switch (ch)
+ {
+ case Ch('\"'): c.string += Ch('\"'); break;
+ case Ch('\\'): c.string += Ch('\\'); break;
+ case Ch('/'): c.string += Ch('/'); break;
+ case Ch('b'): c.string += Ch('\b'); break;
+ case Ch('f'): c.string += Ch('\f'); break;
+ case Ch('n'): c.string += Ch('\n'); break;
+ case Ch('r'): c.string += Ch('\r'); break;
+ case Ch('t'): c.string += Ch('\t'); break;
+ default: BOOST_ASSERT(0);
+ }
+ }
+ };
+
+ struct a_unicode
+ {
+ context &c;
+ a_unicode(context &c): c(c) { }
+ void operator()(unsigned long u) const
+ {
+ //u = (std::min)(u, static_cast<unsigned long>((std::numeric_limits<Ch>::max)()));
+ c.string += Ch(u);
+ }
+ };
+
+ };
+
+ ///////////////////////////////////////////////////////////////////////
+ // Json grammar
+
+ template<class Ptree>
+ struct json_grammar :
+ public boost::spirit::classic::grammar<json_grammar<Ptree> >
+ {
+
+ typedef context<Ptree> Context;
+ typedef typename Ptree::key_type::value_type Ch;
+
+ mutable Context c;
+
+ template<class Scanner>
+ struct definition
+ {
+
+ boost::spirit::classic::rule<Scanner>
+ root, object, member, array, item, value, string, number;
+ boost::spirit::classic::rule<
+ typename boost::spirit::classic::lexeme_scanner<Scanner>::type>
+ character, escape;
+
+ definition(const json_grammar &self)
+ {
+
+ using namespace boost::spirit::classic;
+ // There's a boost::assertion too, so another explicit using
+ // here:
+ using boost::spirit::classic::assertion;
+
+ // Assertions
+ assertion<std::string> expect_root("expected object or array");
+ assertion<std::string> expect_eoi("expected end of input");
+ assertion<std::string> expect_objclose("expected ',' or '}'");
+ assertion<std::string> expect_arrclose("expected ',' or ']'");
+ assertion<std::string> expect_name("expected object name");
+ assertion<std::string> expect_colon("expected ':'");
+ assertion<std::string> expect_value("expected value");
+ assertion<std::string> expect_escape("invalid escape sequence");
+
+ // JSON grammar rules
+ root
+ = expect_root(object | array)
+ >> expect_eoi(end_p)
+ ;
+
+ object
+ = ch_p('{')[typename Context::a_object_s(self.c)]
+ >> (ch_p('}')[typename Context::a_object_e(self.c)]
+ | (list_p(member, ch_p(','))
+ >> expect_objclose(ch_p('}')[typename Context::a_object_e(self.c)])
+ )
+ )
+ ;
+
+ member
+ = expect_name(string[typename Context::a_name(self.c)])
+ >> expect_colon(ch_p(':'))
+ >> expect_value(value)
+ ;
+
+ array
+ = ch_p('[')[typename Context::a_object_s(self.c)]
+ >> (ch_p(']')[typename Context::a_object_e(self.c)]
+ | (list_p(item, ch_p(','))
+ >> expect_arrclose(ch_p(']')[typename Context::a_object_e(self.c)])
+ )
+ )
+ ;
+
+ item
+ = expect_value(value)
+ ;
+
+ value
+ = string[typename Context::a_string_val(self.c)]
+ | (number | str_p("true") | "false" | "null")[typename Context::a_literal_val(self.c)]
+ | object
+ | array
+ ;
+
+ number
+ = !ch_p("-") >>
+ (ch_p("0") | (range_p(Ch('1'), Ch('9')) >> *digit_p)) >>
+ !(ch_p(".") >> +digit_p) >>
+ !(chset_p(detail::widen<Ch>("eE").c_str()) >>
+ !chset_p(detail::widen<Ch>("-+").c_str()) >>
+ +digit_p)
+ ;
+
+ string
+ = +(lexeme_d[confix_p('\"', *character, '\"')])
+ ;
+
+ character
+ = (anychar_p - "\\" - "\"")
+ [typename Context::a_char(self.c)]
+ | ch_p("\\") >> expect_escape(escape)
+ ;
+
+ escape
+ = chset_p(detail::widen<Ch>("\"\\/bfnrt").c_str())
+ [typename Context::a_escape(self.c)]
+ | 'u' >> uint_parser<unsigned long, 16, 4, 4>()
+ [typename Context::a_unicode(self.c)]
+ ;
+
+ // Debug
+ BOOST_SPIRIT_DEBUG_RULE(root);
+ BOOST_SPIRIT_DEBUG_RULE(object);
+ BOOST_SPIRIT_DEBUG_RULE(member);
+ BOOST_SPIRIT_DEBUG_RULE(array);
+ BOOST_SPIRIT_DEBUG_RULE(item);
+ BOOST_SPIRIT_DEBUG_RULE(value);
+ BOOST_SPIRIT_DEBUG_RULE(string);
+ BOOST_SPIRIT_DEBUG_RULE(number);
+ BOOST_SPIRIT_DEBUG_RULE(escape);
+ BOOST_SPIRIT_DEBUG_RULE(character);
+
+ }
+
+ const boost::spirit::classic::rule<Scanner> &start() const
+ {
+ return root;
+ }
+
+ };
+
+ };
+
+ template<class It, class Ch>
+ unsigned long count_lines(It begin, It end)
+ {
+ return static_cast<unsigned long>(std::count(begin, end, Ch('\n')) + 1);
+ }
+
+ template<class Ptree>
+ void read_json_internal(std::basic_istream<typename Ptree::key_type::value_type> &stream,
+ Ptree &pt,
+ const std::string &filename)
+ {
+
+ using namespace boost::spirit::classic;
+ typedef typename Ptree::key_type::value_type Ch;
+ typedef typename std::vector<Ch>::iterator It;
+
+ // Load data into vector
+ std::vector<Ch> v(std::istreambuf_iterator<Ch>(stream.rdbuf()),
+ std::istreambuf_iterator<Ch>());
+ if (!stream.good())
+ BOOST_PROPERTY_TREE_THROW(json_parser_error("read error", filename, 0));
+
+ // Prepare grammar
+ json_grammar<Ptree> g;
+
+ // Parse
+ try
+ {
+ parse_info<It> pi = parse(v.begin(), v.end(), g,
+ space_p | comment_p("//") | comment_p("/*", "*/"));
+ if (!pi.hit || !pi.full)
+ BOOST_PROPERTY_TREE_THROW((parser_error<std::string, It>(v.begin(), "syntax error")));
+ }
+ catch (parser_error<std::string, It> &e)
+ {
+ BOOST_PROPERTY_TREE_THROW(json_parser_error(e.descriptor, filename, count_lines<It, Ch>(v.begin(), e.where)));
+ }
+
+ // Swap grammar context root and pt
+ pt.swap(g.c.root);
+
+ }
+
+} } }
+
+#endif
+

0 comments on commit 00893b3

Please sign in to comment.
Something went wrong with that request. Please try again.