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