Ejemplo n.º 1
0
        /// <summary>
        ///     This method updates child views and clears the batch of events.
        /// </summary>
        protected void SendBatch()
        {
            // If there are child views and the batch was filled, fireStatementStopped update method
            if (child != null) {
                // Convert to object arrays
                EventBean[] newData = null;
                EventBean[] oldData = null;
                if (!currentBatch.IsEmpty()) {
                    newData = currentBatch.ToArray();
                }

                if (lastBatch != null && !lastBatch.IsEmpty()) {
                    oldData = lastBatch.ToArray();
                }

                // update view buffer to serve expressions require access to events held
                viewUpdatedCollection?.Update(newData, oldData);

                // Post new data (current batch) and old data (prior batch)
                if (newData != null || oldData != null) {
                    agentInstanceContext.InstrumentationProvider.QViewIndicate(
                        lengthBatchViewFactory,
                        newData,
                        oldData);
                    child.Update(newData, oldData);
                    agentInstanceContext.InstrumentationProvider.AViewIndicate();
                }
            }

            lastBatch = currentBatch;
            currentBatch = new ArrayDeque<EventBean>();
        }
Ejemplo n.º 2
0
        /// <summary>Adds event to the time window for the specified timestamp. </summary>
        /// <param name="timestamp">the time slot for the event</param>
        /// <param name="bean">event to add</param>
        public void Add(long timestamp, EventBean bean)
        {
            // Empty window
            if (_window.IsEmpty())
            {
                var pairX = new TimeWindowPair(timestamp, bean);
                _window.Add(pairX);

                if (_reverseIndex != null)
                {
                    _reverseIndex[bean] = pairX;
                }
                _size = 1;
                return;
            }

            TimeWindowPair lastPair = _window.Last;

            // Windows last timestamp matches the one supplied
            if (lastPair.Timestamp == timestamp)
            {
                if (lastPair.EventHolder is IList <EventBean> )
                {
                    var list = (IList <EventBean>)lastPair.EventHolder;
                    list.Add(bean);
                }
                else if (lastPair.EventHolder == null)
                {
                    lastPair.EventHolder = bean;
                }
                else
                {
                    var existing           = (EventBean)lastPair.EventHolder;
                    IList <EventBean> list = new List <EventBean>(4);
                    list.Add(existing);
                    list.Add(bean);
                    lastPair.EventHolder = list;
                }
                if (_reverseIndex != null)
                {
                    _reverseIndex[bean] = lastPair;
                }
                _size++;
                return;
            }

            // Append to window
            var pair = new TimeWindowPair(timestamp, bean);

            if (_reverseIndex != null)
            {
                _reverseIndex[bean] = pair;
            }
            _window.Add(pair);
            _size++;
        }
Ejemplo n.º 3
0
        /// <summary>
        /// This method updates child views and clears the batch of events.
        /// We schedule a new callback at this time if there were events in the batch.
        /// </summary>
        public void SendBatch()
        {
            _isCallbackScheduled = false;

            // If there are child views and the batch was filled, fireStatementStopped update method
            if (HasViews)
            {
                // Convert to object arrays
                EventBean[] newData = null;
                EventBean[] oldData = null;
                if (!_currentBatch.IsEmpty())
                {
                    newData = _currentBatch.ToArray();
                }
                if ((_lastBatch != null) && (!_lastBatch.IsEmpty()))
                {
                    oldData = _lastBatch.ToArray();
                }

                // Post new data (current batch) and old data (prior batch)
                if (_viewUpdatedCollection != null)
                {
                    _viewUpdatedCollection.Update(newData, oldData);
                }
                if ((newData != null) || (oldData != null) || _isForceOutput)
                {
                    if (InstrumentationHelper.ENABLED)
                    {
                        InstrumentationHelper.Get().QViewIndicate(this, _timeBatchViewFactory.ViewName, newData, oldData);
                    }
                    UpdateChildren(newData, oldData);
                    if (InstrumentationHelper.ENABLED)
                    {
                        InstrumentationHelper.Get().AViewIndicate();
                    }
                }
            }

            // Only if forceOutput is enabled or
            // there have been any events in this or the last interval do we schedule a callback,
            // such as to not waste resources when no events arrive.
            if ((!_currentBatch.IsEmpty()) || ((_lastBatch != null) && (!_lastBatch.IsEmpty()))
                ||
                _isForceOutput)
            {
                ScheduleCallback();
                _isCallbackScheduled = true;
            }

            _lastBatch    = _currentBatch;
            _currentBatch = new ArrayDeque <EventBean>();
        }
Ejemplo n.º 4
0
        public override void Update(EventBean[] newData, EventBean[] oldData)
        {
            if (InstrumentationHelper.ENABLED)
            {
                InstrumentationHelper.Get().QViewProcessIRStream(this, _timeBatchViewFactory.ViewName, newData, oldData);
            }

            // we don't care about removed data from a prior view
            if ((newData == null) || (newData.Length == 0))
            {
                if (InstrumentationHelper.ENABLED)
                {
                    InstrumentationHelper.Get().AViewProcessIRStream();
                }
                return;
            }

            // If we have an empty window about to be filled for the first time, schedule a callback
            if (_currentBatch.IsEmpty())
            {
                if (_currentReferencePoint == null)
                {
                    _currentReferencePoint = _initialReferencePoint;
                    if (_currentReferencePoint == null)
                    {
                        _currentReferencePoint = _agentInstanceContext.StatementContext.SchedulingService.Time;
                    }
                }

                // Schedule the next callback if there is none currently scheduled
                if (!_isCallbackScheduled)
                {
                    ScheduleCallback();
                    _isCallbackScheduled = true;
                }
            }

            // add data points to the timeWindow
            foreach (EventBean newEvent in newData)
            {
                _currentBatch.Add(newEvent);
            }

            // We do not update child views, since we batch the events.
            if (InstrumentationHelper.ENABLED)
            {
                InstrumentationHelper.Get().AViewProcessIRStream();
            }
        }
Ejemplo n.º 5
0
        /// <summary>
        ///     Flatten the vector of arrays to an array. Return null if an empty vector was passed, else
        ///     return an array containing all the events.
        /// </summary>
        /// <param name="eventVector">vector</param>
        /// <returns>array with all events</returns>
        public static EventBean[] Flatten(ArrayDeque<EventBean[]> eventVector)
        {
            if (eventVector.IsEmpty()) {
                return null;
            }

            if (eventVector.Count == 1) {
                return eventVector.First;
            }

            var totalElements = 0;
            foreach (var arr in eventVector) {
                if (arr != null) {
                    totalElements += arr.Length;
                }
            }

            if (totalElements == 0) {
                return null;
            }

            var result = new EventBean[totalElements];
            var destPos = 0;
            foreach (var arr in eventVector) {
                if (arr != null) {
                    Array.Copy(arr, 0, result, destPos, arr.Length);
                    destPos += arr.Length;
                }
            }

            return result;
        }
Ejemplo n.º 6
0
        public static ICollection<EventBean> GetDistinctByProp(
            ArrayDeque<EventBean> events,
            EventPropertyValueGetter getter)
        {
            if (events == null || events.IsEmpty()) {
                return new EventBean[0];
            }

            if (events.Count < 2) {
                return events;
            }

            var map = new LinkedHashMap<object, EventBean>();
            if (events.First is NaturalEventBean) {
                foreach (var theEvent in events) {
                    var inner = ((NaturalEventBean) theEvent).OptionalSynthetic;
                    var key = getter.Get(inner);
                    map[key] = inner;
                }
            }
            else {
                foreach (var theEvent in events) {
                    var key = getter.Get(theEvent);
                    map[key] = theEvent;
                }
            }

            return map.Values.ToArray();
        }
Ejemplo n.º 7
0
        public static EventBean[] GetNewDataNonRemoved(
            EventBean[] newData,
            ISet<EventBean> removedEvents)
        {
            var filter = false;
            for (var i = 0; i < newData.Length; i++) {
                if (removedEvents.Contains(newData[i])) {
                    filter = true;
                }
            }

            if (!filter) {
                return newData;
            }

            if (newData.Length == 1) {
                return null;
            }

            var events = new ArrayDeque<EventBean>(newData.Length - 1);
            for (var i = 0; i < newData.Length; i++) {
                if (!removedEvents.Contains(newData[i])) {
                    events.Add(newData[i]);
                }
            }

            if (events.IsEmpty()) {
                return null;
            }

            return events.ToArray();
        }
Ejemplo n.º 8
0
        public static EventBean[] GetNewDataNonRemoved(
            EventBean[] newData,
            ISet<EventBean> removedEvents,
            EventBean[][] newEventsPerView)
        {
            if (newData == null || newData.Length == 0) {
                return null;
            }

            if (newData.Length == 1) {
                if (removedEvents.Contains(newData[0])) {
                    return null;
                }

                var pass = FindEvent(newData[0], newEventsPerView);
                return pass ? newData : null;
            }

            var events = new ArrayDeque<EventBean>(newData.Length - 1);
            for (var i = 0; i < newData.Length; i++) {
                if (!removedEvents.Contains(newData[i])) {
                    var pass = FindEvent(newData[i], newEventsPerView);
                    if (pass) {
                        events.Add(newData[i]);
                    }
                }
            }

            if (events.IsEmpty()) {
                return null;
            }

            return events.ToArray();
        }
Ejemplo n.º 9
0
        /// <summary>
        ///     NOTE: Code-generation-invoked method, method name and parameter order matters
        /// </summary>
        /// <param name="arrays">values</param>
        /// <returns>array</returns>
        public static EventBean[][] ToArrayEventsArray(ArrayDeque<EventBean[]> arrays)
        {
            if (arrays.IsEmpty()) {
                return EVENTBEANARRAYARRAY_EMPTY;
            }

            return arrays.ToArray();
        }
Ejemplo n.º 10
0
        /// <summary>Add to the current node building up the tree path information.</summary>
        /// <param name="remainingParameters">any remaining parameters</param>
        /// <param name="filterCallback">the filter callback</param>
        /// <param name="currentNode">is the node to add to</param>
        /// <param name="lockFactory">the lock factory</param>
        private static void AddToNode(
            ArrayDeque<FilterValueSetParam> remainingParameters,
            FilterHandle filterCallback,
            FilterHandleSetNode currentNode,
            FilterServiceGranularLockFactory lockFactory)
        {
            // If no parameters are specified, add to current node, and done
            if (remainingParameters.IsEmpty()) {
                using (currentNode.NodeRWLock.WriteLock.Acquire())
                {
                    currentNode.Add(filterCallback);
                }

                return;
            }

            // Need to find an existing index that matches one of the filter parameters
            Pair<FilterValueSetParam, FilterParamIndexBase> pair;
            using (currentNode.NodeRWLock.ReadLock.Acquire())
            {
                pair = IndexHelper.FindIndex(remainingParameters, currentNode.Indizes);

                // Found an index matching a filter parameter
                if (pair != null)
                {
                    remainingParameters.Remove(pair.First);
                    var filterForValue = pair.First.FilterForValue;
                    var index = pair.Second;
                    AddToIndex(remainingParameters, filterCallback, index, filterForValue, lockFactory);
                    return;
                }
            }

            // An index for any of the filter parameters was not found, create one
            using (currentNode.NodeRWLock.WriteLock.Acquire())
            {
                pair = IndexHelper.FindIndex(remainingParameters, currentNode.Indizes);

                // Attempt to find an index again this time under a write lock
                if (pair != null)
                {
                    remainingParameters.Remove(pair.First);
                    var filterForValue = pair.First.FilterForValue;
                    var indexInner = pair.Second;
                    AddToIndex(remainingParameters, filterCallback, indexInner, filterForValue, lockFactory);
                    return;
                }

                // No index found that matches any parameters, create a new one
                // Pick the next parameter for an index
                var parameterPickedForIndex = remainingParameters.RemoveFirst();
                var index = IndexFactory.CreateIndex(parameterPickedForIndex.Lookupable, lockFactory, parameterPickedForIndex.FilterOperator);

                currentNode.Add(index);
                AddToIndex(remainingParameters, filterCallback, index, parameterPickedForIndex.FilterForValue, lockFactory);
            }
        }
Ejemplo n.º 11
0
        public override void Update(
            EventBean[] newData,
            EventBean[] oldData)
        {
            AgentInstanceContext agentInstanceContext = agentInstanceViewFactoryContext.AgentInstanceContext;
            agentInstanceContext.AuditProvider.View(newData, oldData, agentInstanceContext, factory);
            agentInstanceContext.InstrumentationProvider.QViewProcessIRStream(factory, newData, oldData);
            long timestamp = -1;

            // add data points to the window
            // we don't care about removed data from a prior view
            if (newData != null) {
                for (int i = 0; i < newData.Length; i++) {
                    timestamp = GetLongValue(newData[i]);
                    timeWindow.Add(timestamp, newData[i]);
                }
            }

            // Remove from the window any events that have an older timestamp then the last event's timestamp
            ArrayDeque<EventBean> expired = null;
            if (timestamp != -1) {
                expired = timeWindow.ExpireEvents(
                    timestamp -
                    timePeriodProvide.DeltaSubtract(timestamp, null, true, agentInstanceViewFactoryContext) +
                    1);
            }

            EventBean[] oldDataUpdate = null;
            if ((expired != null) && (!expired.IsEmpty())) {
                oldDataUpdate = expired.ToArray();
            }

            if ((oldData != null) && (agentInstanceViewFactoryContext.IsRemoveStream)) {
                foreach (EventBean anOldData in oldData) {
                    timeWindow.Remove(anOldData);
                }

                if (oldDataUpdate == null) {
                    oldDataUpdate = oldData;
                }
                else {
                    oldDataUpdate = CollectionUtil.AddArrayWithSetSemantics(oldData, oldDataUpdate);
                }
            }

            viewUpdatedCollection?.Update(newData, oldDataUpdate);

            // If there are child views, fireStatementStopped update method
            if (Child != null) {
                agentInstanceContext.InstrumentationProvider.QViewIndicate(factory, newData, oldDataUpdate);
                Child.Update(newData, oldDataUpdate);
                agentInstanceContext.InstrumentationProvider.AViewIndicate();
            }

            agentInstanceContext.InstrumentationProvider.AViewProcessIRStream();
        }
Ejemplo n.º 12
0
        /// <summary>
        ///     Flatten the vector of arrays to an array. Return null if an empty vector was passed, else
        ///     return an array containing all the events.
        /// </summary>
        /// <param name="eventVector">vector</param>
        /// <returns>array with all events</returns>
        public static UniformPair<EventBean[]> FlattenList(ArrayDeque<UniformPair<EventBean[]>> eventVector)
        {
            if (eventVector.IsEmpty()) {
                return null;
            }

            if (eventVector.Count == 1) {
                return eventVector.First;
            }

            var totalNew = 0;
            var totalOld = 0;
            foreach (var pair in eventVector) {
                if (pair != null) {
                    if (pair.First != null) {
                        totalNew += pair.First.Length;
                    }

                    if (pair.Second != null) {
                        totalOld += pair.Second.Length;
                    }
                }
            }

            if (totalNew + totalOld == 0) {
                return null;
            }

            EventBean[] resultNew = null;
            if (totalNew > 0) {
                resultNew = new EventBean[totalNew];
            }

            EventBean[] resultOld = null;
            if (totalOld > 0) {
                resultOld = new EventBean[totalOld];
            }

            var destPosNew = 0;
            var destPosOld = 0;
            foreach (var pair in eventVector) {
                if (pair != null) {
                    if (pair.First != null) {
                        Array.Copy(pair.First, 0, resultNew, destPosNew, pair.First.Length);
                        destPosNew += pair.First.Length;
                    }

                    if (pair.Second != null) {
                        Array.Copy(pair.Second, 0, resultOld, destPosOld, pair.Second.Length);
                        destPosOld += pair.Second.Length;
                    }
                }
            }

            return new UniformPair<EventBean[]>(resultNew, resultOld);
        }
Ejemplo n.º 13
0
 private void NextToken()
 {
     tokens.RemoveFirst(); // Pop();
     if (tokens.IsEmpty()) {
         lookahead = new Token(TokenType.END, "");
     }
     else {
         lookahead = tokens.First;
     }
 }
Ejemplo n.º 14
0
 public EventBean[] GetProperty(EventBean theEvent, ExprEvaluatorContext exprEvaluatorContext)
 {
     var resultEvents = new ArrayDeque<EventBean>();
     _eventsPerStream[0] = theEvent;
     PopulateEvents(theEvent, 0, resultEvents, exprEvaluatorContext);
     if (resultEvents.IsEmpty())
     {
         return null;
     }
     return resultEvents.ToArray();
 }
Ejemplo n.º 15
0
 /// <summary>Returns true if the window is empty, or false if not empty. </summary>
 /// <returns>true if empty</returns>
 public bool IsEmpty()
 {
     if (LastBatch != null)
     {
         if (LastBatch.IsNotEmpty())
         {
             return(false);
         }
     }
     return(CurrentBatch.IsEmpty());
 }
Ejemplo n.º 16
0
        public PropertyTokenParser(
            ArrayDeque<Token> tokens,
            bool rootedDynamic)
        {
            if (tokens.IsEmpty()) {
                throw new PropertyParseNodepException("Empty property name");
            }

            lookahead = tokens.First;
            this.tokens = tokens;
            dynamic = rootedDynamic;
        }
Ejemplo n.º 17
0
        /// <summary>
        ///     This method updates child views and clears the batch of events.
        ///     We schedule a new callback at this time if there were events in the batch.
        /// </summary>
        private void SendBatch()
        {
            _isCallbackScheduled = false;

            // If there are child views and the batch was filled, fireStatementStopped update method
            if (Child != null) {
                // Convert to object arrays
                EventBean[] newData = null;
                EventBean[] oldData = null;
                if (!_currentBatch.IsEmpty()) {
                    newData = _currentBatch.ToArray();
                }

                if (_lastBatch != null && !_lastBatch.IsEmpty()) {
                    oldData = _lastBatch.ToArray();
                }

                // Post new data (current batch) and old data (prior batch)
                _viewUpdatedCollection?.Update(newData, oldData);

                if (newData != null || oldData != null || _factory.isForceUpdate) {
                    _agentInstanceContext.InstrumentationProvider.QViewIndicate(_factory, newData, oldData);
                    Child.Update(newData, oldData);
                    _agentInstanceContext.InstrumentationProvider.AViewIndicate();
                }
            }

            // Only if forceOutput is enabled or
            // there have been any events in this or the last interval do we schedule a callback,
            // such as to not waste resources when no events arrive.
            if (!_currentBatch.IsEmpty() ||
                _lastBatch != null && !_lastBatch.IsEmpty() ||
                _factory.isForceUpdate) {
                ScheduleCallback();
                _isCallbackScheduled = true;
            }

            _lastBatch = _currentBatch;
            _currentBatch = new ArrayDeque<EventBean>();
        }
Ejemplo n.º 18
0
        /// <summary>Returns the accumulative events for the input event. </summary>
        /// <param name="theEvent">is the input event</param>
        /// <param name="exprEvaluatorContext">expression evaluation context</param>
        /// <returns>events per stream for each row</returns>
        public ArrayDeque <EventBean[]> GetAccumulative(EventBean theEvent, ExprEvaluatorContext exprEvaluatorContext)
        {
            var resultEvents    = new ArrayDeque <EventBean[]>();
            var eventsPerStream = new EventBean[_levels];

            eventsPerStream[0] = theEvent;
            PopulateEvents(eventsPerStream, theEvent, 0, resultEvents, exprEvaluatorContext);
            if (resultEvents.IsEmpty())
            {
                return(null);
            }
            return(resultEvents);
        }
Ejemplo n.º 19
0
        public override void Update(
            EventBean[] newData,
            EventBean[] oldData)
        {
            _agentInstanceContext.AuditProvider.View(newData, oldData, _agentInstanceContext, _factory);
            _agentInstanceContext.InstrumentationProvider.QViewProcessIRStream(_factory, newData, oldData);

            // we don't care about removed data from a prior view
            if (newData == null || newData.Length == 0) {
                _agentInstanceContext.InstrumentationProvider.AViewProcessIRStream();
                return;
            }

            // If we have an empty window about to be filled for the first time, schedule a callback
            if (_currentBatch.IsEmpty()) {
                if (_currentReferencePoint == null) {
                    _currentReferencePoint = _factory.optionalReferencePoint;
                    if (_currentReferencePoint == null) {
                        _currentReferencePoint = _agentInstanceContext.StatementContext.SchedulingService.Time;
                    }
                }

                // Schedule the next callback if there is none currently scheduled
                if (!_isCallbackScheduled) {
                    ScheduleCallback();
                    _isCallbackScheduled = true;
                }
            }

            // add data points to the timeWindow
            foreach (var newEvent in newData) {
                _currentBatch.Add(newEvent);
            }

            // We do not update child views, since we batch the events.
            _agentInstanceContext.InstrumentationProvider.AViewProcessIRStream();
        }
Ejemplo n.º 20
0
 public void DequeueBackTest()
 {
     for (int i = 0; i < 3; i++)
     {
         deque.Enqueue(i);
     }
     for (int i = 0; i < 3; i++)
     {
         deque.EnqueueFront(i);
     }
     Assert.AreEqual(2, deque.DequeueBack());
     Assert.AreEqual("{2, 1, 0, 0, 1}", deque.ToString());
     for (int i = 0; i < 5; i++)
     {
         deque.DequeueBack();
     }
     Assert.AreEqual(true, deque.IsEmpty());
 }
Ejemplo n.º 21
0
 /// <summary>Returns true if the window is empty, or false if not empty. </summary>
 /// <returns>true if empty</returns>
 public bool IsEmpty()
 {
     return(_events.IsEmpty());
 }
Ejemplo n.º 22
0
        // Called based on schedule evaluation registered when a variable changes (new data is null).
        // Called when new data arrives.
        private void Expire(
            EventBean[] newData,
            EventBean[] oldData)
        {
            OneEventCollection expired = null;
            if (oldData != null) {
                expired = new OneEventCollection();
                expired.Add(oldData);
            }

            var expiredCount = 0;
            if (!window.IsEmpty()) {
                var newest = window.Last;

                while (true) {
                    var first = window.First;

                    var pass = CheckEvent(first, newest, expiredCount);
                    if (!pass) {
                        if (expired == null) {
                            expired = new OneEventCollection();
                        }

                        var removed = window.RemoveFirst().TheEvent;
                        expired.Add(removed);
                        if (aggregationService != null) {
                            removedEvents[0] = removed;
                            aggregationService.ApplyLeave(removedEvents, null, agentInstanceContext);
                        }

                        expiredCount++;
                        InternalHandleExpired(first);
                    }
                    else {
                        break;
                    }

                    if (window.IsEmpty()) {
                        aggregationService?.ClearResults(agentInstanceContext);

                        break;
                    }
                }
            }

            // Check for any events that get pushed out of the window
            EventBean[] expiredArr = null;
            if (expired != null) {
                expiredArr = expired.ToArray();
            }

            // update event buffer for access by expressions, if any
            viewUpdatedCollection?.Update(newData, expiredArr);

            // If there are child views, call update method
            if (child != null) {
                agentInstanceContext.InstrumentationProvider.QViewIndicate(factory, newData, expiredArr);
                child.Update(newData, expiredArr);
                agentInstanceContext.InstrumentationProvider.AViewIndicate();
            }
        }
Ejemplo n.º 23
0
        /// <summary>
        /// Add to the current node building up the tree path information.
        /// </summary>
        /// <param name="remainingParameters">The remaining parameters.</param>
        /// <param name="filterCallback">The filter callback.</param>
        /// <param name="currentNode">is the node to add to</param>
        /// <param name="treePathInfo">is filled with information about which indizes were chosen to add the filter to</param>
        /// <param name="lockFactory">The lock factory.</param>
        private static void AddToNode(
            ArrayDeque <FilterValueSetParam> remainingParameters,
            FilterHandle filterCallback,
            FilterHandleSetNode currentNode,
            ArrayDeque <EventTypeIndexBuilderIndexLookupablePair> treePathInfo,
            FilterServiceGranularLockFactory lockFactory)
        {
            if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled))
            {
                Log.Debug(".addToNode (" + Thread.CurrentThread.ManagedThreadId + ") Adding filterCallback, node=" + currentNode +
                          "  remainingParameters=" + PrintRemainingParameters(remainingParameters));
            }

            // If no parameters are specified, add to current node, and done
            if (remainingParameters.IsEmpty())
            {
                using (currentNode.NodeRWLock.AcquireWriteLock())
                {
                    currentNode.Add(filterCallback);
                }
                return;
            }

            // Need to find an existing index that matches one of the filter parameters
            Pair <FilterValueSetParam, FilterParamIndexBase> pair;

            using (currentNode.NodeRWLock.AcquireReadLock())
            {
                pair = IndexHelper.FindIndex(remainingParameters, currentNode.Indizes);

                // Found an index matching a filter parameter
                if (pair != null)
                {
                    remainingParameters.Remove(pair.First);
                    var filterForValue = pair.First.FilterForValue;
                    var index          = pair.Second;
                    treePathInfo.Add(new EventTypeIndexBuilderIndexLookupablePair(index, filterForValue));
                    AddToIndex(remainingParameters, filterCallback, index, filterForValue, treePathInfo, lockFactory);
                    return;
                }
            }

            // An index for any of the filter parameters was not found, create one
            using (currentNode.NodeRWLock.AcquireWriteLock())
            {
                pair = IndexHelper.FindIndex(remainingParameters, currentNode.Indizes);

                // Attempt to find an index again this time under a write lock
                if (pair != null)
                {
                    remainingParameters.Remove(pair.First);
                    var filterForValue = pair.First.FilterForValue;
                    var indexX         = pair.Second;
                    treePathInfo.Add(new EventTypeIndexBuilderIndexLookupablePair(indexX, filterForValue));
                    AddToIndex(remainingParameters, filterCallback, indexX, filterForValue, treePathInfo, lockFactory);
                    return;
                }

                // No index found that matches any parameters, create a new one
                // Pick the next parameter for an index
                FilterValueSetParam parameterPickedForIndex = remainingParameters.RemoveFirst();

                var index = IndexFactory.CreateIndex(parameterPickedForIndex.Lookupable, lockFactory, parameterPickedForIndex.FilterOperator);

                currentNode.Indizes.Add(index);
                treePathInfo.Add(new EventTypeIndexBuilderIndexLookupablePair(index, parameterPickedForIndex.FilterForValue));
                AddToIndex(remainingParameters, filterCallback, index, parameterPickedForIndex.FilterForValue, treePathInfo, lockFactory);
            }
        }
Ejemplo n.º 24
0
        /// <summary>
        /// Add to an index the value to filter for.
        /// </summary>
        /// <param name="remainingParameters">The remaining parameters.</param>
        /// <param name="filterCallback">The filter callback.</param>
        /// <param name="index">is the index to add to</param>
        /// <param name="filterForValue">is the filter parameter value to add</param>
        /// <param name="treePathInfo">is the specification to fill on where is was added</param>
        /// <param name="lockFactory">The lock factory.</param>
        private static void AddToIndex(
            ArrayDeque <FilterValueSetParam> remainingParameters,
            FilterHandle filterCallback,
            FilterParamIndexBase index,
            Object filterForValue,
            ArrayDeque <EventTypeIndexBuilderIndexLookupablePair> treePathInfo,
            FilterServiceGranularLockFactory lockFactory)
        {
            if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled))
            {
                Log.Debug(".addToIndex ({0}) Adding to index {1}  expressionValue={2}",
                          Thread.CurrentThread.ManagedThreadId, index, filterForValue);
            }

            EventEvaluator eventEvaluator;

            using (index.ReadWriteLock.AcquireReadLock())
            {
                eventEvaluator = index[filterForValue];

                // The filter parameter value already existed in bean, add and release locks
                if (eventEvaluator != null)
                {
                    var added = AddToEvaluator(remainingParameters, filterCallback, eventEvaluator, treePathInfo, lockFactory);
                    if (added)
                    {
                        return;
                    }
                }
            }

            // new filter parameter value, need a write lock
            using (index.ReadWriteLock.AcquireWriteLock())
            {
                eventEvaluator = index[filterForValue];

                // It may exist now since another thread could have added the entry
                if (eventEvaluator != null)
                {
                    var added = AddToEvaluator(remainingParameters, filterCallback, eventEvaluator, treePathInfo, lockFactory);
                    if (added)
                    {
                        return;
                    }

                    // The found eventEvaluator must be converted to a new FilterHandleSetNode
                    var nextIndexX = (FilterParamIndexBase)eventEvaluator;
                    var newNode    = new FilterHandleSetNode(lockFactory.ObtainNew());
                    newNode.Add(nextIndexX);
                    index.Remove(filterForValue);
                    index[filterForValue] = newNode;
                    AddToNode(remainingParameters, filterCallback, newNode, treePathInfo, lockFactory);

                    return;
                }

                // The index does not currently have this filterCallback value,
                // if there are no remaining parameters, create a node
                if (remainingParameters.IsEmpty())
                {
                    var node = new FilterHandleSetNode(lockFactory.ObtainNew());
                    AddToNode(remainingParameters, filterCallback, node, treePathInfo, lockFactory);
                    index[filterForValue] = node;
                    return;
                }

                // If there are remaining parameters, create a new index for the next parameter
                FilterValueSetParam parameterPickedForIndex = remainingParameters.RemoveFirst();

                var nextIndex = IndexFactory.CreateIndex(parameterPickedForIndex.Lookupable, lockFactory, parameterPickedForIndex.FilterOperator);

                index[filterForValue] = nextIndex;
                treePathInfo.Add(new EventTypeIndexBuilderIndexLookupablePair(nextIndex, parameterPickedForIndex.FilterForValue));
                AddToIndex(remainingParameters, filterCallback, nextIndex, parameterPickedForIndex.FilterForValue, treePathInfo, lockFactory);
            }
        }
Ejemplo n.º 25
0
        private void ProcessDispatches(
            ArrayDeque <NamedWindowConsumerLatch> dispatches,
            ArrayDeque <NamedWindowConsumerLatch> work,
            IDictionary <EPStatementAgentInstanceHandle, Object> dispatchesPerStmt)
        {
            if (dispatches.Count == 1)
            {
                var latch = dispatches.First();
                try
                {
                    latch.Await();
                    var newData = latch.DeltaData.NewData;
                    var oldData = latch.DeltaData.OldData;

                    foreach (var entry in latch.DispatchTo)
                    {
                        var handle = entry.Key;

                        handle.StatementHandle.MetricsHandle.Call(
                            _metricReportingService.PerformanceCollector,
                            () => { ProcessHandle(handle, entry.Value, newData, oldData); });

                        if ((_isPrioritized) && (handle.IsPreemptive))
                        {
                            break;
                        }
                    }
                }
                finally
                {
                    latch.Done();
                }

                return;
            }

            // Multiple different-result dispatches to same or different statements are needed in two situations:
            // a) an event comes in, triggers two insert-into statements inserting into the same named window and the window produces 2 results
            // b) a time batch is grouped in the named window, and a timer fires for both groups at the same time producing more then one result
            // c) two on-merge/update/delete statements fire for the same arriving event each updating the named window

            // Most likely all dispatches go to different statements since most statements are not joins of
            // named windows that produce results at the same time. Therefore sort by statement handle.
            // We need to process in N-element chains to preserve dispatches that are next to each other for the same thread.

            while (!dispatches.IsEmpty())
            {
                // the first latch always gets awaited
                var first = dispatches.RemoveFirst();
                first.Await();
                work.Add(first);

                // determine which further latches are in this chain and
                // add these, skipping await for any latches in the chain

                dispatches.RemoveWhere(
                    (next, continuation) =>
                {
                    NamedWindowConsumerLatch earlier = next.Earlier;
                    if (earlier == null || work.Contains(earlier))
                    {
                        work.Add(next);
                        return(true);
                    }
                    else
                    {
                        continuation.Value = false;
                        return(false);
                    }
                });

                ProcessDispatches(work, dispatchesPerStmt);
            }
        }
Ejemplo n.º 26
0
 /// <summary>
 /// Returns true if the window is empty, or false if not empty.
 /// </summary>
 /// <returns>true if empty</returns>
 public bool IsEmpty()
 {
     return(_window.IsEmpty());
 }
Ejemplo n.º 27
0
        private static void EvaluateEventForStatementInternal(EPServicesContext servicesContext, EventBean theEvent, IList <AgentInstance> agentInstances)
        {
            // context was created - reevaluate for the given event
            var callbacks = new ArrayDeque <FilterHandle>(2);

            servicesContext.FilterService.Evaluate(theEvent, callbacks);   // evaluates for ALL statements
            if (callbacks.IsEmpty())
            {
                return;
            }

            // there is a single callback and a single context, if they match we are done
            if (agentInstances.Count == 1 && callbacks.Count == 1)
            {
                var agentInstance = agentInstances[0];
                if (agentInstance.AgentInstanceContext.StatementId == callbacks.First.StatementId)
                {
                    Process(agentInstance, servicesContext, callbacks, theEvent);
                }
                return;
            }

            // use the right sorted/unsorted Map keyed by AgentInstance to sort
            var isPrioritized = servicesContext.ConfigSnapshot.EngineDefaults.Execution.IsPrioritized;
            IDictionary <AgentInstance, object> stmtCallbacks;

            if (!isPrioritized)
            {
                stmtCallbacks = new Dictionary <AgentInstance, object>();
            }
            else
            {
                stmtCallbacks = new SortedDictionary <AgentInstance, object>(AgentInstanceComparator.INSTANCE);
            }

            // process all callbacks
            foreach (var filterHandle in callbacks)
            {
                // determine if this filter entry applies to any of the affected agent instances
                var           statementId        = filterHandle.StatementId;
                AgentInstance agentInstanceFound = null;
                foreach (var agentInstance in agentInstances)
                {
                    if (agentInstance.AgentInstanceContext.StatementId == statementId)
                    {
                        agentInstanceFound = agentInstance;
                        break;
                    }
                }
                if (agentInstanceFound == null)
                {   // when the callback is for some other stmt
                    continue;
                }

                var handleCallback = (EPStatementHandleCallback)filterHandle;
                var handle         = handleCallback.AgentInstanceHandle;

                // Self-joins require that the internal dispatch happens after all streams are evaluated.
                // Priority or preemptive settings also require special ordering.
                if (handle.CanSelfJoin || isPrioritized)
                {
                    var stmtCallback = stmtCallbacks.Get(agentInstanceFound);
                    if (stmtCallback == null)
                    {
                        stmtCallbacks.Put(agentInstanceFound, handleCallback);
                    }
                    else if (stmtCallback is ICollection <FilterHandle> )
                    {
                        var collection = (ICollection <FilterHandle>)stmtCallback;
                        if (!collection.Contains(handleCallback)) // De-duplicate for Filter OR expression paths
                        {
                            collection.Add(handleCallback);
                        }
                    }
                    else
                    {
                        var deque = new ArrayDeque <FilterHandle>(4);
                        deque.Add((EPStatementHandleCallback)stmtCallback);
                        if (stmtCallback != handleCallback) // De-duplicate for Filter OR expression paths
                        {
                            deque.Add(handleCallback);
                        }
                        stmtCallbacks.Put(agentInstanceFound, deque);
                    }
                    continue;
                }

                // no need to be sorted, process
                Process(agentInstanceFound, servicesContext, Collections.SingletonList <FilterHandle>(handleCallback), theEvent);
            }

            if (stmtCallbacks.IsEmpty())
            {
                return;
            }

            // Process self-join or sorted prioritized callbacks
            foreach (var entry in stmtCallbacks)
            {
                var agentInstance = entry.Key;
                var callbackList  = entry.Value;
                if (callbackList is ICollection <FilterHandle> )
                {
                    Process(agentInstance, servicesContext, (ICollection <FilterHandle>)callbackList, theEvent);
                }
                else
                {
                    Process(agentInstance, servicesContext, Collections.SingletonList <FilterHandle>((FilterHandle)callbackList), theEvent);
                }
                if (agentInstance.AgentInstanceContext.EpStatementAgentInstanceHandle.IsPreemptive)
                {
                    return;
                }
            }
        }
Ejemplo n.º 28
0
        public static ExprDotNodeRealizedChain GetChainEvaluators(
            int?streamOfProviderIfApplicable,
            EPType inputType,
            IList <ExprChainedSpec> chainSpec,
            ExprValidationContext validationContext,
            bool isDuckTyping,
            ExprDotNodeFilterAnalyzerInput inputDesc)
        {
            var            methodEvals      = new List <ExprDotEval>();
            var            currentInputType = inputType;
            EnumMethodEnum?lastLambdaFunc   = null;
            var            lastElement      = chainSpec.IsEmpty() ? null : chainSpec[chainSpec.Count - 1];
            ExprDotNodeFilterAnalyzerDesc filterAnalyzerDesc = null;

            var chainSpecStack = new ArrayDeque <ExprChainedSpec>(chainSpec);

            while (!chainSpecStack.IsEmpty())
            {
                var chainElement = chainSpecStack.RemoveFirst();
                lastLambdaFunc = null;  // reset

                // compile parameters for chain element
                var paramEvals = new ExprEvaluator[chainElement.Parameters.Count];
                var paramTypes = new Type[chainElement.Parameters.Count];
                for (var i = 0; i < chainElement.Parameters.Count; i++)
                {
                    paramEvals[i] = chainElement.Parameters[i].ExprEvaluator;
                    paramTypes[i] = paramEvals[i].ReturnType;
                }

                // check if special 'size' method
                if (currentInputType is ClassMultiValuedEPType)
                {
                    var type = (ClassMultiValuedEPType)currentInputType;
                    if ((chainElement.Name.ToLower() == "size") && paramTypes.Length == 0 && Equals(lastElement, chainElement))
                    {
                        var sizeExpr = new ExprDotEvalArraySize();
                        methodEvals.Add(sizeExpr);
                        currentInputType = sizeExpr.TypeInfo;
                        continue;
                    }
                    if ((chainElement.Name.ToLower() == "get") && paramTypes.Length == 1 && paramTypes[0].GetBoxedType() == typeof(int?))
                    {
                        var componentType = type.Component;
                        var get           = new ExprDotEvalArrayGet(paramEvals[0], componentType);
                        methodEvals.Add(get);
                        currentInputType = get.TypeInfo;
                        continue;
                    }
                }

                // determine if there is a matching method
                var matchingMethod = false;
                var methodTarget   = GetMethodTarget(currentInputType);
                if (methodTarget != null)
                {
                    try
                    {
                        GetValidateMethodDescriptor(methodTarget, chainElement.Name, chainElement.Parameters, validationContext);
                        matchingMethod = true;
                    }
                    catch (ExprValidationException)
                    {
                        // expected
                    }
                }

                // resolve lambda
                if (chainElement.Name.IsEnumerationMethod() && (!matchingMethod || methodTarget.IsArray || methodTarget.IsImplementsInterface(typeof(ICollection <object>))))
                {
                    var enumerationMethod = EnumMethodEnumExtensions.FromName(chainElement.Name);
                    var eval = TypeHelper.Instantiate <ExprDotEvalEnumMethod>(enumerationMethod.GetImplementation());
                    eval.Init(streamOfProviderIfApplicable, enumerationMethod, chainElement.Name, currentInputType, chainElement.Parameters, validationContext);
                    currentInputType = eval.TypeInfo;
                    if (currentInputType == null)
                    {
                        throw new IllegalStateException("Enumeration method '" + chainElement.Name + "' has not returned type information");
                    }
                    methodEvals.Add(eval);
                    lastLambdaFunc = enumerationMethod;
                    continue;
                }

                // resolve datetime
                if (chainElement.Name.IsDateTimeMethod() && (!matchingMethod || methodTarget == typeof(DateTimeOffset?)))
                {
                    var datetimeMethod = DatetimeMethodEnumExtensions.FromName(chainElement.Name);
                    var datetimeImpl   = ExprDotEvalDTFactory.ValidateMake(
                        validationContext.StreamTypeService, chainSpecStack, datetimeMethod, chainElement.Name,
                        currentInputType, chainElement.Parameters, inputDesc,
                        validationContext.EngineImportService.TimeZone,
                        validationContext.EngineImportService.TimeAbacus);
                    currentInputType = datetimeImpl.ReturnType;
                    if (currentInputType == null)
                    {
                        throw new IllegalStateException("Date-time method '" + chainElement.Name + "' has not returned type information");
                    }
                    methodEvals.Add(datetimeImpl.Eval);
                    filterAnalyzerDesc = datetimeImpl.IntervalFilterDesc;
                    continue;
                }

                // try to resolve as property if the last method returned a type
                if (currentInputType is EventEPType)
                {
                    var inputEventType = ((EventEPType)currentInputType).EventType;
                    var type           = inputEventType.GetPropertyType(chainElement.Name);
                    var getter         = inputEventType.GetGetter(chainElement.Name);
                    if (type != null && getter != null)
                    {
                        var noduck = new ExprDotEvalProperty(getter, EPTypeHelper.SingleValue(type.GetBoxedType()));
                        methodEvals.Add(noduck);
                        currentInputType = EPTypeHelper.SingleValue(EPTypeHelper.GetClassSingleValued(noduck.TypeInfo));
                        continue;
                    }
                }

                // Finally try to resolve the method
                if (methodTarget != null)
                {
                    try
                    {
                        // find descriptor again, allow for duck typing
                        var desc       = GetValidateMethodDescriptor(methodTarget, chainElement.Name, chainElement.Parameters, validationContext);
                        var fastMethod = desc.FastMethod;
                        paramEvals = desc.ChildEvals;

                        ExprDotEval eval;
                        if (currentInputType is ClassEPType)
                        {
                            // if followed by an enumeration method, convert array to collection
                            if (fastMethod.ReturnType.IsArray && !chainSpecStack.IsEmpty() && chainSpecStack.First.Name.IsEnumerationMethod())
                            {
                                eval = new ExprDotMethodEvalNoDuckWrapArray(validationContext.StatementName, fastMethod, paramEvals);
                            }
                            else
                            {
                                eval = new ExprDotMethodEvalNoDuck(validationContext.StatementName, fastMethod, paramEvals);
                            }
                        }
                        else
                        {
                            eval = new ExprDotMethodEvalNoDuckUnderlying(validationContext.StatementName, fastMethod, paramEvals);
                        }
                        methodEvals.Add(eval);
                        currentInputType = eval.TypeInfo;
                    }
                    catch (Exception e)
                    {
                        if (!isDuckTyping)
                        {
                            throw new ExprValidationException(e.Message, e);
                        }
                        else
                        {
                            var duck = new ExprDotMethodEvalDuck(validationContext.StatementName, validationContext.EngineImportService, chainElement.Name, paramTypes, paramEvals);
                            methodEvals.Add(duck);
                            currentInputType = duck.TypeInfo;
                        }
                    }
                    continue;
                }

                var message = "Could not find event property, enumeration method or instance method named '" +
                              chainElement.Name + "' in " + currentInputType.ToTypeDescriptive();
                throw new ExprValidationException(message);
            }

            var intermediateEvals = methodEvals.ToArray();

            if (lastLambdaFunc != null)
            {
                ExprDotEval finalEval = null;
                if (currentInputType is EventMultiValuedEPType)
                {
                    var mvType        = (EventMultiValuedEPType)currentInputType;
                    var tableMetadata = validationContext.TableService.GetTableMetadataFromEventType(mvType.Component);
                    if (tableMetadata != null)
                    {
                        finalEval = new ExprDotEvalUnpackCollEventBeanTable(mvType.Component, tableMetadata);
                    }
                    else
                    {
                        finalEval = new ExprDotEvalUnpackCollEventBean(mvType.Component);
                    }
                }
                else if (currentInputType is EventEPType)
                {
                    var epType        = (EventEPType)currentInputType;
                    var tableMetadata = validationContext.TableService.GetTableMetadataFromEventType(epType.EventType);
                    if (tableMetadata != null)
                    {
                        finalEval = new ExprDotEvalUnpackBeanTable(epType.EventType, tableMetadata);
                    }
                    else
                    {
                        finalEval = new ExprDotEvalUnpackBean(epType.EventType);
                    }
                }
                if (finalEval != null)
                {
                    methodEvals.Add(finalEval);
                }
            }

            var unpackingEvals = methodEvals.ToArray();

            return(new ExprDotNodeRealizedChain(intermediateEvals, unpackingEvals, filterAnalyzerDesc));
        }
Ejemplo n.º 29
0
        private void ProcessDispatches(
            ArrayDeque<NamedWindowConsumerLatch> dispatches,
            ArrayDeque<NamedWindowConsumerLatch> work,
            IDictionary<EPStatementAgentInstanceHandle, object> dispatchesPerStmt)
        {
            if (dispatches.Count == 1) {
                var latch = dispatches.First;
                try {
                    latch.Await();
                    var newData = latch.DeltaData.NewData;
                    var oldData = latch.DeltaData.OldData;

                    if (metricReportingService.IsMetricsReportingEnabled) {
                        foreach (var entry in latch.DispatchTo) {
                            var handle = entry.Key;
                            if (handle.StatementHandle.MetricsHandle.IsEnabled) {
                                var performanceMetric = PerformanceMetricsHelper.Call(
                                    () => ProcessHandle(handle, entry.Value, newData, oldData), 1);
                                metricReportingService.AccountTime(
                                    handle.StatementHandle.MetricsHandle,
                                    performanceMetric, performanceMetric.NumInput);
                            }
                            else {
                                ProcessHandle(handle, entry.Value, newData, oldData);
                            }

                            if (isPrioritized && handle.IsPreemptive) {
                                break;
                            }
                        }
                    }
                    else {
                        foreach (var entry in latch.DispatchTo) {
                            var handle = entry.Key;
                            ProcessHandle(handle, entry.Value, newData, oldData);

                            if (isPrioritized && handle.IsPreemptive) {
                                break;
                            }
                        }
                    }
                }
                finally {
                    latch.Done();
                }

                return;
            }

            // Multiple different-result dispatches to same or different statements are needed in two situations:
            // a) an event comes in, triggers two insert-into statements inserting into the same named window and the window produces 2 results
            // b) a time batch is grouped in the named window, and a timer fires for both groups at the same time producing more then one result
            // c) two on-merge/update/delete statements fire for the same arriving event each updating the named window
            // Most likely all dispatches go to different statements since most statements are not joins of
            // named windows that produce results at the same time. Therefore sort by statement handle.
            // We need to process in N-element chains to preserve dispatches that are next to each other for the same thread.
            while (!dispatches.IsEmpty()) {
                // the first latch always gets awaited
                var first = dispatches.RemoveFirst();
                first.Await();
                work.Add(first);

                // determine which further latches are in this chain and add these, skipping await for any latches in the chain

                dispatches.RemoveWhere(
                    (next, continuation) => {
                        var result = next.Earlier == null || work.Contains(next.Earlier);
                        continuation.Value = !result;
                        return result;
                    },
                    work.Add);

#if false
                var enumerator = dispatches.GetEnumerator();
                while (enumerator.MoveNext()) {
                    var next = enumerator.Current;
                    var earlier = next.Earlier;
                    if (earlier == null || work.Contains(earlier)) {
                        work.Add(next);
                        enumerator.Remove();
                    }
                    else {
                        break;
                    }
                }
#endif

                ProcessDispatches(work, dispatchesPerStmt);
            }
        }
Ejemplo n.º 30
0
        public override void Update(EventBean[] newData, EventBean[] oldData)
        {
            if (InstrumentationHelper.ENABLED)
            {
                InstrumentationHelper.Get()
                .QViewProcessIRStream(this, _externallyTimedWindowViewFactory.ViewName, newData, oldData);
            }
            long timestamp = -1;

            // add data points to the window
            // we don't care about removed data from a prior view
            if (newData != null)
            {
                for (int i = 0; i < newData.Length; i++)
                {
                    timestamp = GetLongValue(newData[i]);
                    _timeWindow.Add(timestamp, newData[i]);
                }
            }

            // Remove from the window any events that have an older timestamp then the last event's timestamp
            ArrayDeque <EventBean> expired = null;

            if (timestamp != -1)
            {
                expired =
                    _timeWindow.ExpireEvents(timestamp - _timeDeltaComputation.DeltaMillisecondsSubtract(timestamp) + 1);
            }

            EventBean[] oldDataUpdate = null;
            if ((expired != null) && (!expired.IsEmpty()))
            {
                oldDataUpdate = expired.ToArray();
            }

            if ((oldData != null) && (AgentInstanceViewFactoryContext.IsRemoveStream))
            {
                foreach (EventBean anOldData in oldData)
                {
                    _timeWindow.Remove(anOldData);
                }

                if (oldDataUpdate == null)
                {
                    oldDataUpdate = oldData;
                }
                else
                {
                    oldDataUpdate = CollectionUtil.AddArrayWithSetSemantics(oldData, oldDataUpdate);
                }
            }

            if (_viewUpdatedCollection != null)
            {
                _viewUpdatedCollection.Update(newData, oldDataUpdate);
            }

            // If there are child views, fireStatementStopped update method
            if (HasViews)
            {
                if (InstrumentationHelper.ENABLED)
                {
                    InstrumentationHelper.Get().QViewIndicate(this, _externallyTimedWindowViewFactory.ViewName, newData, oldDataUpdate);
                }
                UpdateChildren(newData, oldDataUpdate);
                if (InstrumentationHelper.ENABLED)
                {
                    InstrumentationHelper.Get().AViewIndicate();
                }
            }

            if (InstrumentationHelper.ENABLED)
            {
                InstrumentationHelper.Get().AViewProcessIRStream();
            }
        }