1 // This may look like C code, but it's really -*- C++ -*-
2 /*
3  * Copyright (C) 2008 Emweb bv, Herent, Belgium.
4  *
5  * See the LICENSE file for terms of use.
6  */
7 #ifndef GIT_MODEL_H_
8 #define GIT_MODEL_H_
9 
10 #include <Wt/WAbstractItemModel.h>
11 
12 #include "Git.h"
13 
14 using namespace Wt;
15 
16 /**
17  * @addtogroup gitmodelexample
18  */
19 /*@{*/
20 
21 /*! \class GitModel
22  *  \brief A model that retrieves revision trees from a git repository.
23  *
24  * In its present form, it presents only a single column of data: the
25  * file names. Additional data could be easily added. Git "tree" objects
26  * correspond to folders, and "blob" objects to files.
27  *
28  * The model is read-only, does not support sorting (that could be
29  * provided by using a WSortFilterProxyModel).
30  *
31  * The model loads only minimal information in memory: to create model indexes
32  * for folders. These cannot be uniquely identified by their SHA1 id, since
33  * two identical folders at different locations would have the same SHA1 id.
34  *
35  * The internal id of model indexes created by the model uniquely identify
36  * a containing folder for a particular file.
37  */
38 class GitModel : public Wt::WAbstractItemModel
39 {
40 public:
41   /*! \brief The role which may be used on a file to retrieve its contents.
42    */
43   static const ItemDataRole ContentsRole;
44   static const ItemDataRole FilePathRole;
45 
46   /*! \brief Constructor.
47    */
48   GitModel();
49 
50   /*! \brief Set the repository and load its 'master' revision.
51    */
52   void setRepositoryPath(const std::string& repositoryPath);
53 
54   /*! \brief Load a particular revision.
55    *
56    * The revision name may be any revision accepted by git, by
57    * git-rev-parse(1).
58    */
59   void loadRevision(const std::string& revName);
60 
61   /*! \brief Returns the parent index.
62    *
63    * Consults the internal data structure to find the parent index.
64    */
65   virtual WModelIndex parent(const WModelIndex& index) const;
66 
67   /*! \brief Returns the column count.
68    *
69    * Returns 1.
70    */
71   virtual int columnCount(const WModelIndex& parent = WModelIndex())
72     const;
73 
74   /*! \brief Returns the row count.
75    *
76    * Returns 0 unless the item represents a folder, in which case it returns
77    * the number of items in the tree object that corresponds to the folder.
78    */
79   virtual int rowCount(const WModelIndex& parent = WModelIndex()) const;
80 
81   /*! \brief Returns a child index.
82    *
83    * Consults the internal data structure to create a child index. If
84    * necessary, the internal data structure is expanded by adding an
85    * entry for using the <i>parent</i> index as a parent index.
86    */
87   virtual WModelIndex
88   index(int row, int column, const WModelIndex& parent = WModelIndex())
89     const;
90 
91   /*! \brief Returns data.
92    *
93    * Returns only data corresponding to DisplayRole and ContentsRole.
94    */
95   virtual cpp17::any
96   data(const WModelIndex& index, ItemDataRole role = ItemDataRole::Display) const;
97 
98   /*! \brief Returns header data.
99    */
100   virtual cpp17::any
101   headerData(int section, Orientation orientation = Orientation::Horizontal,
102              ItemDataRole role = ItemDataRole::Display) const;
103 
104   using WAbstractItemModel::data;
105 
106 private:
107   /*! \brief The git repository. */
108   Git git_;
109 
110   /*! \class ChildIndex
111    *  \brief Index usable as a key to a map, that identifies a child/row
112    *         within a tree.
113    */
114   struct ChildIndex {
115     int parentId;
116     int index;
117 
ChildIndexChildIndex118     ChildIndex(int aParent, int anIndex)
119       : parentId(aParent), index(anIndex) { }
120 
121     bool operator< (const ChildIndex& other) const {
122       if (parentId < other.parentId)
123 	return true;
124       else if (parentId > other.parentId)
125 	return false;
126       else return index < other.index;
127     }
128   };
129 
130   /*! \class Tree
131    *  \brief Used to uniquely locate a folder within the folder hierarchy.
132    */
133   class Tree {
134   public:
135     /*! \brief Constructor.
136      */
Tree(int parentId,int index,const Git::ObjectId & object,int rowCount)137     Tree(int parentId, int index, const Git::ObjectId& object,
138 	 int rowCount)
139       : index_(parentId, index),
140 	treeObject_(object),
141 	rowCount_(rowCount)
142     { }
143 
144     /*! \brief Returns the parent id.
145      *
146      * Index of the parent folder within the treeData_ vector.
147      */
parentId()148     int parentId() const { return index_.parentId; }
149 
150     /*! \brief Returns the child index within the parent folder.
151      *
152      * Index of this folder within the file list of the parent folder.
153      */
index()154     int index() const { return index_.index; }
155 
156     /*! \brief Returns the SHA1 id for the git tree object.
157      */
treeObject()158     const Git::ObjectId& treeObject() const { return treeObject_; }
159 
160     /*! \brief Returns the (cached) row count.
161      */
rowCount()162     int rowCount() const { return rowCount_; }
163 
164   private:
165     ChildIndex    index_;
166     Git::ObjectId treeObject_;
167     int           rowCount_;
168   };
169 
170   typedef std::map<ChildIndex, int> ChildPointerMap;
171 
172   /*! \brief List of folder objects.
173    *
174    * This list contains folders for which a model index has been allocated.
175    *
176    * Model indexes have an internal id that are indexes into this vector,
177    * identifying the folder that contains a particular file.
178    *
179    * Note: only for folders this is needed, since files will never be used
180    * as a 'parent' index.
181    *
182    * It is populated on-the-fly, as the user navigates the model.
183    */
184   mutable std::vector<Tree> treeData_;
185 
186   /*! \brief Maps child indexes to tree indexes.
187    *
188    * This map provides a way to lookup data in treeData_. It has an entry
189    * corresponding to every entry in treeData_: it maps child indexes for
190    * folders to indexes in the treeData_ vector.
191    *
192    * It is populated on-the-fly, as the user navigates the model.
193    */
194   mutable ChildPointerMap   childPointer_;
195 
196   /*! \brief Get or allocate an id for a folder.
197    *
198    * The folder is identified by a given childIndex within a parent
199    * folder. This method adds data to the treeData_ (and
200    * childPointer_) data structures.
201    */
202   int getTreeId(int parentId, int childIndex) const;
203 
204   /*! \brief Get the Git::Object that corresponds to an index.
205    */
206   Git::Object getObject(const WModelIndex& index) const;
207 };
208 
209 /*@}*/
210 
211 #endif // GIT_MODEL_H_
212