1 /***************************************************************************
2 qgsrelationmanager.cpp
3 --------------------------------------
4 Date : 1.3.2013
5 Copyright : (C) 2013 Matthias Kuhn
6 Email : matthias at opengis dot ch
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16 #include "qgsrelationmanager.h"
17
18 #include "qgsapplication.h"
19 #include "qgslogger.h"
20 #include "qgsproject.h"
21 #include "qgsvectordataprovider.h"
22 #include "qgsvectorlayer.h"
23
QgsRelationManager(QgsProject * project)24 QgsRelationManager::QgsRelationManager( QgsProject *project )
25 : QObject( project )
26 , mProject( project )
27 {
28 if ( project )
29 {
30 // TODO: QGIS 4 remove: relations are now stored with the layer style
31 connect( project, &QgsProject::readProjectWithContext, this, &QgsRelationManager::readProject );
32 // TODO: QGIS 4 remove: relations are now stored with the layer style
33 connect( project, &QgsProject::writeProject, this, &QgsRelationManager::writeProject );
34
35 connect( project, &QgsProject::layersRemoved, this, &QgsRelationManager::layersRemoved );
36 }
37 }
38
context() const39 QgsRelationContext QgsRelationManager::context() const
40 {
41 return QgsRelationContext( mProject );
42 }
43
setRelations(const QList<QgsRelation> & relations)44 void QgsRelationManager::setRelations( const QList<QgsRelation> &relations )
45 {
46 mRelations.clear();
47 for ( const QgsRelation &rel : std::as_const( relations ) )
48 {
49 addRelation( rel );
50 }
51 emit changed();
52 }
53
relations() const54 QMap<QString, QgsRelation> QgsRelationManager::relations() const
55 {
56 return mRelations;
57 }
58
addRelation(const QgsRelation & relation)59 void QgsRelationManager::addRelation( const QgsRelation &relation )
60 {
61 // Do not add relations to layers that do not exist
62 if ( !( relation.referencingLayer() && relation.referencedLayer() ) )
63 return;
64
65 mRelations.insert( relation.id(), relation );
66 if ( mProject )
67 {
68 mProject->setDirty( true );
69 }
70 emit changed();
71 }
72
73
updateRelationsStatus()74 void QgsRelationManager::updateRelationsStatus()
75 {
76 for ( auto relation : mRelations )
77 {
78 relation.updateRelationStatus();
79 }
80 }
81
82
removeRelation(const QString & id)83 void QgsRelationManager::removeRelation( const QString &id )
84 {
85 mRelations.remove( id );
86 emit changed();
87 }
88
removeRelation(const QgsRelation & relation)89 void QgsRelationManager::removeRelation( const QgsRelation &relation )
90 {
91 mRelations.remove( relation.id() );
92 emit changed();
93 }
94
relation(const QString & id) const95 QgsRelation QgsRelationManager::relation( const QString &id ) const
96 {
97 return mRelations.value( id );
98 }
99
relationsByName(const QString & name) const100 QList<QgsRelation> QgsRelationManager::relationsByName( const QString &name ) const
101 {
102 QList<QgsRelation> relations;
103
104 for ( const QgsRelation &rel : std::as_const( mRelations ) )
105 {
106 if ( QString::compare( rel.name(), name, Qt::CaseInsensitive ) == 0 )
107 relations << rel;
108 }
109
110 return relations;
111 }
112
clear()113 void QgsRelationManager::clear()
114 {
115 mRelations.clear();
116 emit changed();
117 }
118
referencingRelations(const QgsVectorLayer * layer,int fieldIdx) const119 QList<QgsRelation> QgsRelationManager::referencingRelations( const QgsVectorLayer *layer, int fieldIdx ) const
120 {
121 if ( !layer )
122 {
123 return mRelations.values();
124 }
125
126 QList<QgsRelation> relations;
127
128 for ( const QgsRelation &rel : std::as_const( mRelations ) )
129 {
130 if ( rel.referencingLayer() == layer )
131 {
132 if ( fieldIdx != -2 )
133 {
134 bool containsField = false;
135 const auto constFieldPairs = rel.fieldPairs();
136 for ( const QgsRelation::FieldPair &fp : constFieldPairs )
137 {
138 if ( fieldIdx == layer->fields().lookupField( fp.referencingField() ) )
139 {
140 containsField = true;
141 break;
142 }
143 }
144
145 if ( !containsField )
146 {
147 continue;
148 }
149 }
150 relations.append( rel );
151 }
152 }
153
154 return relations;
155 }
156
referencedRelations(const QgsVectorLayer * layer) const157 QList<QgsRelation> QgsRelationManager::referencedRelations( const QgsVectorLayer *layer ) const
158 {
159 if ( !layer )
160 {
161 return mRelations.values();
162 }
163
164 QList<QgsRelation> relations;
165
166 for ( const QgsRelation &rel : std::as_const( mRelations ) )
167 {
168 if ( rel.referencedLayer() == layer )
169 {
170 relations.append( rel );
171 }
172 }
173
174 return relations;
175 }
176
readProject(const QDomDocument & doc,QgsReadWriteContext & context)177 void QgsRelationManager::readProject( const QDomDocument &doc, QgsReadWriteContext &context )
178 {
179 mRelations.clear();
180 mPolymorphicRelations.clear();
181
182 QDomNodeList relationNodes = doc.elementsByTagName( QStringLiteral( "relations" ) );
183 if ( relationNodes.count() )
184 {
185 QgsRelationContext relcontext( mProject );
186
187 QDomNode node = relationNodes.item( 0 );
188 QDomNodeList relationNodes = node.childNodes();
189 int relCount = relationNodes.count();
190 for ( int i = 0; i < relCount; ++i )
191 {
192 addRelation( QgsRelation::createFromXml( relationNodes.at( i ), context, relcontext ) );
193 }
194 }
195 else
196 {
197 QgsDebugMsg( QStringLiteral( "No relations data present in this document" ) );
198 }
199
200 QDomNodeList polymorphicRelationNodes = doc.elementsByTagName( QStringLiteral( "polymorphicRelations" ) );
201 if ( polymorphicRelationNodes.count() )
202 {
203 QgsRelationContext relcontext( mProject );
204
205 QDomNode node = polymorphicRelationNodes.item( 0 );
206 QDomNodeList relationNodes = node.childNodes();
207 int relCount = relationNodes.count();
208 for ( int i = 0; i < relCount; ++i )
209 {
210 addPolymorphicRelation( QgsPolymorphicRelation::createFromXml( relationNodes.at( i ), context, relcontext ) );
211 }
212 }
213 else
214 {
215 QgsDebugMsgLevel( QStringLiteral( "No polymorphic relations data present in this document" ), 3 );
216 }
217
218 emit relationsLoaded();
219 emit changed();
220 }
221
writeProject(QDomDocument & doc)222 void QgsRelationManager::writeProject( QDomDocument &doc )
223 {
224 QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
225 if ( !nl.count() )
226 {
227 QgsDebugMsg( QStringLiteral( "Unable to find qgis element in project file" ) );
228 return;
229 }
230 QDomNode qgisNode = nl.item( 0 ); // there should only be one
231
232 QDomElement relationsNode = doc.createElement( QStringLiteral( "relations" ) );
233 qgisNode.appendChild( relationsNode );
234
235 for ( const QgsRelation &relation : std::as_const( mRelations ) )
236 {
237 // the generated relations for polymorphic relations should be ignored,
238 // they are generated every time when a polymorphic relation is added
239 if ( relation.type() == QgsRelation::Generated )
240 continue;
241
242 relation.writeXml( relationsNode, doc );
243 }
244
245 QDomElement polymorphicRelationsNode = doc.createElement( QStringLiteral( "polymorphicRelations" ) );
246 qgisNode.appendChild( polymorphicRelationsNode );
247
248 for ( const QgsPolymorphicRelation &relation : std::as_const( mPolymorphicRelations ) )
249 {
250 relation.writeXml( polymorphicRelationsNode, doc );
251 }
252 }
253
layersRemoved(const QStringList & layers)254 void QgsRelationManager::layersRemoved( const QStringList &layers )
255 {
256 bool relationsChanged = false;
257 for ( const QString &layer : std::as_const( layers ) )
258 {
259 QMapIterator<QString, QgsRelation> it( mRelations );
260
261 while ( it.hasNext() )
262 {
263 it.next();
264
265 if ( it.value().referencedLayerId() == layer
266 || it.value().referencingLayerId() == layer )
267 {
268 mRelations.remove( it.key() );
269 relationsChanged = true;
270 }
271 }
272 }
273 if ( relationsChanged )
274 {
275 emit changed();
276 }
277 }
278
hasRelationWithEqualDefinition(const QList<QgsRelation> & existingRelations,const QgsRelation & relation)279 static bool hasRelationWithEqualDefinition( const QList<QgsRelation> &existingRelations, const QgsRelation &relation )
280 {
281 for ( const QgsRelation &cur : std::as_const( existingRelations ) )
282 {
283 if ( cur.hasEqualDefinition( relation ) ) return true;
284 }
285 return false;
286 }
287
discoverRelations(const QList<QgsRelation> & existingRelations,const QList<QgsVectorLayer * > & layers)288 QList<QgsRelation> QgsRelationManager::discoverRelations( const QList<QgsRelation> &existingRelations, const QList<QgsVectorLayer *> &layers )
289 {
290 QList<QgsRelation> result;
291 for ( const QgsVectorLayer *layer : std::as_const( layers ) )
292 {
293 if ( const QgsVectorDataProvider *provider = layer->dataProvider() )
294 {
295 const auto constDiscoverRelations = provider->discoverRelations( layer, layers );
296 for ( const QgsRelation &relation : constDiscoverRelations )
297 {
298 if ( !hasRelationWithEqualDefinition( existingRelations, relation ) )
299 {
300 result.append( relation );
301 }
302 }
303 }
304 }
305 return result;
306 }
307
polymorphicRelations() const308 QMap<QString, QgsPolymorphicRelation> QgsRelationManager::polymorphicRelations() const
309 {
310 return mPolymorphicRelations;
311 }
312
polymorphicRelation(const QString & polymorphicRelationId) const313 QgsPolymorphicRelation QgsRelationManager::polymorphicRelation( const QString &polymorphicRelationId ) const
314 {
315 return mPolymorphicRelations.value( polymorphicRelationId );
316 }
317
addPolymorphicRelation(const QgsPolymorphicRelation & polymorphicRelation)318 void QgsRelationManager::addPolymorphicRelation( const QgsPolymorphicRelation &polymorphicRelation )
319 {
320 if ( !polymorphicRelation.referencingLayer() || polymorphicRelation.id().isNull() )
321 return;
322
323 mPolymorphicRelations.insert( polymorphicRelation.id(), polymorphicRelation );
324
325 const QList<QgsRelation> generatedRelations = polymorphicRelation.generateRelations();
326 for ( const QgsRelation &generatedRelation : generatedRelations )
327 addRelation( generatedRelation );
328 }
329
removePolymorphicRelation(const QString & polymorphicRelationId)330 void QgsRelationManager::removePolymorphicRelation( const QString &polymorphicRelationId )
331 {
332 QgsPolymorphicRelation relation = mPolymorphicRelations.take( polymorphicRelationId );
333
334 const QList<QgsRelation> generatedRelations = relation.generateRelations();
335 for ( const QgsRelation &generatedRelation : generatedRelations )
336 removeRelation( generatedRelation.id() );
337 }
338
setPolymorphicRelations(const QList<QgsPolymorphicRelation> & relations)339 void QgsRelationManager::setPolymorphicRelations( const QList<QgsPolymorphicRelation> &relations )
340 {
341 const QList<QgsPolymorphicRelation> oldRelations = polymorphicRelations().values();
342 for ( const QgsPolymorphicRelation &oldRelation : oldRelations )
343 removePolymorphicRelation( oldRelation.id() );
344
345 for ( const QgsPolymorphicRelation &newRelation : relations )
346 addPolymorphicRelation( newRelation );
347 }
348