Exemple #1
0
        // determines if a given entity, appearing in a given event definition, should be filtered out,
        // considering the entities that have already been visited - an entity is filtered out if it
        // appears in another even definition, which superceedes this event definition.
        private static bool IsSuperceeded(IEntity entity, EventDefinitionInfos infos, List <Tuple <IEntity, EventDefinitionInfos> > entities)
        {
            //var argType = meta.EventArgsType;
            var argType = infos.EventDefinition.Args.GetType();

            // look for other instances of the same entity, coming from an event args that supercedes other event args,
            // ie is marked with the attribute, and is not this event args (cannot supersede itself)
            var superceeding = entities
                               .Where(x => x.Item2.SupersedeTypes.Length > 0 && // has the attribute
                                      x.Item2.EventDefinition.Args.GetType() != argType && // is not the same
                                      Equals(x.Item1, entity)) // same entity
                               .ToArray();

            // first time we see this entity = not filtered
            if (superceeding.Length == 0)
            {
                return(false);
            }

            // fixme see notes above
            // delete event args does NOT superceedes 'unpublished' event
            if (argType.IsGenericType && argType.GetGenericTypeDefinition() == typeof(PublishEventArgs <>) && infos.EventDefinition.EventName == "Unpublished")
            {
                return(false);
            }

            // found occurences, need to determine if this event args is superceded
            if (argType.IsGenericType)
            {
                // generic, must compare type arguments
                var supercededBy = superceeding.FirstOrDefault(x =>
                                                               x.Item2.SupersedeTypes.Any(y =>
                                                                                          // superceeding a generic type which has the same generic type definition
                                                                                          // fixme no matter the generic type parameters? could be different?
                                                                                          y.IsGenericTypeDefinition && y == argType.GetGenericTypeDefinition()
                                                                                          // or superceeding a non-generic type which is ... fixme how is this ever possible? argType *is* generic?
                                                                                          || y.IsGenericTypeDefinition == false && y == argType));
                return(supercededBy != null);
            }
            else
            {
                // non-generic, can compare types 1:1
                var supercededBy = superceeding.FirstOrDefault(x =>
                                                               x.Item2.SupersedeTypes.Any(y => y == argType));
                return(supercededBy != null);
            }
        }
        // this is way too convoluted, the supersede attribute is used only on DeleteEventargs to specify
        // that it supersedes save, publish, move and copy - BUT - publish event args is also used for
        // unpublishing and should NOT be superseded - so really it should not be managed at event args
        // level but at event level
        //
        // what we want is:
        // if an entity is deleted, then all Saved, Moved, Copied, Published events prior to this should
        // not trigger for the entity - and even though, does it make any sense? making a copy of an entity
        // should ... trigger?
        //
        // not going to refactor it all - we probably want to *always* trigger event but tell people that
        // due to scopes, they should not expected eg a saved entity to still be around - however, now,
        // going to write a ugly condition to deal with U4-10764

        // iterates over the events (latest first) and filter out any events or entities in event args that are included
        // in more recent events that Supersede previous ones. For example, If an Entity has been Saved and then Deleted, we don't want
        // to raise the Saved event (well actually we just don't want to include it in the args for that saved event)
        internal static IEnumerable <IEventDefinition> FilterSupersededAndUpdateToLatestEntity(IReadOnlyList <IEventDefinition> events)
        {
            // keeps the 'latest' entity and associated event data
            var entities = new List <Tuple <IEntity, EventDefinitionInfos> >();

            // collects the event definitions
            // collects the arguments in result, that require their entities to be updated
            var result     = new List <IEventDefinition>();
            var resultArgs = new List <CancellableObjectEventArgs>();

            // eagerly fetch superseded arg types for each arg type
            var argTypeSuperceeding = events.Select(x => x.Args.GetType())
                                      .Distinct()
                                      .ToDictionary(x => x, x => x.GetCustomAttributes <SupersedeEventAttribute>(false).Select(y => y.SupersededEventArgsType).ToArray());

            // iterate over all events and filter
            //
            // process the list in reverse, because events are added in the order they are raised and we want to keep
            // the latest (most recent) entities and filter out what is not relevant anymore (too old), eg if an entity
            // is Deleted after being Saved, we want to filter out the Saved event
            for (var index = events.Count - 1; index >= 0; index--)
            {
                var def = events[index];

                var infos = new EventDefinitionInfos
                {
                    EventDefinition = def,
                    SupersedeTypes  = argTypeSuperceeding[def.Args.GetType()]
                };

                var args = def.Args as CancellableObjectEventArgs;
                if (args == null)
                {
                    // not a cancellable event arg, include event definition in result
                    result.Add(def);
                }
                else
                {
                    // event object can either be a single object or an enumerable of objects
                    // try to get as an enumerable, get null if it's not
                    var eventObjects = TypeHelper.CreateGenericEnumerableFromObject(args.EventObject);
                    if (eventObjects == null)
                    {
                        // single object, cast as an IEntity
                        // if cannot cast, cannot filter, nothing - just include event definition in result
                        var eventEntity = args.EventObject as IEntity;
                        if (eventEntity == null)
                        {
                            result.Add(def);
                            continue;
                        }

                        // look for this entity in superseding event args
                        // found = must be removed (ie not added), else track
                        if (IsSuperceeded(eventEntity, infos, entities) == false)
                        {
                            // track
                            entities.Add(Tuple.Create(eventEntity, infos));

                            // track result arguments
                            // include event definition in result
                            resultArgs.Add(args);
                            result.Add(def);
                        }
                    }
                    else
                    {
                        // enumerable of objects
                        var toRemove = new List <IEntity>();
                        foreach (var eventObject in eventObjects)
                        {
                            // extract the event object, cast as an IEntity
                            // if cannot cast, cannot filter, nothing to do - just leave it in the list & continue
                            var eventEntity = eventObject as IEntity;
                            if (eventEntity == null)
                            {
                                continue;
                            }

                            // look for this entity in superseding event args
                            // found = must be removed, else track
                            if (IsSuperceeded(eventEntity, infos, entities))
                            {
                                toRemove.Add(eventEntity);
                            }
                            else
                            {
                                entities.Add(Tuple.Create(eventEntity, infos));
                            }
                        }

                        // remove superseded entities
                        foreach (var entity in toRemove)
                        {
                            eventObjects.Remove(entity);
                        }

                        // if there are still entities in the list, keep the event definition
                        if (eventObjects.Count > 0)
                        {
                            if (toRemove.Count > 0)
                            {
                                // re-assign if changed
                                args.EventObject = eventObjects;
                            }

                            // track result arguments
                            // include event definition in result
                            resultArgs.Add(args);
                            result.Add(def);
                        }
                    }
                }
            }

            // go over all args in result, and update them with the latest instanceof each entity
            UpdateToLatestEntities(entities, resultArgs);

            // reverse, since we processed the list in reverse
            result.Reverse();

            return(result);
        }