1   package com.atlassian.core.ofbiz.association;
2   
3   import com.atlassian.core.ofbiz.CoreFactory;
4   import com.atlassian.core.user.UserUtils;
5   import com.opensymphony.user.EntityNotFoundException;
6   import com.opensymphony.user.User;
7   import org.apache.commons.collections.CollectionUtils;
8   import org.apache.commons.collections.Transformer;
9   import org.apache.log4j.Logger;
10  import org.ofbiz.core.entity.EntityUtil;
11  import org.ofbiz.core.entity.GenericDelegator;
12  import org.ofbiz.core.entity.GenericEntityException;
13  import org.ofbiz.core.entity.GenericPK;
14  import org.ofbiz.core.entity.GenericValue;
15  import org.ofbiz.core.util.UtilMisc;
16  
17  import java.util.ArrayList;
18  import java.util.Collections;
19  import java.util.HashMap;
20  import java.util.Iterator;
21  import java.util.List;
22  import java.util.Map;
23  
24  public class DefaultAssociationManager implements AssociationManager
25  {
26      private static final Logger log = Logger.getLogger(DefaultAssociationManager.class);
27      private final GenericDelegator delegator = CoreFactory.getGenericDelegator();
28  
29      /**
30       * Create an association between two entities, given a particular association type.
31       * <p/>
32       * If the association already exists - it will not be created.
33       *
34       * @return The new association, or the existing association if it already existed.
35       */
36      public GenericValue createAssociation(GenericValue source, GenericValue sink, String associationType)
37              throws GenericEntityException
38      {
39          final GenericValue existingAssocation = getAssociation(source, sink, associationType);
40          if (existingAssocation == null)
41          {
42              GenericValue v = delegator.makeValue("NodeAssociation", UtilMisc.toMap(
43                      "associationType", associationType,
44                      "sourceNodeId", source.getLong("id"),
45                      "sourceNodeEntity", source.getEntityName(),
46                      "sinkNodeId", sink.getLong("id"),
47                      "sinkNodeEntity", sink.getEntityName()));
48              v.create();
49              return v;
50          }
51          return existingAssocation;
52      }
53  
54      public GenericValue createAssociation(Long sourceNodeId, String sourceNodeEntity, Long sinkNodeId, String sinkNodeEntity, String associationType)
55              throws GenericEntityException
56      {
57          final GenericValue existingAssocation = getAssociation(sourceNodeId, sourceNodeEntity, sinkNodeId, sinkNodeEntity, associationType);
58          if (existingAssocation == null)
59          {
60              GenericValue v = delegator.makeValue("NodeAssociation", UtilMisc.toMap(
61                      "associationType", associationType,
62                      "sourceNodeId", sourceNodeId,
63                      "sourceNodeEntity", sourceNodeEntity,
64                      "sinkNodeId", sinkNodeId,
65                      "sinkNodeEntity", sinkNodeEntity));
66              v.create();
67              return v;
68          }
69          return existingAssocation;
70      }
71  
72      public GenericValue createAssociation(String userName, Long sinkNodeId, String sinkNodeEntity, String associationType)
73              throws GenericEntityException
74      {
75          final GenericValue existingAssociation = getAssociation(userName, sinkNodeId, sinkNodeEntity, associationType);
76          if (existingAssociation == null)
77          {
78              GenericValue v = delegator.makeValue("UserAssociation", UtilMisc.toMap(
79                      "associationType", associationType,
80                      "sourceName", userName,
81                      "sinkNodeId", sinkNodeId,
82                      "sinkNodeEntity", sinkNodeEntity));
83              v.create();
84              v.refresh();
85              return v;
86          }
87          return existingAssociation;
88      }
89  
90      public GenericValue createAssociation(User user, GenericValue sink, String associationType)
91              throws GenericEntityException
92      {
93          final GenericValue v = delegator.makeValue("UserAssociation", UtilMisc.toMap(
94                  "associationType", associationType,
95                  "sourceName", user.getName(),
96                  "sinkNodeId", sink.getLong("id"),
97                  "sinkNodeEntity", sink.getEntityName()));
98          v.create();
99          v.refresh();
100         return v;
101     }
102 
103 
104     public void removeAssociation(User user, GenericValue sink, String associationType) throws GenericEntityException
105     {
106         removeAssociation(user.getName(), sink, associationType);
107     }
108 
109     public void removeAssociation(String username, GenericValue sink, String associationType)
110             throws GenericEntityException
111     {
112         final Map fields = new HashMap();
113         fields.put("sinkNodeId", sink.getLong("id"));
114         fields.put("sinkNodeEntity", sink.getEntityName());
115         fields.put("sourceName", username);
116         fields.put("associationType", associationType);
117         delegator.removeByPrimaryKey(delegator.makePK("UserAssociation", fields));
118     }
119 
120     public void removeAssociation(GenericValue source, GenericValue sink, String associationType)
121             throws GenericEntityException
122     {
123         final Map fields = new HashMap();
124         fields.put("sinkNodeId", sink.getLong("id"));
125         fields.put("sinkNodeEntity", sink.getEntityName());
126         fields.put("sourceNodeId", source.getLong("id"));
127         fields.put("sourceNodeEntity", source.getEntityName());
128         fields.put("associationType", associationType);
129         delegator.removeByPrimaryKey(delegator.makePK("NodeAssociation", fields));
130     }
131 
132     /**
133      * Remove all entity<->entity associations, given the source
134      */
135     public void removeAssociationsFromSource(GenericValue source) throws GenericEntityException
136     {
137         final Map fields = new HashMap();
138         fields.put("sourceNodeId", source.getLong("id"));
139         fields.put("sourceNodeEntity", source.getEntityName());
140         delegator.removeByAnd("NodeAssociation", fields);
141     }
142 
143     public void removeAssociationsFromSink(GenericValue sink) throws GenericEntityException
144     {
145         final Map fields = new HashMap();
146         fields.put("sinkNodeId", sink.getLong("id"));
147         fields.put("sinkNodeEntity", sink.getEntityName());
148         delegator.removeByAnd("NodeAssociation", fields);
149     }
150 
151     /**
152      * Remove all user<->entity associations, given the entity
153      */
154     public void removeUserAssociationsFromSink(GenericValue sink) throws GenericEntityException
155     {
156         final Map fields = new HashMap();
157         fields.put("sinkNodeId", sink.getLong("id"));
158         fields.put("sinkNodeEntity", sink.getEntityName());
159         delegator.removeByAnd("UserAssociation", fields);
160     }
161 
162     /**
163      * Remove all user<->entity associations, given the entity and association type
164      */
165     public void removeUserAssociationsFromSink(GenericValue sink, String associationType) throws GenericEntityException
166     {
167         final Map fields = new HashMap();
168         fields.put("sinkNodeId", sink.getLong("id"));
169         fields.put("sinkNodeEntity", sink.getEntityName());
170         fields.put("associationType", associationType);
171         delegator.removeByAnd("UserAssociation", fields);
172     }
173 
174 
175     /**
176      * Remove all user<->entity associations, given the user
177      */
178     public void removeUserAssociationsFromUser(User user) throws GenericEntityException
179     {
180         final Map fields = new HashMap();
181         fields.put("sourceName", user.getName());
182         delegator.removeByAnd("UserAssociation", fields);
183     }
184 
185     public void removeUserAssociationsFromUser(final User user, final String associationType)
186             throws GenericEntityException
187     {
188         final Map fields = new HashMap();
189         fields.put("sourceName", user.getName());
190         fields.put("associationType", associationType);
191         delegator.removeByAnd("UserAssociation", fields);
192     }
193 
194     public void removeUserAssociationsFromUser(final User user, final String associationType, final String entityName)
195             throws GenericEntityException
196     {
197         final Map fields = new HashMap();
198         fields.put("sourceName", user.getName());
199         fields.put("associationType", associationType);
200         fields.put("sinkNodeEntity", entityName);
201         delegator.removeByAnd("UserAssociation", fields);
202     }
203 
204 
205     /**
206      * Swap all assocaitions of a particular type from one sink to another.
207      * <p/>
208      * Used in ComponentDelete and VersionDelete.
209      */
210     public void swapAssociation(String sourceEntityName, String associationType, GenericValue fromSink, GenericValue toSink)
211             throws GenericEntityException
212     {
213         final List sources = getSourceFromSink(fromSink, sourceEntityName, associationType, false);
214         swapAssociation(sources, associationType, fromSink, toSink);
215     }
216 
217     /**
218      * Swaps all associations for a given list of entities (say move a list of unresolved issue entities to a new fix for version)
219      */
220     public void swapAssociation(List entities, String associationType, GenericValue fromSink, GenericValue toSink)
221             throws GenericEntityException
222     {
223         for (Iterator iterator = entities.iterator(); iterator.hasNext();)
224         {
225             GenericValue entity = (GenericValue) iterator.next();
226             CoreFactory.getAssociationManager().createAssociation(entity, toSink, associationType);
227             removeAssociation(entity, fromSink, associationType);
228         }
229     }
230 
231     List getAssociations(String associationName, Map fields, boolean useCache, boolean useSequence)
232             throws GenericEntityException
233     {
234         List result;
235         if (useCache)
236         {
237             result = delegator.findByAndCache(associationName, fields);
238         }
239         else
240         {
241             result = delegator.findByAnd(associationName, fields);
242         }
243 
244         if (useSequence)
245         {
246             result = EntityUtil.orderBy(result, UtilMisc.toList("sequence"));
247         }
248         return result;
249     }
250 
251     public GenericValue getAssociation(GenericValue source, GenericValue sink, String associationType)
252             throws GenericEntityException
253     {
254         return getAssociation(source.getLong("id"), source.getEntityName(), sink.getLong("id"), sink.getEntityName(), associationType);
255     }
256 
257     public GenericValue getAssociation(User user, GenericValue sink, String associationType)
258             throws GenericEntityException
259     {
260         if (user == null)
261         {
262             return null;
263         }
264 
265         return EntityUtil.getOnly(delegator.findByAnd("UserAssociation", UtilMisc.toMap(
266                 "associationType", associationType,
267                 "sourceName", user.getName(),
268                 "sinkNodeId", sink.getLong("id"),
269                 "sinkNodeEntity", sink.getEntityName())));
270     }
271 
272     /**
273      * Operates on NodeAssociations - gets MANY sinks from ONE source
274      */
275     public List getSinkFromSource(GenericValue source, String sinkName, String associationType, boolean useCache)
276             throws GenericEntityException
277     {
278         return getSinkFromSource(source, sinkName, associationType, useCache, false);
279     }
280 
281     public List getSinkFromSource(GenericValue source, String sinkName, String associationType, boolean useCache, boolean useSequence)
282             throws GenericEntityException
283     {
284         if (source == null)
285         {
286             throw new IllegalArgumentException("Source GenericValue can not be null.");
287         }
288 
289         final List result = getSinkIdsFromSource(source, sinkName, associationType, useCache, useSequence);
290 
291         final List outList = new ArrayList(result.size());
292         for (Iterator iterator = result.iterator(); iterator.hasNext();)
293         {
294             GenericValue value = (GenericValue) iterator.next();
295             GenericValue byPrimaryKey;
296             if (useCache)
297             {
298                 byPrimaryKey = delegator.findByPrimaryKeyCache(sinkName, UtilMisc.toMap("id", value.getLong("sinkNodeId")));
299             }
300             else
301             {
302                 byPrimaryKey = delegator.findByPrimaryKey(sinkName, UtilMisc.toMap("id", value.getLong("sinkNodeId")));
303             }
304 
305             if (byPrimaryKey != null)
306             {
307                 outList.add(byPrimaryKey);
308             }
309         }
310         return result.isEmpty() ? Collections.EMPTY_LIST : outList;
311     }
312 
313     private List getSinkIdsFromSource(GenericValue source, String sinkName, String associationType, boolean useCache, boolean useSequence)
314             throws GenericEntityException
315     {
316         final Map fields = new HashMap();
317         fields.put("sourceNodeId", source.getLong("id"));
318         fields.put("sourceNodeEntity", source.getEntityName());
319         fields.put("associationType", associationType);
320         fields.put("sinkNodeEntity", sinkName);
321         return getAssociations("NodeAssociation", fields, useCache, useSequence);
322     }
323 
324     /**
325      * Operates on NodeAssociations - gets MANY sources from ONE sink
326      */
327     public List getSourceFromSink(GenericValue sink, String sourceName, String associationType, boolean useCache)
328             throws GenericEntityException
329     {
330         return getSourceFromSink(sink, sourceName, associationType, useCache, false);
331     }
332 
333     /**
334      * Operates on NodeAssociations - gets MANY sources from ONE sink
335      */
336     public List getSourceFromSink(GenericValue sink, String sourceName, String associationType, boolean useCache, boolean useSequence)
337             throws GenericEntityException
338     {
339         if (sink == null)
340         {
341             throw new IllegalArgumentException("Sink GenericValue can not be null.");
342         }
343         final List result = getSourceIdsFromSink(sink, associationType, sourceName, useCache, useSequence);
344         final List outList = new ArrayList(result.size());
345         for (Iterator iterator = result.iterator(); iterator.hasNext();)
346         {
347             GenericValue value = (GenericValue) iterator.next();
348             GenericPK pk = delegator.makePK(sourceName, UtilMisc.toMap("id", value.getLong("sourceNodeId")));
349             GenericValue byPrimaryKey;
350             if (useCache)
351             {
352                 byPrimaryKey = delegator.findByPrimaryKeyCache(pk);
353             }
354             else
355             {
356                 byPrimaryKey = delegator.findByPrimaryKey(pk);
357             }
358 
359             if (byPrimaryKey != null)
360             {
361                 outList.add(byPrimaryKey);
362             }
363         }
364         return result.isEmpty() ? Collections.EMPTY_LIST : outList;
365     }
366 
367     private List getSourceIdsFromSink(GenericValue sink, String associationType, String sourceName, boolean useCache, boolean useSequence)
368             throws GenericEntityException
369     {
370         final Map fields = new HashMap();
371         fields.put("sinkNodeId", sink.getLong("id"));
372         fields.put("sinkNodeEntity", sink.getEntityName());
373         fields.put("associationType", associationType);
374         fields.put("sourceNodeEntity", sourceName);
375         return getAssociations("NodeAssociation", fields, useCache, useSequence);
376     }
377 
378     /**
379      * Operates on UserAssociations - gets MANY sinks from ONE user
380      */
381     public List getSinkFromUser(User source, String sinkName, String associationType, boolean useCache)
382             throws GenericEntityException
383     {
384         return getSinkFromUser(source, sinkName, associationType, useCache, false);
385     }
386 
387     public List /*<Long>*/ getSinkIdsFromUser(final User source, final String sinkName, final String associationType, final boolean useCache)
388             throws GenericEntityException
389     {
390         final List result = getAssociationsForUser(source, sinkName, associationType, useCache, false);
391         CollectionUtils.transform(result, new Transformer()
392         {
393             public Object transform(Object input)
394             {
395                 return ((GenericValue) input).getLong("sinkNodeId");
396             }
397         });
398         return result;
399     }
400 
401     /**
402      * Operates on UserAssociations - gets MANY sinks from ONE user
403      */
404     public List getSinkFromUser(User source, String sinkName, String associationType, boolean useCache, boolean useSequence)
405             throws GenericEntityException
406     {
407         final List result = getAssociationsForUser(source, sinkName, associationType, useCache, useSequence);
408 
409         final List outList = new ArrayList(result.size());
410         for (Iterator iterator = result.iterator(); iterator.hasNext();)
411         {
412             GenericValue value = (GenericValue) iterator.next();
413             GenericPK pk = delegator.makePK(sinkName, UtilMisc.toMap("id", value.getLong("sinkNodeId")));
414             GenericValue byPrimaryKey;
415             if (useCache)
416             {
417                 byPrimaryKey = delegator.findByPrimaryKeyCache(pk);
418             }
419             else
420             {
421                 byPrimaryKey = delegator.findByPrimaryKey(pk);
422             }
423 
424             if (byPrimaryKey != null)
425             {
426                 outList.add(byPrimaryKey);
427             }
428         }
429         return result.isEmpty() ? Collections.EMPTY_LIST : outList;
430     }
431 
432     private List getAssociationsForUser(final User source, final String sinkName, final String associationType, final boolean useCache, final boolean useSequence)
433             throws GenericEntityException
434     {
435         if (source == null)
436         {
437             throw new IllegalArgumentException("User can not be null.");
438         }
439         final Map fields = new HashMap();
440         fields.put("sourceName", source.getName());
441         fields.put("associationType", associationType);
442         fields.put("sinkNodeEntity", sinkName);
443         return getAssociations("UserAssociation", fields, useCache, useSequence);
444     }
445 
446     /**
447      * Operates on UserAssociations - gets MANY users from ONE sink
448      */
449     public List getUserFromSink(GenericValue sink, String associationType, boolean useCache)
450             throws GenericEntityException, EntityNotFoundException
451     {
452         return getUserFromSink(sink, associationType, useCache, false);
453     }
454 
455     /**
456      * Operates on UserAssociations - gets MANY users from ONE sink
457      */
458     public List/*<User>*/ getUserFromSink(GenericValue sink, String associationType, boolean useCache, boolean useSequence)
459             throws GenericEntityException, EntityNotFoundException
460     {
461         final List/*<String>*/ usernames = getUsernamesFromSink(sink, associationType, useCache, useSequence);
462         final List/*<User>*/ users = new ArrayList(usernames.size());
463         for (Iterator it = usernames.iterator(); it.hasNext();)
464         {
465             String username = (String) it.next();
466             try
467             {
468                 final User user = UserUtils.getUser(username);
469                 if (user != null)
470                 {
471                     users.add(user);
472                 }
473             }
474             catch (EntityNotFoundException e)
475             {
476                 log.error("Cannot find user with username '" + username + "'.", e);
477             }
478         }
479         return users;
480     }
481 
482     public List/*<String>*/ getUsernamesFromSink(GenericValue sink, String associationType, boolean useCache, boolean useSequence)
483             throws GenericEntityException
484     {
485         if (sink == null)
486         {
487             throw new IllegalArgumentException("Sink GenericValue can not be null.");
488         }
489 
490         final Map fields = new HashMap();
491         fields.put("sinkNodeId", sink.getLong("id"));
492         fields.put("sinkNodeEntity", sink.getEntityName());
493         fields.put("associationType", associationType);
494 
495         final List results = getAssociations("UserAssociation", fields, useCache, useSequence);
496         if (results.isEmpty())
497         {
498             return Collections.EMPTY_LIST;
499         }
500 
501         final List outList = new ArrayList(results.size());
502         for (Iterator it = results.iterator(); it.hasNext();)
503         {
504             outList.add(((GenericValue) it.next()).getString("sourceName"));
505         }
506         return outList;
507     }
508 
509     public List getSinkIdsFromSource(GenericValue source, String sinkEntity, String associationType)
510             throws GenericEntityException
511     {
512         List sinks = getSinkIdsFromSource(source, sinkEntity, associationType, false, false);
513 
514         if (sinks != null && !sinks.isEmpty())
515         {
516             List sinkIds = new ArrayList();
517             for (Iterator iterator = sinks.iterator(); iterator.hasNext();)
518             {
519                 GenericValue sink = (GenericValue) iterator.next();
520                 sinkIds.add(sink.getLong("sinkNodeId"));
521             }
522             return sinkIds;
523         }
524 
525         return Collections.EMPTY_LIST;
526     }
527 
528     public List getSourceIdsFromSink(GenericValue sink, String sourceEntity, String associationType)
529             throws GenericEntityException
530     {
531         List sources = getSourceIdsFromSink(sink, associationType, sourceEntity, false, false);
532 
533         if (sources != null && !sources.isEmpty())
534         {
535             List sourceIds = new ArrayList();
536             for (Iterator iterator = sources.iterator(); iterator.hasNext();)
537             {
538                 GenericValue source = (GenericValue) iterator.next();
539                 sourceIds.add(source.getLong("sourceNodeId"));
540             }
541             return sourceIds;
542         }
543 
544         return Collections.EMPTY_LIST;
545     }
546 
547     private GenericValue getAssociation(Long sourceNodeId, String sourceNodeEntity, Long sinkNodeId, String sinkNodeEntity, String associationType)
548             throws GenericEntityException
549     {
550         return EntityUtil.getOnly(delegator.findByAnd("NodeAssociation", UtilMisc.toMap("associationType", associationType, "sourceNodeId", sourceNodeId, "sourceNodeEntity", sourceNodeEntity, "sinkNodeId", sinkNodeId, "sinkNodeEntity", sinkNodeEntity)));
551     }
552 
553     private GenericValue getAssociation(String sourceName, Long sinkNodeId, String sinkNodeEntity, String associationType)
554             throws GenericEntityException
555     {
556         return EntityUtil.getOnly(delegator.findByAnd("UserAssociation", UtilMisc.toMap("associationType", associationType, "sourceName", sourceName, "sinkNodeId", sinkNodeId, "sinkNodeEntity", sinkNodeEntity)));
557     }
558 }