Tulip  5.7.4
Large graphs analysis and drawing
CSVGraphImport.h
1 /*
2  *
3  * This file is part of Tulip (https://tulip.labri.fr)
4  *
5  * Authors: David Auber and the Tulip development Team
6  * from LaBRI, University of Bordeaux
7  *
8  * Tulip is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation, either version 3
11  * of the License, or (at your option) any later version.
12  *
13  * Tulip is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16  * See the GNU General Public License for more details.
17  *
18  */
19 ///@cond DOXYGEN_HIDDEN
20 
21 #ifndef CSVGRAPHIMPORT_H
22 #define CSVGRAPHIMPORT_H
23 
24 #include <unordered_map>
25 
26 #include <tulip/CSVContentHandler.h>
27 #include <tulip/Graph.h>
28 #include <tulip/tulipconf.h>
29 
30 #include <QMessageBox>
31 
32 namespace tlp {
33 class PropertyInterface;
34 
35 #define DEF_VALUE_SEPARATOR ','
36 
37 /**
38  * @brief Store import parameters for a CSV file column.
39  *
40  * Contains all the parameters defined by user for a given CSV column (the name of the column, its
41  *data type and if user want to import it).
42  **/
43 class TLP_QT_SCOPE CSVColumn {
44 public:
45  CSVColumn(const std::string &columnName = "", const std::string &columnType = "")
46  : _used(true), _name(columnName), _type(columnType), _valueSeparator(DEF_VALUE_SEPARATOR) {}
47 
48  /**
49  * @brief Get the name of the column.
50  **/
51  const std::string &name() const {
52  return _name;
53  }
54 
55  /**
56  * @brief Tells if the property marked for import.
57  **/
58  bool isUsed() const {
59  return _used;
60  }
61 
62  /**
63  * @brief Tells if the property has been updated by user.
64  **/
65  bool isDefault() const {
66  return (_name == _def_name) && (_type == _def_type) &&
67  (_valueSeparator == DEF_VALUE_SEPARATOR) && _exceptions.empty();
68  }
69 
70  /**
71  * @brief Return the property data type.
72  **/
73  const std::string &dataType() const {
74  return _type;
75  }
76 
77  bool needMultiValues() const {
78  return _valueSeparator != 0;
79  }
80 
81  char getMultiValueSeparator() const {
82  return _valueSeparator;
83  }
84 
85  // possible actions
86  // the two first ones indicate an exception
87  enum Action { ASSIGN_NO_VALUE = 0, SKIP_ROW = 1, ASSIGN_VALUE = 2 };
88 
89  struct Exception {
90  std::string value;
91  Action action;
92  Exception(const std::string &v, Action a) : value(v), action(a) {}
93  };
94 
95  void addException(const std::string &value, Action action) {
96  _exceptions.emplace_back(value, action);
97  }
98 
99  void clearExceptions() {
100  _exceptions.clear();
101  }
102 
103  // look for a specific exception defined for token
104  Action getActionForToken(const std::string &token) {
105  for (const Exception &exception : _exceptions) {
106  if (exception.value == token)
107  return exception.action;
108  }
109  return Action::ASSIGN_VALUE;
110  }
111 
112 protected:
113  bool _used;
114  std::string _name, _def_name;
115  std::string _type, _def_type;
116  char _valueSeparator;
117  std::vector<Exception> _exceptions;
118 };
119 /**
120  * @brief Store all the advanced import parameters for the CSV file.
121  *
122  * Store the information about columns and rows to import.
123  * Use this object to configure the import process of a CSVImportGraph object.
124  **/
125 class TLP_QT_SCOPE CSVImportParameters {
126 public:
127  CSVImportParameters(unsigned int fromLine = 0, unsigned int toLine = UINT_MAX,
128  const std::vector<CSVColumn *> &columns = std::vector<CSVColumn *>());
129  virtual ~CSVImportParameters();
130 
131  /**
132  * @brief Return the number of column.
133  **/
134  unsigned int columnNumber() const;
135 
136  /**
137  * @brief return true if the column is marked for import
138  **/
139  bool importColumn(unsigned int column) const;
140  /**
141  * @brief Get the column name
142  **/
143  std::string getColumnName(unsigned int column) const;
144  /**
145  * @brief Get the column data type
146  **/
147  std::string getColumnDataType(unsigned int column) const;
148 
149  /**
150  * @brief Get the column separator for multiple values
151  **/
152  char getColumnMultiValueSeparator(unsigned int column) const;
153 
154  /**
155  * @brief Get the column action according to the given token
156  **/
157  CSVColumn::Action getColumnActionForToken(unsigned int column, const std::string &token) const;
158 
159  /**
160  * @brief Return the index of the first line to import
161  **/
162  unsigned int getFirstLineIndex() const;
163  /**
164  * @brief Return the index of the last line to import
165  **/
166  unsigned int getLastLineIndex() const;
167  /**
168  * @brief Return true if the given row is between the first row to import and the last row to
169  *import
170  **/
171  bool importRow(unsigned int row) const;
172 
173 private:
174  unsigned int fromLine;
175  unsigned int toLine;
176  std::vector<CSVColumn *> columns;
177 };
178 
179 /**
180  * @brief Interface to map CSV rows to graph elements.
181  *
182  * To build the mapping user had to parse the CSV file.
183  * @code
184  * CSVParser *parser;
185  * CSVToGraphDataMapping *mapping;
186  * parser->parse(mapping);
187  * //Now the mapping has been built.
188  * //Get the element for the first row.
189  * pair<tlp::ElementType,unsigned int> element = mapping->getElementForRow(0);
190  * @endcode
191  **/
192 class TLP_QT_SCOPE CSVToGraphDataMapping {
193 public:
194  virtual ~CSVToGraphDataMapping() {}
195  virtual std::pair<tlp::ElementType, std::vector<unsigned int>>
196  getElementsForRow(const std::vector<std::vector<std::string>> &tokens) = 0;
197  virtual void init(unsigned int rowNumber) = 0;
198 };
199 
200 /**
201  * @brief Abstract class handling node or edge mapping between a CSV column and a graph property.
202  *
203  * Be sure there is a property with the given name in the graph or an error will occur.
204  * Automatically handle CSV file parsing just implements the buildIndexForRow function to fill the
205  *rowToGraphId map with the right graph element.
206  **/
207 class TLP_QT_SCOPE AbstractCSVToGraphDataMapping : public CSVToGraphDataMapping {
208 public:
209  AbstractCSVToGraphDataMapping(tlp::Graph *graph, tlp::ElementType type,
210  const std::vector<unsigned int> &columnIds,
211  const std::vector<std::string> &propertyNames);
212  ~AbstractCSVToGraphDataMapping() override {}
213 
214  void init(unsigned int rowNumber) override;
215  std::pair<tlp::ElementType, std::vector<unsigned int>>
216  getElementsForRow(const std::vector<std::vector<std::string>> &tokens) override;
217 
218 protected:
219  /**
220  * @brief Create a new element if no elements for the given row was found.
221  * @return Return the graph element id or UINT_MAX if no new element is created.
222  **/
223  virtual unsigned int buildIndexForRow(unsigned int row, const std::vector<std::string> &keys) = 0;
224 
225 protected:
226  std::unordered_map<std::string, unsigned int> valueToId;
227  tlp::Graph *graph;
228  tlp::ElementType type;
229  std::vector<unsigned int> columnIds;
230  std::vector<tlp::PropertyInterface *> keyProperties;
231 };
232 /**
233  * @brief Map each row of the CSV file on a new node.
234  **/
235 class TLP_QT_SCOPE CSVToNewNodeIdMapping : public CSVToGraphDataMapping {
236 public:
237  CSVToNewNodeIdMapping(tlp::Graph *graph);
238  void init(unsigned int rowNumber) override;
239  std::pair<tlp::ElementType, std::vector<unsigned int>>
240  getElementsForRow(const std::vector<std::vector<std::string>> &tokens) override;
241 
242 private:
243  tlp::Graph *graph;
244 };
245 
246 /**
247  * @brief Try to map CSV file rows to nodes according to value between a CSV column and a graph
248  *property.
249  *
250  * Be sure there is a property with the given name in the graph before using it.
251  **/
252 class TLP_QT_SCOPE CSVToGraphNodeIdMapping : public AbstractCSVToGraphDataMapping {
253 public:
254  /**
255  * @param graph The graph where the nodes will be searched.
256  * @param columnIndex The index of the column with the ids in the CSV file.
257  * @param propertyName The name of the property to search ids.
258  * @param firstRow The first row to search ids.
259  * @param lastRow The last row to search ids.
260  * @param createNode If set to true if there is no node for an id in the CSV file a new node will
261  *be created for this id.
262  **/
263  CSVToGraphNodeIdMapping(tlp::Graph *graph, const std::vector<unsigned int> &columnIds,
264  const std::vector<std::string> &propertyNames, bool createNode = false);
265  void init(unsigned int rowNumber) override;
266 
267 protected:
268  unsigned int buildIndexForRow(unsigned int row, const std::vector<std::string> &keys) override;
269 
270 private:
271  bool createMissingNodes;
272 };
273 /**
274  * @brief Try to map CSV file rows to edges according to value between a CSV column and a graph
275  *property.
276  *
277  * Be sure there is a property with the given name in the graph before using it.
278  **/
279 class TLP_QT_SCOPE CSVToGraphEdgeIdMapping : public AbstractCSVToGraphDataMapping {
280 public:
281  /**
282  * @param graph The graph where the edges will be searched.
283  * @param columnIndex The index of the column with the ids in the CSV file.
284  * @param propertyName The name of the property to search ids.
285  * @param firstRow The first row to search ids.
286  * @param lastRow The last row to search ids.
287  **/
288  CSVToGraphEdgeIdMapping(tlp::Graph *graph, const std::vector<unsigned int> &columnIds,
289  const std::vector<std::string> &propertyNames);
290 
291 protected:
292  unsigned int buildIndexForRow(unsigned int row, const std::vector<std::string> &keys) override;
293 };
294 
295 /**
296  * @brief Try to map CSV file rows to edges according to edge source and destination.
297  *
298  * For each row in the CSV file create an edge in the graph between source and destination nodes.
299  *Find source node by comparing id in the source CSV column and destination node by comparing id in
300  *the destination CSV column.
301  **/
302 class TLP_QT_SCOPE CSVToGraphEdgeSrcTgtMapping : public CSVToGraphDataMapping {
303 public:
304  /**
305  * @param graph The graph where the edges will be searched.
306  * @param srcColumnIndex The index of the column with the source node id in the CSV file.
307  * @param tgtColumnIndex The index of the column with the taret node id in the CSV file.
308  * @param srcPropertyName The name of the property to search source node id.
309  * @param tgtPropertyName The name of the property to search target node id.
310  * @param firstRow The first row to search ids.
311  * @param lastRow The last row to search ids.
312  * @param createMissinElements If true create source node, destination node if one of them is not
313  *found in the graph.
314  **/
315  CSVToGraphEdgeSrcTgtMapping(tlp::Graph *graph, const std::vector<unsigned int> &srcColumnIds,
316  const std::vector<unsigned int> &tgtColumnIds,
317  const std::vector<std::string> &srcPropNames,
318  const std::vector<std::string> &tgtPropNames,
319  bool createMissinElements = false);
320  std::pair<tlp::ElementType, unsigned int> getElementForRow(unsigned int row);
321  void init(unsigned int lineNumbers) override;
322  std::pair<tlp::ElementType, std::vector<unsigned int>>
323  getElementsForRow(const std::vector<std::vector<std::string>> &tokens) override;
324 
325 private:
326  tlp::Graph *graph;
327  std::unordered_map<std::string, unsigned int> srcValueToId;
328  std::unordered_map<std::string, unsigned int> tgtValueToId;
329  std::vector<unsigned int> srcColumnIds;
330  std::vector<unsigned int> tgtColumnIds;
331  std::vector<tlp::PropertyInterface *> srcProperties;
332  std::vector<tlp::PropertyInterface *> tgtProperties;
333  bool sameSrcTgtProperties;
334  bool buildMissingElements;
335 };
336 
337 /**
338  * @brief Interface to perform mapping between CSV columns and graph properties during the CSV
339  *import process.
340  *
341  **/
342 class TLP_QT_SCOPE CSVImportColumnToGraphPropertyMapping {
343 public:
344  virtual ~CSVImportColumnToGraphPropertyMapping() {}
345  /**
346  * @brief Return the property corresponding to the column index.
347  * @param column The index of the column.
348  * @param token The current token. May be needed to determine column data type.
349  *
350  * The token parameter is used to guess property type if needed.
351  **/
352  virtual tlp::PropertyInterface *getPropertyInterface(unsigned int column,
353  const std::string &token) = 0;
354 };
355 
356 /**
357  * @brief Proxy to handle all the properties operations like access, creation, data type detection
358  *during the CSV parsing process.
359  *
360  * Try to guess the type of the property in function of the first token
361  * if user don't tell which type the property is.
362  **/
363 class TLP_QT_SCOPE CSVImportColumnToGraphPropertyMappingProxy
364  : public CSVImportColumnToGraphPropertyMapping {
365 public:
366  CSVImportColumnToGraphPropertyMappingProxy(tlp::Graph *graph,
367  const CSVImportParameters &importParameters,
368  QWidget *parent = nullptr);
369  ~CSVImportColumnToGraphPropertyMappingProxy() override {}
370  tlp::PropertyInterface *getPropertyInterface(unsigned int column,
371  const std::string &token) override;
372 
373 private:
374  tlp::Graph *graph;
375  CSVImportParameters importParameters;
376  std::unordered_map<unsigned int, tlp::PropertyInterface *> propertiesBuffer;
377  QMessageBox::StandardButton overwritePropertiesButton;
378  QWidget *parent;
379  PropertyInterface *generateApproximateProperty(const std::string &name, const std::string &type);
380 };
381 
382 /**
383  * @brief Manage all the CSV import process. Use the mapping object to find the graph element in
384  *function of the row and the propertiesManager to find the property corresponding to the column.
385  * The import parameters are used to filter the rows and the columns to import.
386  **/
387 class TLP_QT_SCOPE CSVGraphImport : public tlp::CSVContentHandler {
388 public:
389  CSVGraphImport(CSVToGraphDataMapping *mapping,
390  CSVImportColumnToGraphPropertyMapping *propertiesManager,
391  const CSVImportParameters &importParameters);
392  ~CSVGraphImport() override;
393  bool begin() override;
394  bool line(unsigned int row, const std::vector<CSVToken> &lineTokens) override;
395  bool end(unsigned int rowNumber, unsigned int columnNumber) override;
396 
397 protected:
398  CSVToGraphDataMapping *mapping;
399  CSVImportColumnToGraphPropertyMapping *propertiesManager;
400  CSVImportParameters importParameters;
401 };
402 } // namespace tlp
403 #endif // CSVGRAPHIMPORT_H
404 ///@endcond
PropertyInterface describes the interface of a graph property.
ElementType
Definition: Graph.h:50